fnt: Improved error handling, etc.
[deark.git] / modules / ico.c
blobb80a5dd8f360603e1d0decb39d45a3e262f40150
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Windows ICO and CUR formats
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_ico);
10 DE_DECLARE_MODULE(de_module_win1ico);
12 struct page_ctx {
13 i64 img_num;
14 i64 data_size;
15 i64 data_offset;
16 int hotspot_x, hotspot_y; // Valid if lctx::is_cur
17 i64 fg_start, bg_start;
18 i64 pdwidth, mask_pdwidth;
19 int use_mask;
20 int has_alpha_channel;
21 int has_inv_bkgd;
22 struct de_bmpinfo bi;
23 de_bitmap *img;
24 de_bitmap *mask_img;
25 u32 pal[256];
28 typedef struct localctx_struct {
29 int is_cur;
30 int extract_unused_masks;
31 } lctx;
33 static void do_extract_png(deark *c, lctx *d, i64 pos, i64 len)
35 char ext[64];
36 i64 w, h;
38 // Peek at the PNG data, to figure out the dimensions.
39 w = de_getu32be(pos+16);
40 h = de_getu32be(pos+20);
42 de_snprintf(ext, sizeof(ext), "%dx%d.png", (int)w, (int)h);
44 // TODO?: Might be nice to edit the PNG file to add an htSP chunk for the
45 // hotspot, but it seems like more trouble than it's worth.
46 dbuf_create_file_from_slice(c->infile, pos, len, ext, NULL, 0);
49 static u32 get_inv_bkgd_replacement_clr(i64 i, i64 j)
51 if((i+j)%2) {
52 return DE_MAKE_RGBA(255,0,128,128);
54 return DE_MAKE_RGBA(128,0,255,128);
57 static void warn_inv_bkgd(deark *c)
59 de_warn(c, "This image contains inverse background pixels, which are not "
60 "fully supported.");
63 static void decode_fg_image_default(deark *c, lctx *d, struct page_ctx *pg)
65 i64 i, j;
66 de_bitmap *img = pg->img;
68 // Read the main image
69 if(pg->bi.bitcount==24) {
70 de_convert_image_rgb(c->infile, pg->fg_start, pg->bi.rowspan, 3,
71 img, DE_GETRGBFLAG_BGR);
73 else if(pg->bi.bitcount<=8) {
74 de_convert_image_paletted(c->infile, pg->fg_start, pg->bi.bitcount,
75 pg->bi.rowspan, pg->pal, img, 0);
77 else {
78 return;
81 // Apply the mask (already read) to the main image
82 for(j=0; j<img->height; j++) {
83 for(i=0; i<pg->bi.width; i++) {
84 u8 maskclr;
85 de_color fgclr;
87 maskclr = DE_COLOR_K(de_bitmap_getpixel(pg->mask_img, i, j));
88 if(maskclr==0) continue; // normal opaque pixel
90 fgclr = de_bitmap_getpixel(pg->img, i, j);
91 if((fgclr & 0x00ffffffU)==0) {
92 // normal transparent pixel
93 de_bitmap_setpixel_rgba(pg->img, i, j, DE_STOCKCOLOR_TRANSPARENT);
95 else {
96 u32 newclr;
98 pg->has_inv_bkgd = 1;
99 newclr = get_inv_bkgd_replacement_clr(i, j);
100 de_bitmap_setpixel_rgba(pg->img, i, j, newclr);
106 static void decode_fg_image_32(deark *c, lctx *d, struct page_ctx *pg)
108 i64 i, j;
109 i64 pos;
110 de_bitmap *img = pg->img;
112 for(j=0; j<img->height; j++) {
113 pos = pg->fg_start + pg->bi.rowspan*j;
115 for(i=0; i<pg->pdwidth; i++) {
116 u8 cr, cg, cb, ca;
118 cb = de_getbyte_p(&pos);
119 cg = de_getbyte_p(&pos);
120 cr = de_getbyte_p(&pos);
121 ca = de_getbyte_p(&pos);
122 de_bitmap_setpixel_rgba(img, i, j, DE_MAKE_RGBA(cr,cg,cb,ca));
127 static void do_image_data(deark *c, lctx *d, struct page_ctx *pg)
129 de_finfo *fi = NULL;
130 char filename_token[32];
131 i64 pos1 = pg->data_offset;
132 i64 len = pg->data_size;
134 if(pos1+len > c->infile->len) goto done;
136 if(!fmtutil_get_bmpinfo(c, c->infile, &pg->bi, pos1, len, DE_BMPINFO_ICO_FORMAT)) {
137 de_err(c, "Invalid bitmap");
138 goto done;
141 if(pg->bi.file_format == DE_BMPINFO_FMT_PNG) {
142 do_extract_png(c, d, pos1, len);
143 goto done;
146 switch(pg->bi.bitcount) {
147 case 1: case 2: case 4: case 8: case 24: case 32:
148 break;
149 case 16:
150 de_err(c, "(image #%d) Unsupported bit count (%d)", (int)pg->img_num, (int)pg->bi.bitcount);
151 goto done;
152 default:
153 de_err(c, "(image #%d) Invalid bit count (%d)", (int)pg->img_num, (int)pg->bi.bitcount);
154 goto done;
157 if(pg->bi.compression_field!=0) {
158 // TODO: Support BITFIELDS
159 de_err(c, "Compression / BITFIELDS not supported");
160 goto done;
163 if(pg->bi.bitcount==32) {
164 // 32bpp images have both an alpha channel, and a 1bpp "mask".
165 // We never use a 32bpp image's mask (although we may extract it
166 // separately).
167 // I'm not sure that's necessarily the best thing to do. I think that
168 // in theory the mask could be used to get inverted-background-color
169 // pixels, though I don't know if Windows allows that.
170 pg->use_mask = 0;
171 pg->has_alpha_channel = 1;
173 else {
174 pg->use_mask = 1;
177 de_snprintf(filename_token, sizeof(filename_token), "%dx%dx%d",
178 (int)pg->bi.width, (int)pg->bi.height, (int)pg->bi.bitcount);
180 pg->pdwidth = (pg->bi.rowspan * 8)/pg->bi.bitcount;
181 pg->mask_pdwidth = pg->bi.mask_rowspan * 8;
183 pg->img = de_bitmap_create2(c, pg->bi.width, pg->pdwidth, pg->bi.height, 4);
185 // Read palette
186 if (pg->bi.pal_entries > 0) {
187 if(pg->bi.pal_entries>256) goto done;
189 de_read_palette_rgb(c->infile,
190 pos1+pg->bi.infohdrsize, pg->bi.pal_entries, pg->bi.bytes_per_pal_entry,
191 pg->pal, 256, DE_GETRGBFLAG_BGR);
194 pg->fg_start = pos1 + pg->bi.size_of_headers_and_pal;
195 pg->bg_start = pos1 + pg->bi.size_of_headers_and_pal + pg->bi.foreground_size;
197 de_dbg(c, "foreground at %d, mask at %d", (int)pg->fg_start, (int)pg->bg_start);
199 // Foreground padding pixels exist if the width times the bitcount is not a
200 // multiple of 32. This is rare.
201 // Mask padding pixels exist if the width is not a multiple of 32. This is
202 // common (when width=16 or 48).
204 // Note: For the -padpix feature, we never combine the mask image's padding
205 // pixels with the foreground image's padding pixels.
206 // Issues:
207 // (1) There may be more mask padding pixels than image padding pixels.
208 // (2) Inverse background pixels, and the warning about them.
209 // The mask's padding will be normally be ignored, but the padded mask will
210 // be written to a separate file if d->extract_unused_masks is enabled.
212 pg->mask_img = de_bitmap_create2(c, pg->bi.width, pg->mask_pdwidth, pg->bi.height, 1);
213 de_convert_image_bilevel(c->infile, pg->bg_start, pg->bi.mask_rowspan, pg->mask_img, 0);
215 if(pg->bi.bitcount==32) {
216 decode_fg_image_32(c, d, pg);
218 else {
219 decode_fg_image_default(c, d, pg);
222 if(pg->has_inv_bkgd) {
223 warn_inv_bkgd(c);
226 de_bitmap_optimize_alpha(pg->img, (pg->bi.bitcount==32)?0x1:0x0);
228 fi = de_finfo_create(c);
230 de_finfo_set_name_from_sz(c, fi, filename_token, 0, DE_ENCODING_ASCII);
232 if(d->is_cur) {
233 fi->has_hotspot = 1;
234 fi->hotspot_x = pg->hotspot_x;
235 fi->hotspot_y = pg->hotspot_y;
238 de_bitmap_write_to_file_finfo(pg->img, fi, DE_CREATEFLAG_FLIP_IMAGE);
240 if(d->extract_unused_masks && (!pg->use_mask || (c->padpix && pg->mask_pdwidth>pg->bi.width))) {
241 char maskname_token[32];
243 de_snprintf(maskname_token, sizeof(maskname_token), "%dx%dx%dmask",
244 (int)pg->bi.width, (int)pg->bi.height, (int)pg->bi.bitcount);
245 de_bitmap_write_to_file(pg->mask_img, maskname_token, DE_CREATEFLAG_IS_AUX | DE_CREATEFLAG_FLIP_IMAGE);
248 done:
249 if(pg) {
250 de_bitmap_destroy(pg->img);
251 pg->img = NULL;
252 de_bitmap_destroy(pg->mask_img);
253 pg->mask_img = NULL;
255 de_finfo_destroy(c, fi);
258 static void do_image_dir_entry(deark *c, lctx *d, i64 img_num, i64 pos)
260 struct page_ctx *pg = NULL;
262 pg = de_malloc(c, sizeof(struct page_ctx));
263 pg->img_num = img_num;
265 de_dbg(c, "image #%d, index at %d", (int)pg->img_num, (int)pos);
266 de_dbg_indent(c, 1);
267 if(d->is_cur) {
268 pg->hotspot_x = (int)de_getu16le(pos+4);
269 pg->hotspot_y = (int)de_getu16le(pos+6);
270 de_dbg(c, "hotspot: %d,%d", pg->hotspot_x, pg->hotspot_y);
272 pg->data_size = de_getu32le(pos+8);
273 pg->data_offset = de_getu32le(pos+12);
274 de_dbg(c, "offset=%"I64_FMT", size=%"I64_FMT, pg->data_offset, pg->data_size);
276 do_image_data(c, d, pg);
278 de_free(c, pg);
279 de_dbg_indent(c, -1);
282 static void de_run_ico(deark *c, de_module_params *mparams)
284 lctx *d = NULL;
285 i64 x;
286 i64 num_images;
287 i64 i;
289 d = de_malloc(c, sizeof(lctx));
290 d->extract_unused_masks = (c->extract_level>=2);
292 x = de_getu16le(2);
293 if(x==1) {
294 d->is_cur=0;
295 de_declare_fmt(c, "Windows Icon");
297 else if(x==2) {
298 d->is_cur=1;
299 de_declare_fmt(c, "Windows Cursor");
301 else {
302 de_dbg(c, "Not an ICO/CUR file");
303 goto done;
306 num_images = de_getu16le(4);
307 de_dbg(c, "images in file: %d", (int)num_images);
308 if(!de_good_image_count(c, num_images)) {
309 goto done;
312 for(i=0; i<num_images; i++) {
313 do_image_dir_entry(c, d, i, 6+16*i);
316 done:
317 de_free(c, d);
320 // Windows icons and cursors don't have a distinctive signature. This
321 // function tries to screen out other formats.
322 static int is_windows_ico_or_cur(deark *c)
324 i64 numicons;
325 i64 i;
326 i64 size, offset;
327 u8 buf[4];
329 de_read(buf, 0, 4);
330 if(de_memcmp(buf, "\x00\x00\x01\x00", 4) &&
331 de_memcmp(buf, "\x00\x00\x02\x00", 4))
333 return 0;
336 numicons = de_getu16le(4);
338 // Each icon must use at least 16 bytes for the directory, 40 for the
339 // info header, 4 for the foreground, and 4 for the mask.
340 if(numicons<1 || (6+numicons*64)>c->infile->len) return 0;
342 // Examine the first few icon index entries.
343 for(i=0; i<numicons && i<8; i++) {
344 size = de_getu32le(6+16*i+8);
345 offset = de_getu32le(6+16*i+12);
346 if(size<48) return 0;
347 if(offset < 6+numicons*16) return 0;
348 if(offset+size > c->infile->len) return 0;
350 return 1;
353 static int de_identify_ico(deark *c)
355 if(is_windows_ico_or_cur(c)) {
356 return 80;
358 return 0;
361 void de_module_ico(deark *c, struct deark_module_info *mi)
363 mi->id = "ico";
364 mi->desc = "Windows icon/cursor";
365 mi->run_fn = de_run_ico;
366 mi->identify_fn = de_identify_ico;
369 ////////////////////////////////////////////////////////////////
371 typedef struct win1ctx_struct {
372 unsigned int type_code;
373 int is_cur;
374 const char *type_name;
375 i64 bytes_consumed;
376 } win1ctx;
378 static int decode_win1_icon(deark *c, win1ctx *d, i64 pos1)
380 de_bitmap *mask = NULL;
381 de_bitmap *img = NULL;
382 de_finfo *fi = NULL;
383 i64 npwidth, h;
384 i64 pdwidth;
385 i64 rowspan;
386 i64 i, j;
387 i64 pos = pos1;
388 int has_inv_bkgd = 0;
389 int hotspot_x = 0;
390 int hotspot_y = 0;
391 int retval = 0;
392 int saved_indent_level;
394 de_dbg_indent_save(c, &saved_indent_level);
395 if(pos1+12 > c->infile->len) goto done;
397 de_dbg(c, "%s at %"I64_FMT, d->type_name, pos);
398 de_dbg_indent(c, 1);
400 if(d->is_cur) {
401 hotspot_x = (int)de_getu16le(pos);
402 hotspot_y = (int)de_getu16le(pos+2);
403 de_dbg(c, "hotspot: %d,%d", hotspot_x, hotspot_y);
405 pos += 4;
407 npwidth = de_getu16le_p(&pos);
408 h = de_getu16le_p(&pos);
409 de_dbg_dimensions(c, npwidth, h);
410 if(!de_good_image_dimensions(c, npwidth, h)) goto done;
412 rowspan = de_getu16le_p(&pos);
413 de_dbg(c, "bytes/row: %d", (int)rowspan);
414 pdwidth = rowspan*8;
416 if(d->is_cur) {
417 unsigned int csColor;
418 csColor = (unsigned int)de_getu16le(pos);
419 de_dbg(c, "csColor: 0x%04x", csColor);
421 pos += 2;
423 mask = de_bitmap_create2(c, npwidth, pdwidth, h, 1);
424 img = de_bitmap_create2(c, npwidth, pdwidth, h, 4);
425 de_dbg(c, "mask at %"I64_FMT, pos);
426 de_convert_image_bilevel(c->infile, pos, rowspan, mask, 0);
427 pos += rowspan*h;
428 de_dbg(c, "foreground at %"I64_FMT, pos);
429 de_convert_image_bilevel(c->infile, pos, rowspan, img, 0);
430 pos += rowspan*h;
432 // This whole loop does nothing, except handle inverse-background-color
433 // pixels. But we have to do something, because such pixels are not
434 // uncommon.
435 for(j=0; j<h; j++) {
436 for(i=0; i<pdwidth; i++) {
437 u8 fgclr, maskclr;
438 u32 newclr;
440 maskclr = DE_COLOR_K(de_bitmap_getpixel(mask, i, j));
441 if(maskclr==0) continue;
442 fgclr = DE_COLOR_K(de_bitmap_getpixel(img, i, j));
443 if(fgclr==0) continue;
445 newclr = get_inv_bkgd_replacement_clr(i, j);
446 de_bitmap_setpixel_gray(mask, i, j, 255-DE_COLOR_A(newclr));
447 de_bitmap_setpixel_rgb(img, i, j, DE_MAKE_OPAQUE(newclr));
448 if(i<npwidth) {
449 has_inv_bkgd = 1;
453 if(has_inv_bkgd) {
454 warn_inv_bkgd(c);
457 de_bitmap_apply_mask(img, mask, DE_BITMAPFLAG_WHITEISTRNS);
459 fi = de_finfo_create(c);
461 if(d->is_cur) {
462 fi->has_hotspot = 1;
463 fi->hotspot_x = hotspot_x;
464 fi->hotspot_y = hotspot_y;
467 de_bitmap_write_to_file_finfo(img, fi, DE_CREATEFLAG_OPT_IMAGE);
468 d->bytes_consumed = pos - pos1;
469 retval = 1;
471 done:
472 de_bitmap_destroy(img);
473 de_bitmap_destroy(mask);
474 de_finfo_destroy(c, fi);
475 de_dbg_indent_restore(c, saved_indent_level);
476 return retval;
479 static void de_run_win1ico(deark *c, de_module_params *mparams)
481 win1ctx *d = NULL;
482 i64 pos = 0;
484 d = de_malloc(c, sizeof(win1ctx));
485 d->type_code = (unsigned int)de_getu16le_p(&pos);
486 de_dbg(c, "type code: 0x%04x", d->type_code);
487 if(d->type_code==0x0003 || d->type_code==0x0103 || d->type_code==0x0203) {
488 d->is_cur = 1;
489 d->type_name = "cursor";
491 else if(d->type_code==0x0001 || d->type_code==0x0101 || d->type_code==0x0201) {
492 d->type_name = "icon";
494 else {
495 de_err(c, "Not a Windows 1.0 icon/cursor");
496 goto done;
498 de_declare_fmtf(c, "Windows 1.0 %s", d->type_name);
500 if(!decode_win1_icon(c, d, pos)) goto done;
501 pos += d->bytes_consumed;
502 if((d->type_code & 0xff00)==0x0200) {
503 // In this case there are supposed to be two icons (this is untested).
504 if(!decode_win1_icon(c, d, pos)) goto done;
507 done:
508 de_free(c, d);
511 static int de_identify_win1ico(deark *c)
513 u8 tclo, tchi;
514 i64 w, h, wb;
515 int has_ext;
517 tclo = de_getbyte(0);
518 tchi = de_getbyte(1);
519 if((tclo==1 || tclo==3) && (tchi<=2)) {
522 else {
523 return 0;
526 w = de_getu16le(6);
527 h = de_getu16le(8);
528 wb = de_getu16le(10);
529 if(w<16 || h<16 || w>256 || h>256) return 0;
530 if(wb != ((w+15)/16)*2) return 0;
531 has_ext = de_input_file_has_ext(c, (tclo==3)?"cur":"ico");
532 if((w==32 || w==64) && h==w && has_ext) return 100;
533 return has_ext ? 70 : 6;
536 void de_module_win1ico(deark *c, struct deark_module_info *mi)
538 mi->id = "win1ico";
539 mi->desc = "Windows 1.0 icon/cursor";
540 mi->run_fn = de_run_win1ico;
541 mi->identify_fn = de_identify_win1ico;