bmp: Rewrote the RLE decompressor
[deark.git] / modules / icns.c
blobfe7fb4996eb27f90abe306defcea0bade7362b46
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // icns - Apple Icon Image format
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_icns);
11 static const de_color g_stdpal16[16] = {
12 0xffffffffU,0xfffcf305U,0xffff6402U,0xffdd0806U,0xfff20884U,0xff4600a5U,0xff0000d4U,0xff02abeaU,
13 0xff1fb714U,0xff006411U,0xff562c05U,0xff90713aU,0xffc0c0c0U,0xff808080U,0xff404040U,0xff000000U
16 #define IMGTYPE_EMBEDDED_FILE 1 // JP2 or PNG
17 #define IMGTYPE_MASK 2
18 #define IMGTYPE_IMAGE 3
19 #define IMGTYPE_IMAGE_AND_MASK 4
20 #define IMGTYPE_ARGB_ETC 5 // ARGB or JP2 or PNG
22 enum content_enum {
23 CONTENT_UNSET=0,
24 CONTENT_UNKNOWN,
25 CONTENT_PNG,
26 CONTENT_JP2,
27 CONTENT_J2C,
28 CONTENT_ARGB
31 struct image_type_info {
32 u32 code;
33 int width;
34 int height;
35 int bpp; // bits per pixel. 0 = unspecified
36 int image_type; // IMGTYPE_*
38 static const struct image_type_info image_type_info_arr[] = {
39 { 0x69636d23, 16, 12, 1, IMGTYPE_IMAGE_AND_MASK }, // icm#
40 { 0x69637323, 16, 16, 1, IMGTYPE_IMAGE_AND_MASK }, // ics#
41 { 0x49434e23, 32, 32, 1, IMGTYPE_IMAGE_AND_MASK }, // ICN#
42 { 0x69636823, 48, 48, 1, IMGTYPE_IMAGE_AND_MASK }, // ich#
44 { 0x49434f4e, 32, 32, 1, IMGTYPE_IMAGE }, // ICON
45 { 0x69636d34, 16, 12, 4, IMGTYPE_IMAGE }, // icm4
46 { 0x69637334, 16, 16, 4, IMGTYPE_IMAGE }, // ics4
47 { 0x69636c34, 32, 32, 4, IMGTYPE_IMAGE }, // icl4
48 { 0x69636834, 48, 48, 4, IMGTYPE_IMAGE }, // ich4
49 { 0x69636d38, 16, 12, 8, IMGTYPE_IMAGE }, // icm8
50 { 0x69637338, 16, 16, 8, IMGTYPE_IMAGE }, // ics8
51 { 0x69636c38, 32, 32, 8, IMGTYPE_IMAGE }, // icl8
52 { 0x69636838, 48, 48, 8, IMGTYPE_IMAGE }, // ich8
53 { 0x69733332, 16, 16, 24, IMGTYPE_IMAGE }, // is32
54 { 0x696c3332, 32, 32, 24, IMGTYPE_IMAGE }, // il32
55 { 0x69683332, 48, 48, 24, IMGTYPE_IMAGE }, // ih32
56 { 0x69743332, 128, 128, 24, IMGTYPE_IMAGE }, // it32
58 { 0x73386d6b, 16, 16, 8, IMGTYPE_MASK }, // s8mk
59 { 0x6c386d6b, 32, 32, 8, IMGTYPE_MASK }, // l8mk
60 { 0x68386d6b, 48, 48, 8, IMGTYPE_MASK }, // h8mk
61 { 0x74386d6b, 128, 128, 8, IMGTYPE_MASK }, // t8mk
63 { 0x69637034, 16, 16, 0, IMGTYPE_EMBEDDED_FILE }, // icp4
64 { 0x69637035, 32, 32, 0, IMGTYPE_EMBEDDED_FILE }, // icp5
65 { 0x69637036, 64, 64, 0, IMGTYPE_EMBEDDED_FILE }, // icp6
66 { 0x69633037, 128, 128, 0, IMGTYPE_EMBEDDED_FILE }, // ic07
67 { 0x69633038, 256, 256, 0, IMGTYPE_EMBEDDED_FILE }, // ic08
68 { 0x69633039, 512, 512, 0, IMGTYPE_EMBEDDED_FILE }, // ic09
69 { 0x69633130, 1024, 1024, 0, IMGTYPE_EMBEDDED_FILE }, // ic10
70 { 0x69633131, 32, 32, 0, IMGTYPE_EMBEDDED_FILE }, // ic11
71 { 0x69633132, 64, 64, 0, IMGTYPE_EMBEDDED_FILE }, // ic12
72 { 0x69633133, 256, 256, 0, IMGTYPE_EMBEDDED_FILE }, // ic13
73 { 0x69633134, 512, 512, 0, IMGTYPE_EMBEDDED_FILE }, // ic14
74 { 0x73623234, 24, 24, 0, IMGTYPE_EMBEDDED_FILE }, // sb24
75 { 0x69637342, 36, 36, 0, IMGTYPE_EMBEDDED_FILE }, // icsB
76 { 0x53423234, 48, 48, 0, IMGTYPE_EMBEDDED_FILE }, // SB24
78 { 0x69633034, 16, 16, 0, IMGTYPE_ARGB_ETC }, // ic04
79 { 0x69633035, 32, 32, 0, IMGTYPE_ARGB_ETC }, // ic05
80 { 0x69637362, 18, 18, 0, IMGTYPE_ARGB_ETC }, // icsb
82 { 0x544f4320, 0, 0, 0, 0 }, // 'TOC '
83 { 0x69636e56, 0, 0, 0, 0 } // icnV
86 struct mask_wrapper {
87 i64 segment_pos;
88 u8 used_flag;
89 u8 nbits;
90 UI width, height;
91 de_bitmap *img;
94 struct page_ctx {
95 int image_num;
96 i64 segment_pos;
97 i64 image_pos;
98 i64 image_len;
99 struct mask_wrapper *mask_ref; // (pointer to a d->mask field; do not free)
100 i64 rowspan;
101 const struct image_type_info *type_info;
102 struct de_fourcc code4cc;
103 enum content_enum content_type; // Used when code4cc is insufficient
104 char filename_token[32];
105 de_color pal[256];
108 #define MASKTYPEID_16_12_1 0
109 #define MASKTYPEID_16_16_1 1
110 #define MASKTYPEID_32_32_1 2
111 #define MASKTYPEID_48_48_1 3
112 #define MASKTYPEID_16_16_8 4
113 #define MASKTYPEID_32_32_8 5
114 #define MASKTYPEID_48_48_8 6
115 #define MASKTYPEID_128_128_8 7
116 #define NUM_MASKTYPES 8
118 typedef struct localctx_struct {
119 i64 file_size;
120 u8 opt_mask1;
121 u8 opt_mask8;
122 u8 opt_mask24;
123 u8 opt_getmasks;
124 struct mask_wrapper mask[NUM_MASKTYPES];
125 u8 have_stdpal256;
126 de_color stdpal256[256];
127 } lctx;
129 static const de_color supplpal256[41] = {
130 0xffee0000U,0xffdd0000U,0xffbb0000U,0xffaa0000U,0xff880000U,
131 0xff770000U,0xff550000U,0xff440000U,0xff220000U,0xff110000U,
132 0xff00ee00U,0xff00dd00U,0xff00bb00U,0xff00aa00U,0xff008800U,
133 0xff007700U,0xff005500U,0xff004400U,0xff002200U,0xff001100U,
134 0xff0000eeU,0xff0000ddU,0xff0000bbU,0xff0000aaU,0xff000088U,
135 0xff000077U,0xff000055U,0xff000044U,0xff000022U,0xff000011U,
136 0xffeeeeeeU,0xffddddddU,0xffbbbbbbU,0xffaaaaaaU,0xff888888U,
137 0xff777777U,0xff555555U,0xff444444U,0xff222222U,0xff111111U,0xff000000U
140 static de_color getpal256(int k)
142 u8 r, g, b;
144 if(k<0 || k>255) return 0;
145 if(k<=214) {
146 // The first 215 palette entries follow a simple pattern.
147 r = (u8)((5-k/36)*0x33);
148 g = (5-(k%36)/6)*0x33;
149 b = (5-k%6)*0x33;
150 return DE_MAKE_RGB(r,g,b);
153 return supplpal256[k-215];
156 static void populate_stdpal256(lctx *d)
158 int k;
160 if(d->have_stdpal256) return;
161 d->have_stdpal256 = 1;
163 for(k=0; k<256; k++) {
164 d->stdpal256[k] = getpal256(k);
168 static void do_decode_1_4_8bit(deark *c, lctx *d, struct page_ctx *pg)
170 de_bitmap *img = NULL;
171 int bypp;
173 bypp = (pg->type_info->bpp==1)?2:4;
174 img = de_bitmap_create(c, pg->type_info->width, pg->type_info->height, bypp);
176 if(pg->type_info->bpp==8) {
177 populate_stdpal256(d);
178 de_memcpy(pg->pal, d->stdpal256, sizeof(d->stdpal256));
180 else if(pg->type_info->bpp==4) {
181 de_memcpy(pg->pal, g_stdpal16, sizeof(g_stdpal16));
183 else { // 1
184 pg->pal[0] = DE_STOCKCOLOR_WHITE;
185 pg->pal[1] = DE_STOCKCOLOR_BLACK;
188 de_convert_image_paletted(c->infile, pg->image_pos, pg->type_info->bpp, pg->rowspan,
189 pg->pal, img, 0);
191 if(pg->mask_ref && pg->mask_ref->img) {
192 de_bitmap_apply_mask(img, pg->mask_ref->img, 0);
195 de_bitmap_write_to_file(img, pg->filename_token, DE_CREATEFLAG_OPT_IMAGE);
196 de_bitmap_destroy(img);
199 static void do_uncompress_24(deark *c, lctx *d, struct page_ctx *pg, dbuf *unc_pixels,
200 i64 skip)
202 i64 pos;
203 u8 b;
204 i64 count;
205 u8 n;
207 pos = pg->image_pos;
208 if(skip) pos+=4;
210 while(1) {
211 if(pos >= pg->image_pos + pg->image_len) break;
213 b = de_getbyte(pos);
214 pos++;
215 if(b>=128) {
216 // Compressed run
217 count = (i64)b - 125;
218 n = de_getbyte(pos);
219 pos++;
220 dbuf_write_run(unc_pixels, n, count);
222 else {
223 // An uncompressed run
224 count = 1 + (i64)b;
225 dbuf_copy(c->infile, pos, count, unc_pixels);
226 pos += count;
231 static void read_image_plane(deark *c, lctx *d,
232 dbuf *unc_pixels, i64 plane, de_bitmap *img, i64 samplenum)
234 i64 i, j;
235 i64 w, h;
237 w = img->width;
238 h = img->height;
240 for(j=0; j<h; j++) {
241 for(i=0; i<w; i++) {
242 u8 v;
244 v = dbuf_getbyte(unc_pixels, (plane*h+j)*w + i);
245 de_bitmap_setsample(img, i, j, samplenum, v);
250 static void do_decode_24bit(deark *c, lctx *d, struct page_ctx *pg)
252 dbuf *unc_pixels = NULL;
253 de_bitmap *img = NULL;
254 i64 w, h;
255 i64 skip;
257 w = pg->type_info->width;
258 h = pg->type_info->height;
260 // TODO: Try to support uncompressed 24-bit images, assuming they exist.
262 // Apparently, some 'it32' icons begin with four extra 0x00 bytes.
263 // Skip over the first four bytes if they are 0x00.
264 // (I don't know the reason for these bytes, but this is the same
265 // logic libicns uses.)
266 skip = 0;
267 if(pg->code4cc.id==0x69743332) { // 'it32' (128x128)
268 if(!dbuf_memcmp(c->infile, pg->image_pos, "\0\0\0\0", 4)) {
269 skip = 4;
273 unc_pixels = dbuf_create_membuf(c, w*h*3, 1);
274 do_uncompress_24(c, d, pg, unc_pixels, skip);
276 img = de_bitmap_create(c, w, h, 4);
278 de_bitmap_rect(img, 0, 0, w, h, DE_STOCKCOLOR_BLACK, 0);
279 read_image_plane(c, d, unc_pixels, 0, img, 0);
280 read_image_plane(c, d, unc_pixels, 1, img, 1);
281 read_image_plane(c, d, unc_pixels, 2, img, 2);
283 if(pg->mask_ref && pg->mask_ref->img) {
284 de_bitmap_apply_mask(img, pg->mask_ref->img, 0);
287 de_bitmap_write_to_file(img, pg->filename_token, DE_CREATEFLAG_OPT_IMAGE);
288 de_bitmap_destroy(img);
289 if(unc_pixels) dbuf_close(unc_pixels);
292 // Sets pg->content_type
293 static void identify_content(deark *c, lctx *d, struct page_ctx *pg)
295 u8 buf[8];
297 if(pg->content_type!=CONTENT_UNSET) return;
299 // Read the first few bytes
300 de_read(buf, pg->image_pos, sizeof(buf));
302 if(buf[4]=='j' && buf[5]=='P') {
303 pg->content_type = CONTENT_JP2;
305 else if(buf[0]==0xff && buf[1]==0x4f && buf[2]==0xff && buf[3]==0x51) {
306 pg->content_type = CONTENT_J2C;
308 else if(buf[0]==0x89 && buf[1]==0x50) {
309 pg->content_type = CONTENT_PNG;
311 else if(buf[0]=='A' && buf[1]=='R' && buf[2]=='G' && buf[3]=='B') {
312 pg->content_type = CONTENT_ARGB;
314 else {
315 pg->content_type = CONTENT_UNKNOWN;
319 // Call this only after the PNG or JP2 format has been identified.
320 static void do_extract_png_or_jp2(deark *c, lctx *d, struct page_ctx *pg)
322 de_finfo *fi = NULL;
323 const char *ext;
325 if(pg->content_type==CONTENT_JP2) {
326 ext = "jp2";
328 else if(pg->content_type==CONTENT_J2C) {
329 ext = "j2c";
331 else if(pg->content_type==CONTENT_PNG) {
332 ext = "png";
334 else {
335 goto done;
338 fi = de_finfo_create(c);
340 de_snprintf(pg->filename_token, sizeof(pg->filename_token), "%dx%d",
341 (int)pg->type_info->width, (int)pg->type_info->height);
342 de_finfo_set_name_from_sz(c, fi, pg->filename_token, 0, DE_ENCODING_ASCII);
344 dbuf_create_file_from_slice(c->infile, pg->image_pos, pg->image_len, ext, fi, 0);
346 done:
347 de_finfo_destroy(c, fi);
350 static void do_argb(deark *c, lctx *d, struct page_ctx *pg)
352 dbuf *unc_pixels = NULL;
353 de_bitmap *img = NULL;
354 i64 w, h;
356 w = pg->type_info->width;
357 h = pg->type_info->height;
359 unc_pixels = dbuf_create_membuf(c, w*h*4, 1);
360 do_uncompress_24(c, d, pg, unc_pixels, 4);
362 img = de_bitmap_create(c, w, h, 4);
364 read_image_plane(c, d, unc_pixels, 0, img, 3);
365 read_image_plane(c, d, unc_pixels, 1, img, 0);
366 read_image_plane(c, d, unc_pixels, 2, img, 1);
367 read_image_plane(c, d, unc_pixels, 3, img, 2);
369 de_snprintf(pg->filename_token, sizeof(pg->filename_token), "%dx%dx32",
370 (int)w, (int)h);
372 de_bitmap_write_to_file(img, pg->filename_token, DE_CREATEFLAG_OPT_IMAGE);
373 de_bitmap_destroy(img);
374 if(unc_pixels) dbuf_close(unc_pixels);
377 static void do_argb_png_or_jp2(deark *c, lctx *d, struct page_ctx *pg)
379 identify_content(c, d, pg);
381 if(pg->type_info->image_type==IMGTYPE_ARGB_ETC && pg->content_type==CONTENT_ARGB) {
382 do_argb(c, d, pg);
383 return;
386 de_dbg(c, "Trying to extract file at %"I64_FMT, pg->image_pos);
388 if(pg->content_type==CONTENT_JP2 || pg->content_type==CONTENT_J2C ||
389 pg->content_type==CONTENT_PNG)
391 do_extract_png_or_jp2(c, d, pg);
392 return;
395 de_err(c, "(Image #%d) Unidentified file format", pg->image_num);
398 // Assumes image_type is IMAGE or IMAGE_AND_MASK.
399 static struct mask_wrapper *find_mask(deark *c, lctx *d, struct page_ctx *pg)
401 struct mask_wrapper *mw = NULL;
402 struct mask_wrapper *mw1 = NULL;
403 struct mask_wrapper *mw8 = NULL;
404 const struct image_type_info *t;
405 int found_mask = 0;
406 u8 opt;
408 t = pg->type_info;
410 // TODO: What is the correct way to match masks to images?
412 if(t->code==0x49434f4e) { // 'ICON'
413 // I'm assuming this format doesn't have a mask.
414 return NULL;
417 if(t->width==16 && t->height==12 && t->bpp<=8) {
418 mw1 = &d->mask[MASKTYPEID_16_12_1];
419 goto afterdiscovery;
421 if(t->width==16 && t->height==16) {
422 mw1 = &d->mask[MASKTYPEID_16_16_1];
423 mw8 = &d->mask[MASKTYPEID_16_16_8];
424 goto afterdiscovery;
426 if(t->width==32) {
427 mw1 = &d->mask[MASKTYPEID_32_32_1];
428 mw8 = &d->mask[MASKTYPEID_32_32_8];
429 goto afterdiscovery;
431 if(t->width==48) {
432 mw1 = &d->mask[MASKTYPEID_48_48_1];
433 mw8 = &d->mask[MASKTYPEID_48_48_8];
434 goto afterdiscovery;
436 if(t->width==128) {
437 mw8 = &d->mask[MASKTYPEID_128_128_8];
438 goto afterdiscovery;
441 afterdiscovery:
442 if(t->bpp==1) {
443 opt = d->opt_mask1;
445 else if(t->bpp<=8) {
446 opt = d->opt_mask8;
448 else {
449 opt = d->opt_mask24;
452 if(opt==1) {
453 mw = mw1;
455 else if(opt==8) {
456 mw = mw8;
458 else if(opt==18) {
459 if(mw1 && mw1->img) mw = mw1;
460 else mw = mw8;
462 else if(opt==81) {
463 if(mw8 && mw8->img) mw = mw8;
464 else mw = mw1;
467 found_mask = (mw && mw->img);
468 if(!found_mask) goto notfound;
470 if(t->image_type==IMGTYPE_IMAGE_AND_MASK && mw==mw1) {
471 // Sanity check. This could fail if there are multiple icons of
472 // the same type.
473 if(pg->segment_pos != mw->segment_pos) {
474 goto notfound;
478 mw->used_flag = 1;
479 de_dbg(c, "[using mask at %"I64_FMT"]", mw->segment_pos);
480 return mw;
482 notfound:
483 de_dbg(c, "[no mask found for icon at %"I64_FMT"]", pg->segment_pos);
484 return NULL;
487 static void convert_image_gray8(dbuf *f, i64 fpos, i64 rowspan, de_bitmap *img)
489 i64 i, j;
491 for(j=0; j<img->height; j++) {
492 for(i=0; i<img->width; i++) {
493 u8 n;
495 n = dbuf_getbyte(f, fpos+j*rowspan+i);
496 de_bitmap_setpixel_gray(img, i, j, n);
501 static void do_read_mask(deark *c, lctx *d, struct page_ctx *pg, int masktype_id,
502 int depth, i64 w, i64 h)
504 de_bitmap *img;
505 i64 rowspan;
506 i64 mask_offset;
507 int saved_indent_level;
509 de_dbg_indent_save(c, &saved_indent_level);
511 if(depth==1) {
512 rowspan = (w+7)/8;
513 mask_offset = rowspan*h;
515 else {
516 rowspan = w;
517 mask_offset = 0;
520 de_dbg(c, "mask(%d"DE_CHAR_TIMES"%d,%d) segment at %"I64_FMT", mask at %"I64_FMT"+%"I64_FMT,
521 (int)w, (int)h, depth, pg->segment_pos, pg->image_pos, mask_offset);
522 de_dbg_indent(c, 1);
524 if(d->mask[masktype_id].img) {
525 de_dbg(c, "duplicate mask type %u", (UI)masktype_id);
526 de_bitmap_destroy(d->mask[masktype_id].img);
528 d->mask[masktype_id].img = de_bitmap_create(c, w, h, 1);
529 d->mask[masktype_id].segment_pos = pg->segment_pos;
530 img = d->mask[masktype_id].img;
532 if(depth==1) {
533 de_convert_image_bilevel(c->infile, pg->image_pos+mask_offset, rowspan, img, 0);
535 else {
536 convert_image_gray8(c->infile, pg->image_pos+mask_offset, rowspan, img);
539 de_dbg_indent_restore(c, saved_indent_level);
542 static void do_icon(deark *c, lctx *d, struct page_ctx *pg)
544 i64 expected_image_size;
545 int is_compressed;
547 if(!pg->type_info) return; // Shouldn't happen.
549 de_strlcpy(pg->filename_token, "", sizeof(pg->filename_token));
551 if(pg->type_info->image_type==IMGTYPE_MASK) {
552 de_dbg(c, "[transparency mask]");
553 return;
556 if(pg->type_info->image_type==IMGTYPE_EMBEDDED_FILE ||
557 pg->type_info->image_type==IMGTYPE_ARGB_ETC)
559 do_argb_png_or_jp2(c, d, pg);
560 return;
563 if(pg->type_info->image_type!=IMGTYPE_IMAGE &&
564 pg->type_info->image_type!=IMGTYPE_IMAGE_AND_MASK)
566 return;
569 // At this point we know it's a regular image (or an image+mask)
571 // Note - This pg->rowspan is arguably incorrect for 24-bit images, since
572 // rows aren't stored contiguously.
573 pg->rowspan = ((pg->type_info->bpp * pg->type_info->width)+7)/8;
575 expected_image_size = pg->rowspan * pg->type_info->height;
576 if(pg->type_info->image_type==IMGTYPE_IMAGE_AND_MASK) {
577 expected_image_size *= 2;
580 is_compressed = (pg->type_info->bpp==24) ? 1 : 0;
582 if(!is_compressed) {
583 if(pg->image_len < expected_image_size) {
584 de_err(c, "(Image #%d) Premature end of image (expected %d bytes, found %d)",
585 pg->image_num, (int)expected_image_size, (int)pg->image_len);
586 return;
588 if(pg->image_len > expected_image_size) {
589 de_warn(c, "(Image #%d) Extra image data found (expected %d bytes, found %d)",
590 pg->image_num, (int)expected_image_size, (int)pg->image_len);
594 pg->mask_ref = find_mask(c, d, pg);
596 de_snprintf(pg->filename_token, sizeof(pg->filename_token), "%dx%dx%d",
597 (int)pg->type_info->width, (int)pg->type_info->height, (int)pg->type_info->bpp);
599 de_dbg(c, "image dimensions: %d"DE_CHAR_TIMES"%d, bpp: %d",
600 pg->type_info->width, pg->type_info->height, pg->type_info->bpp);
602 if(pg->type_info->bpp==1 || pg->type_info->bpp==4 || pg->type_info->bpp==8) {
603 do_decode_1_4_8bit(c, d, pg);
604 return;
606 else if(pg->type_info->bpp==24) {
607 do_decode_24bit(c, d, pg);
608 return;
611 de_warn(c, "(Image #%d) Image type '%s' is not supported", pg->image_num, pg->code4cc.id_sanitized_sz);
614 static void de_run_icns_pass(deark *c, lctx *d, int pass)
616 i64 segment_pos;
617 struct page_ctx *pg = NULL;
618 int image_count;
619 int saved_indent_level;
621 de_dbg_indent_save(c, &saved_indent_level);
622 segment_pos = 8;
623 image_count = 0;
625 while(1) {
626 i64 segment_len;
628 if(pg) { de_free(c, pg); pg=NULL; }
630 if(segment_pos+8 > d->file_size) break;
632 pg = de_malloc(c, sizeof(struct page_ctx));
633 pg->segment_pos = segment_pos;
634 pg->image_num = image_count;
636 dbuf_read_fourcc(c->infile, segment_pos, &pg->code4cc, 4, 0x0);
638 segment_len = de_getu32be(segment_pos+4);
640 pg->image_pos = segment_pos + 8;
641 pg->image_len = segment_len - 8;
643 if(pass==2) {
644 de_dbg(c, "image #%d, type '%s', segment at %"I64_FMT", image at %"I64_FMT", size=%"I64_FMT,
645 pg->image_num, pg->code4cc.id_dbgstr,
646 pg->segment_pos, pg->image_pos, pg->image_len);
648 de_dbg_indent(c, 1);
650 if(segment_len<8 || segment_pos+segment_len > d->file_size) {
651 if(pass==2) {
652 de_err(c, "Invalid length for segment '%s' (%u)", pg->code4cc.id_sanitized_sz,
653 (unsigned int)segment_len);
655 break;
658 if(segment_len==8) {
659 if(pass==2) {
660 de_dbg(c, "[empty icon]");
662 goto next_icon;
665 if(pass==2) {
666 size_t i;
668 // Find this type code in the image_type_info array
669 pg->type_info = NULL;
670 for(i=0; i<DE_ARRAYCOUNT(image_type_info_arr); i++) {
671 if(image_type_info_arr[i].code==pg->code4cc.id) {
672 pg->type_info = &image_type_info_arr[i];
673 break;
676 if(!pg->type_info) {
677 de_warn(c, "(Image #%d) Unknown image type '%s'", pg->image_num, pg->code4cc.id_sanitized_sz);
681 if(pass==1) {
682 de_dbg_indent(c, -1);
683 switch(pg->code4cc.id) {
684 case 0x69636d23: // icm# 16x12x1
685 do_read_mask(c, d, pg, MASKTYPEID_16_12_1, 1, 16, 12);
686 break;
687 case 0x69637323: // ics# 16x16x1
688 do_read_mask(c, d, pg, MASKTYPEID_16_16_1, 1, 16, 16);
689 break;
690 case 0x49434e23: // ICN# 32x32x1
691 do_read_mask(c, d, pg, MASKTYPEID_32_32_1, 1, 32, 32);
692 break;
693 case 0x69636823: // ich# 48x48x1
694 do_read_mask(c, d, pg, MASKTYPEID_48_48_1, 1, 48, 48);
695 break;
696 case 0x73386d6b: // s8mk 16x16x8
697 do_read_mask(c, d, pg, MASKTYPEID_16_16_8, 8, 16, 16);
698 break;
699 case 0x6c386d6b: // l8mk 32x32x8
700 do_read_mask(c, d, pg, MASKTYPEID_32_32_8, 8, 32, 32);
701 break;
702 case 0x68386d6b: // h8mk 48x48x8
703 do_read_mask(c, d, pg, MASKTYPEID_48_48_8, 8, 48, 48);
704 break;
705 case 0x74386d6b: // t8mk 128x128x8
706 do_read_mask(c, d, pg, MASKTYPEID_128_128_8, 8, 128, 128);
707 break;
709 de_dbg_indent(c, 1);
711 else if(pass==2) {
712 do_icon(c, d, pg);
715 next_icon:
716 image_count++;
717 segment_pos += segment_len;
718 de_dbg_indent(c, -1);
721 if(pg) de_free(c, pg);
722 de_dbg_indent_restore(c, saved_indent_level);
725 static void extract_masks(deark *c, lctx *d)
727 size_t i;
728 char tokenbuf[32];
730 de_dbg(c, "extracting masks");
731 for(i=0; i<NUM_MASKTYPES; i++) {
732 if(d->mask[i].img) {
733 de_snprintf(tokenbuf, sizeof(tokenbuf), "mask%ux%ux%u",
734 d->mask[i].width, d->mask[i].height, (UI)d->mask[i].nbits);
735 de_bitmap_write_to_file(d->mask[i].img, tokenbuf, DE_CREATEFLAG_OPT_IMAGE);
740 // FIXME? The way we handle mask type attributes is awkward.
741 // This function was added just for the "getmasks" feature.
742 static void init_masktype_info(lctx *d)
744 size_t i;
745 struct mti_struct {
746 u8 masktype;
747 u8 nbits;
748 UI width, height;
750 static const struct mti_struct mti[NUM_MASKTYPES] = {
751 { MASKTYPEID_16_12_1, 1, 16, 12 },
752 { MASKTYPEID_16_16_1, 1, 16, 16 },
753 { MASKTYPEID_32_32_1, 1, 32, 32 },
754 { MASKTYPEID_48_48_1, 1, 48, 48 },
755 { MASKTYPEID_16_16_8, 8, 16, 16 },
756 { MASKTYPEID_32_32_8, 8, 32, 32 },
757 { MASKTYPEID_48_48_8, 8, 48, 48 },
758 { MASKTYPEID_128_128_8, 8, 128, 128 }
761 for(i=0; i<DE_ARRAYCOUNT(mti); i++) {
762 d->mask[mti[i].masktype].nbits = mti[i].nbits;
763 d->mask[mti[i].masktype].width = mti[i].width;
764 d->mask[mti[i].masktype].height = mti[i].height;
768 static void de_run_icns(deark *c, de_module_params *mparams)
770 lctx *d = NULL;
771 const char *s;
773 d = de_malloc(c, sizeof(lctx));
774 init_masktype_info(d);
776 // (If these options are undocumented, it's because they're still in
777 // development/testing.)
778 // 81 = Use 8-bit mask if present, otherwise 1-bit mask
779 // 18 = Use 1-bit mask if present, otherwise 8-bit mask
780 // 8 = Use 8-bit mask only
781 // 1 = Use 1-bit mask only
782 // 0 = No transparency
784 // TODO: Maybe set opt_mask1 = 81
785 d->opt_mask1 = 1; // Setting for (most) 1bpp icons
786 d->opt_mask8 = 81; // Setting for 4 and 8bpp icons
787 d->opt_mask24 = 81; // Setting for 24bpp icons
788 s = de_get_ext_option(c, "icns:mask1");
789 if(s) {
790 d->opt_mask1 = (u8)de_atoi(s);
792 s = de_get_ext_option(c, "icns:mask8");
793 if(s) {
794 d->opt_mask8 = (u8)de_atoi(s);
796 s = de_get_ext_option(c, "icns:mask24");
797 if(s) {
798 d->opt_mask24 = (u8)de_atoi(s);
801 d->opt_getmasks = de_get_ext_option_bool(c, "icns:getmasks", 0);
803 d->file_size = de_getu32be(4);
804 de_dbg(c, "reported file size: %d", (int)d->file_size);
805 if(d->file_size > c->infile->len) d->file_size = c->infile->len;
807 de_dbg(c, "pass 1: reading masks");
808 de_dbg_indent(c, 1);
809 de_run_icns_pass(c, d, 1);
810 de_dbg_indent(c, -1);
812 if(d->opt_getmasks) {
813 extract_masks(c, d);
814 goto done;
817 de_dbg(c, "pass 2: decoding/extracting icons");
818 de_dbg_indent(c, 1);
819 de_run_icns_pass(c, d, 2);
820 de_dbg_indent(c, -1);
822 done:
823 if(d) {
824 int i;
826 for(i=0; i<NUM_MASKTYPES; i++) {
827 if(d->mask[i].img) {
828 if(!d->mask[i].used_flag && !d->opt_getmasks) {
829 de_dbg(c, "[mask at %"I64_FMT" was not used]", d->mask[i].segment_pos);
831 de_bitmap_destroy(d->mask[i].img);
832 d->mask[i].img = NULL;
835 de_free(c, d);
839 static int de_identify_icns(deark *c)
841 i64 fsize;
843 if(dbuf_memcmp(c->infile, 0, "icns", 4)) return 0;
845 fsize = de_getu32be(4);
846 if(fsize == c->infile->len) return 100;
847 return 20;
850 static void de_help_icns(deark *c)
852 de_msg(c, "-opt icns:getmasks : Only extract the transparency masks");
855 void de_module_icns(deark *c, struct deark_module_info *mi)
857 mi->id = "icns";
858 mi->desc = "Macintosh icon";
859 mi->run_fn = de_run_icns;
860 mi->identify_fn = de_identify_icns;
861 mi->help_fn = de_help_icns;