Refactoring the iff decoder
[deark.git] / modules / nie.c
blobfccd9ec864d6a39c0ca0717b8e039a33346bbab9
1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
5 // NIE - One of the "Naive Image Formats" used with the Wuffs project.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_nie);
11 typedef struct localctx_struct {
12 #define SIG_NIA 0x6ec3af41U
13 #define SIG_NIE 0x6ec3af45U
14 #define SIG_NII 0x6ec3af49U
15 unsigned int sig;
16 const char *fmtname;
17 int is_bgra;
18 int premultiplied_alpha;
19 i64 bytes_per_pixel;
20 i64 width, height;
21 int bad_image_flag;
22 int found_last_frame_flag;
23 } lctx;
25 static u8 unpremultiply_alpha(u8 cval, u8 a)
27 if(a==0xff) {
28 return cval;
30 if(a==0 || cval==0) {
31 return 0;
33 if(cval>=a) {
34 return 0xff;
36 return (u8)(0.5 + (double)cval / ((double)a/255.0));
39 // 8-byte "NII payload"
40 static void read_NIIpayload(deark *c, lctx *d, i64 pos, int printCCD)
42 u64 ccd_code;
43 i64 ccd;
45 ccd_code = dbuf_getu64le(c->infile, pos);
46 if(ccd_code & 0x1) {
47 d->found_last_frame_flag = 1;
49 if(printCCD) {
50 ccd = (i64)(ccd_code>>2);
51 de_dbg(c, "CCD: %"I64_FMT" (%f seconds)", ccd,
52 (double)ccd/705600000.0);
56 // Decode the header, after the 4-byte signature.
57 // That's 12 bytes for NIE, 20 bytes for NII/NIA.
58 // On parse error, prints an error and returns 0.
59 // If the image has invalid attributes, sets d->bad_image_flag (only NIE cares
60 // about this).
61 static int do_header(deark *c, lctx *d, i64 pos)
63 u8 b;
65 b = de_getbyte_p(&pos);
66 if(b!=0xff) {
67 de_err(c, "Unknown %s version", d->fmtname);
68 return 0;
71 if(d->sig==SIG_NII) {
72 pos += 3; // padding
74 else {
75 b = de_getbyte_p(&pos);
76 if(b=='b') {
77 d->is_bgra = 1;
79 else if(b!='r') {
80 d->bad_image_flag = 1;
82 de_dbg(c, "sample order: %sA", (d->is_bgra?"BGR":"RGB"));
84 b = de_getbyte_p(&pos);
85 if(b=='p') {
86 d->premultiplied_alpha = 1;
88 else if(b!='n') {
89 d->bad_image_flag = 1;
91 de_dbg(c, "premultiplied alpha: %d", d->premultiplied_alpha);
93 b = de_getbyte_p(&pos);
94 if(b=='8') {
95 d->bytes_per_pixel = 8;
97 else if(b=='4') {
98 d->bytes_per_pixel = 4;
100 else {
101 d->bad_image_flag = 1;
103 de_dbg(c, "bytes/pixel: %d", (int)d->bytes_per_pixel);
106 d->width = de_getu32le_p(&pos);
107 d->height = de_getu32le_p(&pos);
108 de_dbg_dimensions(c, d->width, d->height);
110 if(d->sig==SIG_NIE) return 1;
112 read_NIIpayload(c, d, pos, 0);
113 return 1;
116 static void do_decode_nie(deark *c, lctx *d)
118 i64 i, j;
119 i64 k;
120 i64 pos = 0;
121 i64 sample_offset[4]; // R, G, B, A
122 de_bitmap *img = NULL;
124 pos += 4; // signature, already read
126 if(!do_header(c, d, pos)) goto done;
127 if(d->bad_image_flag) {
128 de_err(c, "Bad or unsupported NIE format");
129 goto done;
131 pos += 12;
133 if(!de_good_image_dimensions(c, d->width, d->height)) {
134 goto done;
137 // Make a sample-to-byte-offset map.
138 for(k=0; k<4; k++) {
139 sample_offset[k] = k; // Defaults
141 if(d->is_bgra) {
142 sample_offset[0] = 2; // R sample is byte 2
143 sample_offset[2] = 0; // B sample is byte 0
145 if(d->bytes_per_pixel==8) { // If 16 bits/sample, look only at the high byte
146 for(k=0; k<4; k++) {
147 sample_offset[k] = sample_offset[k]*2 + 1;
151 img = de_bitmap_create(c, d->width, d->height, 4);
153 for(j=0; j<d->height; j++) {
154 for(i=0; i<d->width; i++) {
155 u8 pbuf[8];
156 u8 s[4];
158 de_read(pbuf, pos, d->bytes_per_pixel);
159 pos += d->bytes_per_pixel;
161 for(k=0; k<4; k++) {
162 s[k] = pbuf[sample_offset[k]];
165 if(d->premultiplied_alpha && (s[3]!=0xff)) {
166 for(k=0; k<3; k++) {
167 s[k] = unpremultiply_alpha(s[k], s[3]);
171 de_bitmap_setpixel_rgba(img, i, j, DE_MAKE_RGBA(s[0],s[1],s[2],s[3]));
175 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_OPT_IMAGE);
177 done:
178 de_bitmap_destroy(img);
181 static void do_decode_nii(deark *c, lctx *d)
183 i64 pos = 4;
185 if(!do_header(c, d, pos)) goto done;
186 pos += 20;
188 while(1) {
189 if(d->found_last_frame_flag) break;
190 de_dbg(c, "frame info at %"I64_FMT, pos);
191 de_dbg_indent(c, 1);
192 read_NIIpayload(c, d, pos, 1);
193 pos += 8;
194 de_dbg_indent(c, -1);
196 // TODO?: footer
197 done:
201 static void do_decode_nia(deark *c, lctx *d)
203 i64 pos = 4;
204 i64 frame_size, frame_size_padded;
206 if(!do_header(c, d, pos)) goto done;
207 pos += 20;
209 frame_size = 16 + d->width*d->height*d->bytes_per_pixel;
210 de_dbg(c, "frame size: %"I64_FMT" bytes", frame_size);
211 if(frame_size<16 || frame_size>DE_MAX_SANE_OBJECT_SIZE) {
212 goto done;
214 frame_size_padded = de_pad_to_n(frame_size, 8);
216 while(1) {
217 if(d->found_last_frame_flag) break;
218 if(pos+frame_size_padded > c->infile->len) goto done;
219 de_dbg(c, "frame at %"I64_FMT, pos);
220 de_dbg_indent(c, 1);
221 dbuf_create_file_from_slice(c->infile, pos, frame_size, "nie", NULL, 0x0);
222 pos += frame_size_padded;
223 read_NIIpayload(c, d, pos, 1);
224 pos += 8;
225 de_dbg_indent(c, -1);
227 // TODO?: footer
228 done:
232 static void de_run_nie(deark *c, de_module_params *mparams)
234 lctx *d = NULL;
236 d = de_malloc(c, sizeof(lctx));
237 d->sig = (unsigned int)de_getu32be(0);
239 if(d->sig==SIG_NIE) {
240 d->fmtname = "NIE";
242 else if(d->sig==SIG_NII) {
243 d->fmtname = "NII";
245 else if(d->sig==SIG_NIA) {
246 d->fmtname = "NIA";
248 else {
249 de_err(c, "File not in NIE/NII/NIA format");
250 goto done;
253 de_declare_fmt(c, d->fmtname);
255 if(d->sig==SIG_NIE) {
256 do_decode_nie(c, d);
258 else if(d->sig==SIG_NII) {
259 do_decode_nii(c, d);
261 else if(d->sig==SIG_NIA) {
262 do_decode_nia(c, d);
265 done:
266 de_free(c, d);
269 static int de_identify_nie(deark *c)
271 unsigned int sig;
273 sig = (unsigned int)de_getu32be(0);
274 if(sig==SIG_NIE || sig==SIG_NII || sig==SIG_NIA) return 100;
275 return 0;
278 void de_module_nie(deark *c, struct deark_module_info *mi)
280 mi->id = "nie";
281 mi->desc = "NIE/NII/NIA (Naive Image Formats)";
282 mi->run_fn = de_run_nie;
283 mi->identify_fn = de_identify_nie;