code wiki / (root) / nx_galx_upscale.nx

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}