bmp: Rewrote the RLE decompressor
[deark.git] / modules / cdiimage.c
blob107610882126c179aba981faff3a08d4e1695a5d
1 // This file is part of Deark.
2 // Copyright (C) 2022 Jason Summers
3 // See the file COPYING for terms of use.
5 // IFF CDI IMAG
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_cdi_imag);
12 #define CODE_FORM 0x464f524dU
13 #define CODE_IDAT 0x49444154U
14 #define CODE_IHDR 0x49484452U
15 #define CODE_PLTE 0x504c5445U
17 struct cdi_imag_ctx {
18 i64 npwidth, pdwidth, h;
19 i64 rowspan;
20 UI model;
21 UI depth;
22 u8 found_IHDR;
23 u8 dyuv_kind;
24 u8 dyuv_start[3];
25 de_color pal[256];
28 static const char *get_cdi_imag_model_name(UI n)
30 static const char *names[10] = { "RGB888", "RGB555", "DYUV", "CLUT8",
31 "CLUT7", "CLUT4", "CLUT3", "RL7", "RL3", "PLTE" };
33 if(n>=1 && n<=10) return names[n-1];
34 return "?";
37 static void do_cdi_imag_IHDR(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx)
39 i64 pos = ictx->chunkctx->dpos;
40 if(ictx->chunkctx->dlen<10) return;
41 d->found_IHDR = 1;
42 d->npwidth = dbuf_getu16be_p(ictx->f, &pos);
43 d->rowspan = dbuf_getu16be_p(ictx->f, &pos);
44 d->h = dbuf_getu16be_p(ictx->f, &pos);
45 de_dbg_dimensions(c, d->npwidth, d->h);
46 de_dbg(c, "bytes/row: %u", (UI)d->rowspan);
47 d->model = (UI)dbuf_getu16be_p(ictx->f, &pos);
48 de_dbg(c, "model: %u (%s)", d->model, get_cdi_imag_model_name(d->model));
49 d->depth = (UI)dbuf_getu16be_p(ictx->f, &pos);
50 de_dbg(c, "bits/pixel: %u", d->depth);
52 if(d->model==3 && ictx->chunkctx->dlen>=14) {
53 UI k;
55 d->dyuv_kind = dbuf_getbyte_p(ictx->f, &pos);
56 // 0 = IFF_DYUV_ONE, 1 = IFF_DYUV_EACH
57 de_dbg(c, "dyuv kind: %u", (UI)d->dyuv_kind);
58 for(k=0; k<3; k++) {
59 d->dyuv_start[k] = dbuf_getbyte_p(ictx->f, &pos);
61 de_dbg(c, "dyuv start: %u,%u,%u", (UI)d->dyuv_start[0],
62 (UI)d->dyuv_start[1], (UI)d->dyuv_start[2]);
65 if(d->depth==4 || d->depth==8) {
66 d->pdwidth = (d->rowspan*8)/d->depth;
70 static void do_cdi_imag_PLTE(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx)
72 i64 offset;
73 i64 count;
75 offset = dbuf_getu16be(ictx->f, ictx->chunkctx->dpos);
76 count = dbuf_getu16be(ictx->f, ictx->chunkctx->dpos+2);
77 de_dbg(c, "entries: %u", (UI)count);
78 if(offset>255 || count<1) return;
79 de_read_palette_rgb(ictx->f, ictx->chunkctx->dpos+4, count, 3,
80 &d->pal[offset], 256-offset, 0);
83 static int cdi_imag_decompress_rl7(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx,
84 dbuf *unc_pixels)
86 i64 pos, endpos;
87 i64 xpos = 0;
88 i64 ypos = 0;
89 int need_errmsg = 0;
91 pos = ictx->chunkctx->dpos;
92 endpos = pos + ictx->chunkctx->dlen;
94 while(1) {
95 u8 x;
96 u8 palent;
97 u8 eoln_flag = 0;
98 i64 count;
100 if(ypos >= d->h) break;
102 if(pos >= endpos) {
103 need_errmsg = 1;
104 goto done;
107 x = dbuf_getbyte_p(ictx->f, &pos);
108 palent = x & 0x7f;
109 if(x & 0x80) { // run
110 u8 r;
112 r = dbuf_getbyte_p(ictx->f, &pos);
113 if(r>=2) {
114 count = (i64)r;
116 else if(r==0) {
117 eoln_flag = 1;
118 count = d->npwidth - xpos;
120 else {
121 de_err(c, "Unsupported compression feature");
122 goto done;
125 else { // single pixel
126 count = 1;
129 if(xpos+count > d->npwidth) {
130 need_errmsg = 1;
131 count = d->npwidth - xpos;
133 dbuf_write_run(unc_pixels, palent, count);
134 xpos += count;
136 if(eoln_flag) {
137 ypos++;
138 xpos = 0;
142 done:
143 dbuf_flush(unc_pixels);
144 if(need_errmsg) {
145 de_err(c, "Image decompression failed");
147 return (unc_pixels->len>0);
150 static u8 dbl_to_u8(double x)
152 if(x<=0.0) return 0;
153 if(x>=255.0) return 255;
154 return (u8)(x+0.5);
157 static void yuv_to_rgb(UI y, UI u, UI v, de_color *rgbclr)
159 double r1, g1, b1;
161 b1 = (double)y + ((double)u - 128.0) * 1.733;
162 r1 = (double)y + ((double)v - 128.0) * 1.371;
163 g1 = ((double)y - 0.299*r1 - 0.114*b1)/0.587;
164 *rgbclr = DE_MAKE_RGB(dbl_to_u8(r1), dbl_to_u8(g1), dbl_to_u8(b1));
167 static int do_cdi_imag_model3(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx,
168 de_bitmap *img)
170 int retval = 0;
171 i64 i, j;
172 i64 pos;
173 UI prev[3];
174 static const u8 dtbl[16] = { 0, 1, 4, 9, 16, 27, 44, 79,
175 128, 177, 212, 229, 240, 247, 252, 255 };
177 for(j=0; j<d->h; j++) {
178 pos = ictx->chunkctx->dpos + j*d->rowspan;
179 prev[0] = (UI)d->dyuv_start[0];
180 prev[1] = (UI)d->dyuv_start[1];
181 prev[2] = (UI)d->dyuv_start[2];
182 for(i=0; i<d->pdwidth; i+=2) {
183 u8 b[2];
184 UI ysamp[2];
185 UI usamp, vsamp;
186 de_color clr;
188 b[0] = dbuf_getbyte_p(ictx->f, &pos);
189 b[1] = dbuf_getbyte_p(ictx->f, &pos);
191 // Each sample is a 4-bit code, which is first translated to an
192 // 8-bit value using dtbl[], then added to the previous sample
193 // value to get the real sample value.
194 ysamp[0] = (prev[0] + (UI)dtbl[(UI)(b[0] & 0x0f)])&0xff;
195 prev[0] = ysamp[0];
196 ysamp[1] = (prev[0] + (UI)dtbl[(UI)(b[1] & 0x0f)])&0xff;
197 prev[0] = ysamp[1];
198 usamp = (prev[1] + (UI)dtbl[((UI)b[0] & 0xf0)>>4])&0xff;
199 prev[1] = usamp;
200 vsamp = (prev[2] + (UI)dtbl[((UI)b[1] & 0xf0)>>4])&0xff;
201 prev[2] = vsamp;
203 // Each U and V sample is used for two pixels. The spec says we
204 // should interpolate to improve the image quality. But we're not
205 // going to bother.
206 yuv_to_rgb(ysamp[0], usamp, vsamp, &clr);
207 de_bitmap_setpixel_rgb(img, i+0, j, clr);
208 yuv_to_rgb(ysamp[1], usamp, vsamp, &clr);
209 de_bitmap_setpixel_rgb(img, i+1, j, clr);
212 retval = 1;
214 if(retval) {
215 de_info(c, "Note: Converting colorspace; image quality may be affected");
217 return retval;
220 static int do_cdi_imag_model8(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx,
221 de_bitmap *img)
223 int retval = 0;
224 dbuf *unc_pixels = NULL;
226 if(d->depth!=8) return 0;
228 unc_pixels = dbuf_create_membuf(c, d->npwidth*d->h, 0x1);
229 dbuf_enable_wbuffer(unc_pixels);
230 if(!cdi_imag_decompress_rl7(c, d, ictx, unc_pixels)) goto done;
231 // Note: There are ways to change the palette partway through the image,
232 // so if we were to fully support this model, we could not use
233 // de_convert_image_paletted().
234 de_convert_image_paletted(unc_pixels, 0, (i64)d->depth,
235 d->npwidth, d->pal, img, 0);
236 retval = 1;
238 done:
239 dbuf_close(unc_pixels);
240 return retval;
243 static void do_cdi_imag_IDAT(deark *c, struct cdi_imag_ctx *d, struct de_iffctx *ictx)
245 de_bitmap *img = NULL;
246 int ret = 0;
248 if(!d->found_IHDR) goto done;
250 if((d->model==3 && d->depth==8) ||
251 (d->model==6 && d->depth==4) ||
252 (d->model==8 && d->depth==8))
256 else {
257 de_err(c, "Unsupported image type: model=%u, depth=%u", d->model, d->depth);
258 goto done;
261 if(!de_good_image_dimensions(c, d->npwidth, d->h)) goto done;
263 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->h, 3);
265 if(d->model==3) {
266 if(d->dyuv_kind!=0) {
267 de_err(c, "Unsupported image type");
269 else {
270 ret = do_cdi_imag_model3(c, d, ictx, img);
273 else if(d->model==6) {
274 de_convert_image_paletted(ictx->f, ictx->chunkctx->dpos, (i64)d->depth,
275 d->rowspan, d->pal, img, 0);
276 ret = 1;
278 else if(d->model==8) {
279 ret = do_cdi_imag_model8(c, d, ictx, img);
282 if(ret) {
283 de_bitmap_write_to_file(img, NULL, 0);
286 done:
287 de_bitmap_destroy(img);
290 static int my_cdi_imag_chunk_handler(struct de_iffctx *ictx)
292 deark *c = ictx->c;
293 struct cdi_imag_ctx *d = (struct cdi_imag_ctx*)ictx->userdata;
295 switch(ictx->chunkctx->chunk4cc.id) {
296 case CODE_FORM:
297 ictx->is_std_container = 1;
298 break;
299 case CODE_IDAT:
300 do_cdi_imag_IDAT(c, d, ictx);
301 ictx->handled = 1;
302 break;
303 case CODE_IHDR:
304 do_cdi_imag_IHDR(c, d, ictx);
305 ictx->handled = 1;
306 break;
307 case CODE_PLTE:
308 do_cdi_imag_PLTE(c, d, ictx);
309 ictx->handled = 1;
310 break;
313 return 1;
316 static void de_run_cdi_imag(deark *c, de_module_params *mparams)
318 struct cdi_imag_ctx *d = NULL;
319 struct de_iffctx *ictx = NULL;
321 d = de_malloc(c, sizeof(struct cdi_imag_ctx));
322 ictx = fmtutil_create_iff_decoder(c);
323 ictx->userdata = (void*)d;
324 ictx->has_standard_iff_chunks = 1;
325 ictx->handle_chunk_fn = my_cdi_imag_chunk_handler;
326 ictx->f = c->infile;
327 fmtutil_read_iff_format(ictx, 0, c->infile->len);
328 fmtutil_destroy_iff_decoder(ictx);
329 de_free(c, d);
332 static int de_identify_cdi_imag(deark *c)
334 if((UI)de_getu32be(0)!=CODE_FORM) return 0;
335 if(dbuf_memcmp(c->infile, 8, (const void*)"IMAGIHDR", 8)) return 0;
336 return 100;
339 void de_module_cdi_imag(deark *c, struct deark_module_info *mi)
341 mi->id = "cdi_imag";
342 mi->desc = "CD-I IFF IMAG";
343 mi->run_fn = de_run_cdi_imag;
344 mi->identify_fn = de_identify_cdi_imag;