1 // This file is part of Deark.
2 // Copyright (C) 2022 Jason Summers
3 // See the file COPYING for terms of use.
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
18 i64 npwidth
, pdwidth
, h
;
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];
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;
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) {
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
);
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
)
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
,
91 pos
= ictx
->chunkctx
->dpos
;
92 endpos
= pos
+ ictx
->chunkctx
->dlen
;
100 if(ypos
>= d
->h
) break;
107 x
= dbuf_getbyte_p(ictx
->f
, &pos
);
109 if(x
& 0x80) { // run
112 r
= dbuf_getbyte_p(ictx
->f
, &pos
);
118 count
= d
->npwidth
- xpos
;
121 de_err(c
, "Unsupported compression feature");
125 else { // single pixel
129 if(xpos
+count
> d
->npwidth
) {
131 count
= d
->npwidth
- xpos
;
133 dbuf_write_run(unc_pixels
, palent
, count
);
143 dbuf_flush(unc_pixels
);
145 de_err(c
, "Image decompression failed");
147 return (unc_pixels
->len
>0);
150 static u8
dbl_to_u8(double x
)
153 if(x
>=255.0) return 255;
157 static void yuv_to_rgb(UI y
, UI u
, UI v
, de_color
*rgbclr
)
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
,
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) {
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;
196 ysamp
[1] = (prev
[0] + (UI
)dtbl
[(UI
)(b
[1] & 0x0f)])&0xff;
198 usamp
= (prev
[1] + (UI
)dtbl
[((UI
)b
[0] & 0xf0)>>4])&0xff;
200 vsamp
= (prev
[2] + (UI
)dtbl
[((UI
)b
[1] & 0xf0)>>4])&0xff;
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
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
);
215 de_info(c
, "Note: Converting colorspace; image quality may be affected");
220 static int do_cdi_imag_model8(deark
*c
, struct cdi_imag_ctx
*d
, struct de_iffctx
*ictx
,
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);
239 dbuf_close(unc_pixels
);
243 static void do_cdi_imag_IDAT(deark
*c
, struct cdi_imag_ctx
*d
, struct de_iffctx
*ictx
)
245 de_bitmap
*img
= NULL
;
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))
257 de_err(c
, "Unsupported image type: model=%u, depth=%u", d
->model
, d
->depth
);
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);
266 if(d
->dyuv_kind
!=0) {
267 de_err(c
, "Unsupported image type");
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);
278 else if(d
->model
==8) {
279 ret
= do_cdi_imag_model8(c
, d
, ictx
, img
);
283 de_bitmap_write_to_file(img
, NULL
, 0);
287 de_bitmap_destroy(img
);
290 static int my_cdi_imag_chunk_handler(struct de_iffctx
*ictx
)
293 struct cdi_imag_ctx
*d
= (struct cdi_imag_ctx
*)ictx
->userdata
;
295 switch(ictx
->chunkctx
->chunk4cc
.id
) {
297 ictx
->is_std_container
= 1;
300 do_cdi_imag_IDAT(c
, d
, ictx
);
304 do_cdi_imag_IHDR(c
, d
, ictx
);
308 do_cdi_imag_PLTE(c
, d
, ictx
);
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
;
327 fmtutil_read_iff_format(ictx
, 0, c
->infile
->len
);
328 fmtutil_destroy_iff_decoder(ictx
);
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;
339 void de_module_cdi_imag(deark
*c
, struct deark_module_info
*mi
)
342 mi
->desc
= "CD-I IFF IMAG";
343 mi
->run_fn
= de_run_cdi_imag
;
344 mi
->identify_fn
= de_identify_cdi_imag
;