nx_galx_upscale.nx source
↩ module page · 117 lines · 5680 B
1// nx_galx_upscale.nx -- SOVEREIGN gallery image UPSCALER. Composes the existing sovereign codecs
2// (nx_png_decoder = RFC2083 decode, nx_png = RGB8 encode) with the existing sovereign resampler
3// (nx_img_scale.nx = bilinear Q16.16, OpenCV INTER_LINEAR convention) -- NO ffmpeg / PIL / libpng,
4// NO re-inlined resize math (reuses nx_img_scale_bilinear per the consolidation discipline).
5// Enlarges a stored gen/image by an integer factor (2x..4x), capped to UP_MAXDIM, and re-encodes a
6// larger PNG. Output is CID-addressed under knowledge/store/galx-upscale/<CID>_<n>x.png so the daemon
7// serves it at GET /upscale/<CID>?s=<n>. (True AI super-resolution is the GPU/diffusion path -- the
8// sd-server migration; this is the sovereign no-GPU 2x/4x stopgap that needs no GPU worker.)
9//
10// MEMORY DISCIPLINE (same as nx_galx_thumb): nx_png_decode mmaps a 64MiB IDAT buffer per call and the
11// toolchain has no sys_munmap, so the gallery FORKS one child per /upscale request -- the kernel reclaims
12// every mmap on child exit, bounding RSS regardless of how many upscales are requested.
13//
14// mode: nx_galx_upscale one <src.png> <out.png> [scale] -- upscale a single image (the gate path)
15// license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
18import "nx_png_decoder.nx"
19import "nx_png.nx"
20import "nx_img_scale.nx"
21const UP_MAGIC_65536: i64 = 65536
22
23const UP_DIR: *u8 = "knowledge/store/galx-upscale" as *u8
24const UP_MAXDIM: i64 = 4096 // hard cap on each output dimension (memory bound, rule 11/21)
25
26func up_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
27// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
28// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
29// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
30// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
31func up_n(v: i64) -> i64 { nxi_out(v); return 0 }
32func up_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ if s[i]>=(48 as u8){ if s[i]<=(57 as u8){ v=v*10+((s[i]-(48 as u8)) as i64) } } i=i+1 } return v }
33func up_mkdir(path: *u8) -> i64 { return __syscall(258, 0-100, path as i64, 0x1ed, 0, 0, 0) }
34
35// Upscale ONE image by integer `scale` (>=2), output dims capped to UP_MAXDIM. Returns 0 ok, negative on
36// any failure (caller treats negatives as a skip, never a crash).
37func up_one(src: *u8, out: *u8, scale: i64) -> i64 {
38 let szp: *i64 = sys_mmap(16) as *i64
39 let buf: *u8 = sys_read_file(src, szp)
40 let n: i64 = szp[0]
41 if (buf as i64) == 0 { return 0 - 1 }
42 if n < 8 { return 0 - 2 }
43 let r: *NxPngResult = nx_png_decode(buf, n)
44 if r.error_code != 0 { return 0 - 3 }
45 let hdr: *NxPngHeader = r.header
46 let w: i64 = hdr.width
47 let h: i64 = hdr.height
48 if w <= 0 { return 0 - 4 }
49 if h <= 0 { return 0 - 4 }
50 let nch: i64 = r.n_channels
51 let bpp: i64 = r.bytes_per_pix
52 if nch <= 0 { return 0 - 5 }
53 let bps: i64 = bpp / nch // bytes per sample: 1 (8-bit) or 2 (16-bit hi-byte-first)
54 let px: *u8 = r.pixels
55 let stride: i64 = w * bpp
56 // Build a clean contiguous RGB8 source (handles gray/RGB/RGBA + 8/16-bit -> high byte), the format
57 // nx_img_scale_bilinear consumes (row-major, channel-interleaved u8).
58 let src3: *u8 = sys_mmap(3 * w * h)
59 var yy: i64 = 0
60 while yy < h {
61 var xx: i64 = 0
62 while xx < w {
63 let off: i64 = yy * stride + xx * bpp
64 let rr: i64 = px[off] as i64
65 var gg: i64 = rr
66 var bb: i64 = rr
67 if nch >= 3 { gg = px[off + bps] as i64; bb = px[off + 2 * bps] as i64 }
68 let d3: i64 = (yy * w + xx) * 3
69 src3[d3] = rr as u8
70 src3[d3 + 1] = gg as u8
71 src3[d3 + 2] = bb as u8
72 xx = xx + 1
73 }
74 yy = yy + 1
75 }
76 // Target dims = scale * source, each clamped to UP_MAXDIM.
77 var dw: i64 = w * scale
78 var dh: i64 = h * scale
79 if dw > UP_MAXDIM { dw = UP_MAXDIM }
80 if dh > UP_MAXDIM { dh = UP_MAXDIM }
81 if dw < 1 { dw = 1 }
82 if dh < 1 { dh = 1 }
83 // Bilinear upscale RGB8 -> RGB8 via the shared sovereign resampler.
84 let dst3: *u8 = sys_mmap(3 * dw * dh)
85 let rc: i64 = nx_img_scale_bilinear(src3, w, h, 3, dst3, dw, dh)
86 if rc != 0 { return 0 - 6 }
87 // Pack into the i64 framebuffer write_png expects (cr + cg*256 + cb*65536).
88 let fb: *i64 = sys_mmap(8 * dw * dh) as *i64
89 var i: i64 = 0
90 let tot: i64 = dw * dh
91 while i < tot {
92 let s3: i64 = i * 3
93 fb[i] = (dst3[s3] as i64) + (dst3[s3 + 1] as i64) * 256 + (dst3[s3 + 2] as i64) * UP_MAGIC_65536
94 i = i + 1
95 }
96 return write_png(fb, dw, dh, out)
97}
98
99func main(argc: i64, argv: *i64) -> i64 {
100 if argc < 4 {
101 up_p("usage: nx_galx_upscale one <src.png> <out.png> [scale=2]\n" as *u8)
102 sys_exit(2); return 2
103 }
104 up_mkdir(UP_DIR)
105 let mode: *u8 = argv[1] as *u8
106 if mode[0] == (111 as u8) { // 'o' -> one
107 var scale: i64 = 2
108 if argc >= 5 { scale = up_atoi(argv[4] as *u8) }
109 if scale < 2 { scale = 2 }
110 if scale > 4 { scale = 4 }
111 let rc: i64 = up_one(argv[2] as *u8, argv[3] as *u8, scale)
112 if rc == 0 { up_p("UPSCALE ok -> " as *u8); up_p(argv[3] as *u8); up_p("\n" as *u8); sys_exit(0); return 0 }
113 up_p("UPSCALE FAIL rc=" as *u8); up_n(rc); up_p("\n" as *u8); sys_exit(1); return 1
114 }
115 up_p("unknown mode\n" as *u8)
116 sys_exit(2); return 2
117}