nx_media_convert.nx source
↩ module page · 141 lines · 5930 B
1// nx_media_convert.nx -- the user-facing media CONVERT: a real image FILE in,
2// decode -> selectable-model resample -> encode -> real image FILE out, one call.
3//
4// Composes the existing sovereign decoder + the selectable resampler + the
5// sovereign PNG encoder -- ZERO 3rd-party. This is the literal "upscaling /
6// downscaling when media is converted, with easily selectable models" ask.
7//
8// RUNG: RGB PNG in/out (nx_png_write_rgb is RGB). JPEG input (nx_jpeg_decode)
9// + RGBA/gray channel adaptation = flagged next rung.
10// license_tier: ORIGINAL
11
12import "nx_syscalls.nx"
13import "nx_img_resample.nx"
14import "nx_png_decoder.nx"
15import "nx_png_write.nx"
16import "nx_seg_store.nx"
17import "nx_jpeg_ascii.nx"
18
19// Convert a PNG file to dw x dh using `model`, writing a PNG. Returns 0 ok,
20// negative on error (-1 read, -2 decode, -3 non-RGB, -4 resize, else encode rc).
21func nx_media_convert_png(in_path: *u8, model: i64, dw: i64, dh: i64, out_path: *u8) -> i64 {
22 let szbox: *i64 = sys_mmap(16) as *i64
23 let png: *u8 = ss_readall(in_path, szbox)
24 if szbox[0] <= 0 { return 0 - 1 }
25 let r: *NxPngResult = nx_png_decode(png, szbox[0])
26 if r.error_code != NX_PNG_OK { return 0 - 2 }
27 if r.n_channels != 3 { return 0 - 3 }
28 let hd: *NxPngHeader = r.header
29 let dst: *u8 = sys_mmap(dw * dh * 3 + 16)
30 if nx_img_resample(model, r.pixels, hd.width, hd.height, 3, dst, dw, dh) != 0 { return 0 - 4 }
31 return nx_png_write_rgb(out_path, dst, dw, dh)
32}
33
34// Adapt decoded pixels (nc: 1=gray, 2=gray+alpha, 3=RGB, 4=RGBA) into
35// interleaved RGB (3ch). Gray replicates to R=G=B; alpha is dropped.
36func nx_img_to_rgb(src: *u8, w: i64, h: i64, nc: i64, dst: *u8) -> i64 {
37 if nc <= 0 { return 0 - 1 }
38 let n: i64 = w * h
39 var i: i64 = 0
40 while i < n {
41 let s: i64 = i * nc
42 let d: i64 = i * 3
43 if nc < 3 {
44 let g: i64 = src[s] as i64
45 dst[d + 0] = g as u8
46 dst[d + 1] = g as u8
47 dst[d + 2] = g as u8
48 } else {
49 dst[d + 0] = src[s + 0]
50 dst[d + 1] = src[s + 1]
51 dst[d + 2] = src[s + 2]
52 }
53 i = i + 1
54 }
55 return 0
56}
57
58// resize decoded interleaved-RGB (w x h) to dw x dh with `model`, write PNG.
59func _mc_finish(rgb: *u8, w: i64, h: i64, model: i64, dw: i64, dh: i64, out_path: *u8) -> i64 {
60 let dst: *u8 = sys_mmap(dw * dh * 3 + 16)
61 if nx_img_resample(model, rgb, w, h, 3, dst, dw, dh) != 0 { return 0 - 4 }
62 return nx_png_write_rgb(out_path, dst, dw, dh)
63}
64
65// Convert a JPEG file to dw x dh using `model`, writing a PNG.
66func nx_media_convert_jpeg(in_path: *u8, model: i64, dw: i64, dh: i64, out_path: *u8) -> i64 {
67 let szbox: *i64 = sys_mmap(16) as *i64
68 let jpg: *u8 = ss_readall(in_path, szbox)
69 if szbox[0] <= 8 { return 0 - 1 }
70 let pp_rgb: *i64 = sys_mmap(8) as *i64
71 let pp_w: *i64 = sys_mmap(8) as *i64
72 let pp_h: *i64 = sys_mmap(8) as *i64
73 if nx_jpeg_decode_rgb(jpg, szbox[0], pp_rgb, pp_w, pp_h) != NX_JPEG_ASCII_OK { return 0 - 5 }
74 return _mc_finish(pp_rgb[0] as *u8, pp_w[0], pp_h[0], model, dw, dh, out_path)
75}
76
77// Unified: sniff PNG vs JPEG by magic bytes, decode, resample (selectable
78// model), encode PNG. Any supported image FILE -> resized PNG. Returns 0 ok,
79// negative on error, -6 = unknown/unsupported format.
80func nx_media_convert_file(in_path: *u8, model: i64, dw: i64, dh: i64, out_path: *u8) -> i64 {
81 let szbox: *i64 = sys_mmap(16) as *i64
82 let buf: *u8 = ss_readall(in_path, szbox)
83 let n: i64 = szbox[0]
84 if n <= 8 { return 0 - 1 }
85 if buf[0] == (137 as u8) { // PNG: 0x89 'P' ...
86 if buf[1] == (80 as u8) {
87 let r: *NxPngResult = nx_png_decode(buf, n)
88 if r.error_code != NX_PNG_OK { return 0 - 2 }
89 if r.n_channels < 1 { return 0 - 3 }
90 let hd: *NxPngHeader = r.header
91 let rgb: *u8 = sys_mmap(hd.width * hd.height * 3 + 16)
92 nx_img_to_rgb(r.pixels, hd.width, hd.height, r.n_channels, rgb)
93 return _mc_finish(rgb, hd.width, hd.height, model, dw, dh, out_path)
94 }
95 }
96 if buf[0] == (255 as u8) { // JPEG: 0xFF 0xD8
97 if buf[1] == (216 as u8) {
98 let pp_rgb: *i64 = sys_mmap(8) as *i64
99 let pp_w: *i64 = sys_mmap(8) as *i64
100 let pp_h: *i64 = sys_mmap(8) as *i64
101 if nx_jpeg_decode_rgb(buf, n, pp_rgb, pp_w, pp_h) != NX_JPEG_ASCII_OK { return 0 - 5 }
102 return _mc_finish(pp_rgb[0] as *u8, pp_w[0], pp_h[0], model, dw, dh, out_path)
103 }
104 }
105 return 0 - 6
106}
107
108// Demo: build a sharp 4-quadrant RGB image, encode to a real PNG, then convert
109// that FILE two ways (NEAREST vs BICUBIC) so model selection is visible.
110func main() -> i64 {
111 let sw: i64 = 24
112 let sh: i64 = 24
113 let src: *u8 = sys_mmap(sw * sh * 3 + 16)
114 var y: i64 = 0
115 while y < sh {
116 var x: i64 = 0
117 while x < sw {
118 let o: i64 = (y * sw + x) * 3
119 var rr: i64 = 0
120 var gg: i64 = 0
121 var bb: i64 = 0
122 if y < 12 {
123 if x < 12 { rr = 255 } else { gg = 255 } // TL red, TR green
124 } else {
125 if x < 12 { bb = 255 } else { rr = 255; gg = 255 } // BL blue, BR yellow
126 }
127 src[o + 0] = rr as u8
128 src[o + 1] = gg as u8
129 src[o + 2] = bb as u8
130 x = x + 1
131 }
132 y = y + 1
133 }
134 let srcpath: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_sv_mc_src.png" as *u8
135 if nx_png_write_rgb(srcpath, src, sw, sh) != 0 { return 1 }
136
137 // convert the FILE 5x two ways
138 if nx_media_convert_png(srcpath, NX_RS_NEAREST, 120, 120, "/mnt/c/Users/elder/nishi-core/nxc2/_sv_mc_near.png" as *u8) != 0 { return 2 }
139 if nx_media_convert_png(srcpath, NX_RS_BICUBIC, 120, 120, "/mnt/c/Users/elder/nishi-core/nxc2/_sv_mc_cubic.png" as *u8) != 0 { return 3 }
140 return 0
141}