stuffit,gif: Added new de_dfilter_addslice function
[deark.git] / modules / gif.c
blobc03b13c8ecb4916c7c8b4d02ca43270888dd1416
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GIF image
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_gif);
12 #define DISPOSE_LEAVE 1
13 #define DISPOSE_BKGD 2
14 #define DISPOSE_PREVIOUS 3
16 struct gceinfo {
17 u8 disposal_method;
18 u8 trns_color_idx_valid;
19 u8 trns_color_idx;
22 typedef struct localctx_struct {
23 int compose;
24 int bad_screen_flag;
25 int dump_screen;
26 int dump_plaintext_ext;
27 u8 unexpected_eof_flag;
29 i64 screen_w, screen_h;
30 int has_global_color_table;
31 i64 global_color_table_size; // Number of colors stored in the file
32 de_color global_ct[256];
34 de_bitmap *screen_img;
35 struct gceinfo *gce; // The Graphic Control Ext. in effect for the next image
36 de_finfo *fi; // Reused for each image
37 } lctx;
39 // Data about a single image
40 struct gif_image_data {
41 de_bitmap *img;
42 i64 xpos, ypos;
43 i64 width, height;
44 i64 pixels_set;
45 int interlaced;
46 int has_local_color_table;
47 int failure_flag;
48 struct de_dfilter_ctx *dfctx;
49 i64 local_color_table_size;
50 u16 *interlace_map;
51 de_color local_ct[256];
54 struct subblock_reader_data {
55 void *userdata;
56 i64 subblock_idx;
57 dbuf *inf;
58 i64 subblkpos;
59 i64 reported_dlen;
60 i64 dpos;
61 i64 dlen;
64 typedef void (*subblock_callback_fn_type)(deark *c, lctx *d, struct subblock_reader_data *sbrd);
66 static void on_unexpected_eof(deark *c, lctx *d)
68 if(!d->unexpected_eof_flag) {
69 de_err(c, "Unexpected end of file");
70 d->unexpected_eof_flag = 1;
74 // Call cbfn once for each subblock.
75 // For the block terminator, will be called with .dlen=0.
76 // On unexpected EOF, supplies any bytes that are present (with .dlen>0 &&
77 // .dlen!=.reported_dlen), calls on_unexpected_eof(), and sets *ppos to point
78 // to EOF.
79 static void do_read_subblocks_p(deark *c, lctx *d, dbuf *inf,
80 subblock_callback_fn_type cbfn, void *userdata, i64 *ppos)
82 struct subblock_reader_data sbrd;
83 int eof_flag = 0;
85 de_zeromem(&sbrd, sizeof(struct subblock_reader_data));
86 sbrd.userdata = userdata;
87 sbrd.inf = inf;
89 while(1) {
90 sbrd.subblkpos = *ppos;
92 if(*ppos >= inf->len) {
93 on_unexpected_eof(c, d);
94 *ppos = inf->len;
95 return;
97 sbrd.reported_dlen = (i64)de_getbyte_p(ppos);
99 sbrd.dpos = *ppos;
101 if(sbrd.dpos + sbrd.reported_dlen > inf->len) {
102 eof_flag = 1;
103 sbrd.dlen = inf->len - sbrd.dpos;
105 else {
106 sbrd.dlen = sbrd.reported_dlen;
109 *ppos += sbrd.dlen;
111 if(sbrd.dlen>0 || !eof_flag) {
112 cbfn(c, d, &sbrd);
115 if(eof_flag) {
116 on_unexpected_eof(c, d);
117 *ppos = inf->len;
118 return;
121 if(sbrd.reported_dlen==0) break;
122 sbrd.subblock_idx++;
126 static void do_record_pixel(deark *c, lctx *d, struct gif_image_data *gi, unsigned int coloridx,
127 i64 offset)
129 i64 pixnum;
130 i64 xi, yi;
131 i64 yi1;
132 de_color clr;
134 if(coloridx>255) return;
136 pixnum = gi->pixels_set + offset;
137 xi = pixnum%gi->width;
138 yi1 = pixnum/gi->width;
139 if(gi->interlace_map && yi1<gi->height) {
140 yi = gi->interlace_map[yi1];
142 else {
143 yi = yi1;
146 if(gi->has_local_color_table && coloridx<gi->local_color_table_size) {
147 clr = gi->local_ct[coloridx];
149 else {
150 clr = d->global_ct[coloridx];
153 if(d->gce && d->gce->trns_color_idx_valid &&
154 (d->gce->trns_color_idx == coloridx))
156 // Make this pixel transparent
157 clr = DE_SET_ALPHA(clr, 0);
159 else {
160 clr = DE_SET_ALPHA(clr, 0xff);
163 de_bitmap_setpixel_rgb(gi->img, xi, yi, clr);
166 static int do_read_header(deark *c, lctx *d, i64 pos)
168 de_ucstring *ver = NULL;
170 de_dbg(c, "header at %d", (int)pos);
171 de_dbg_indent(c, 1);
172 ver = ucstring_create(c);
173 dbuf_read_to_ucstring(c->infile, pos+3, 3, ver, 0, DE_ENCODING_ASCII);
174 de_dbg(c, "version: \"%s\"", ucstring_getpsz(ver));
175 de_dbg_indent(c, -1);
176 ucstring_destroy(ver);
177 return 1;
180 static int do_read_screen_descriptor(deark *c, lctx *d, i64 pos)
182 i64 bgcol_index;
183 u8 packed_fields;
184 u8 aspect_ratio_code;
185 unsigned int n;
186 unsigned int global_color_table_size_code;
188 de_dbg(c, "screen descriptor at %d", (int)pos);
189 de_dbg_indent(c, 1);
191 d->screen_w = de_getu16le(pos);
192 d->screen_h = de_getu16le(pos+2);
193 de_dbg(c, "screen dimensions: %d"DE_CHAR_TIMES"%d", (int)d->screen_w, (int)d->screen_h);
195 packed_fields = de_getbyte(pos+4);
196 de_dbg(c, "packed fields: 0x%02x", (unsigned int)packed_fields);
197 de_dbg_indent(c, 1);
198 d->has_global_color_table = (packed_fields&0x80)?1:0;
199 de_dbg(c, "global color table flag: %d", d->has_global_color_table);
201 n = (packed_fields&0x70)>>4;
202 de_dbg(c, "color resolution: %u (%u bit%s)", n, n+1U, n?"s":"");
204 if(d->has_global_color_table) {
205 unsigned int sf;
206 sf = (packed_fields&0x08)?1:0;
207 de_dbg(c, "global color table sorted: %u", sf);
210 if(d->has_global_color_table) {
211 global_color_table_size_code = (unsigned int)(packed_fields&0x07);
212 d->global_color_table_size = de_pow2((i64)global_color_table_size_code+1);
213 de_dbg(c, "global color table size: %u (%d colors)",
214 global_color_table_size_code, (int)d->global_color_table_size);
217 de_dbg_indent(c, -1);
219 // We don't care about the background color, because we always assume the
220 // background is transparent.
221 // TODO: If we ever support writing background-color chunks to PNG files,
222 // then we should look up this color and use it.
223 bgcol_index = (i64)de_getbyte(pos+5);
224 de_dbg(c, "background color index: %d", (int)bgcol_index);
226 aspect_ratio_code = de_getbyte(pos+6);
227 de_dbg(c, "aspect ratio code: %d", (int)aspect_ratio_code);
228 if(aspect_ratio_code!=0 && aspect_ratio_code!=49) {
229 d->fi->density.code = DE_DENSITY_UNK_UNITS;
230 d->fi->density.xdens = 64.0;
231 d->fi->density.ydens = 15.0 + (double)aspect_ratio_code;
234 de_dbg_indent(c, -1);
235 return 1;
238 static void do_read_color_table(deark *c, lctx *d, i64 pos, i64 ncolors,
239 de_color *ct)
241 de_read_palette_rgb(c->infile, pos, ncolors, 3, ct, 256, 0);
244 static int do_read_global_color_table(deark *c, lctx *d, i64 pos, i64 *bytesused)
246 if(!d->has_global_color_table) return 1;
247 de_dbg(c, "global color table at %d", (int)pos);
249 de_dbg_indent(c, 1);
250 do_read_color_table(c, d, pos, d->global_color_table_size, d->global_ct);
251 de_dbg_indent(c, -1);
253 *bytesused = 3*d->global_color_table_size;
254 return 1;
257 static void callback_for_skip_subblocks(deark *c, lctx *d, struct subblock_reader_data *sbrd)
261 static void do_skip_subblocks(deark *c, lctx *d, i64 pos1, i64 *bytesused)
263 i64 pos = pos1;
265 do_read_subblocks_p(c, d, c->infile, callback_for_skip_subblocks, NULL, &pos);
266 *bytesused = pos - pos1;
269 struct copy_subblocks_ctx {
270 dbuf *outf;
271 int has_max;
272 i64 maxlen;
273 i64 nbytes_copied;
276 static void callback_for_copy_subblocks(deark *c, lctx *d, struct subblock_reader_data *sbrd)
278 struct copy_subblocks_ctx *ctx = (struct copy_subblocks_ctx*)sbrd->userdata;
279 i64 nbytes_to_copy;
281 if(ctx->has_max && (ctx->nbytes_copied >= ctx->maxlen)) return;
282 if(sbrd->dlen<1) return;
284 nbytes_to_copy = sbrd->dlen;
285 if(ctx->has_max) {
286 if(ctx->nbytes_copied + nbytes_to_copy > ctx->maxlen) {
287 nbytes_to_copy = ctx->maxlen - ctx->nbytes_copied;
290 dbuf_copy(sbrd->inf, sbrd->dpos, nbytes_to_copy, ctx->outf);
291 ctx->nbytes_copied += nbytes_to_copy;
294 static void do_copy_subblocks_to_dbuf(deark *c, lctx *d, dbuf *outf,
295 i64 pos1, int has_max, i64 maxlen)
297 i64 pos = pos1;
298 struct copy_subblocks_ctx ctx;
300 ctx.outf = outf;
301 ctx.has_max = has_max;
302 ctx.maxlen = maxlen;
303 ctx.nbytes_copied = 0;
304 do_read_subblocks_p(c, d, c->infile, callback_for_copy_subblocks, (void*)&ctx, &pos);
307 static void discard_current_gce_data(deark *c, lctx *d)
309 if(d->gce) {
310 de_free(c, d->gce);
311 d->gce = NULL;
315 static void do_graphic_control_extension(deark *c, lctx *d, i64 pos)
317 i64 n;
318 u8 packed_fields;
319 u8 user_input_flag;
320 i64 delay_time_raw;
321 double delay_time;
322 const char *name;
324 discard_current_gce_data(c, d);
326 n = (i64)de_getbyte(pos);
327 if(n!=4) {
328 de_warn(c, "Wrong graphic control ext. block size (expected 4, is %d)",
329 (int)n);
330 if(n<4) return;
333 d->gce = de_malloc(c, sizeof(struct gceinfo));
335 packed_fields = de_getbyte(pos+1);
336 de_dbg(c, "packed fields: 0x%02x", (unsigned int)packed_fields);
337 de_dbg_indent(c, 1);
338 d->gce->trns_color_idx_valid = packed_fields&0x01;
339 de_dbg(c, "has transparency: %d", (int)d->gce->trns_color_idx_valid);
341 user_input_flag = (packed_fields>>1)&0x1;
342 de_dbg(c, "user input flag: %d", (int)user_input_flag);
344 d->gce->disposal_method = (packed_fields>>2)&0x7;
345 switch(d->gce->disposal_method) {
346 case 0: name="unspecified"; break;
347 case DISPOSE_LEAVE: name="leave in place"; break;
348 case DISPOSE_BKGD: name="restore to background"; break;
349 case DISPOSE_PREVIOUS: name="restore to previous"; break;
350 default: name="?";
352 de_dbg(c, "disposal method: %d (%s)", (int)d->gce->disposal_method, name);
353 de_dbg_indent(c, -1);
355 delay_time_raw = de_getu16le(pos+2);
356 delay_time = ((double)delay_time_raw)/100.0;
357 de_dbg(c, "delay time: %d (%.02f sec)", (int)delay_time_raw, delay_time);
359 if(d->gce->trns_color_idx_valid) {
360 d->gce->trns_color_idx = de_getbyte(pos+4);
361 de_dbg(c, "transparent color index: %d", (int)d->gce->trns_color_idx);
365 struct comment_ext_ctx {
366 de_ucstring *s;
367 dbuf *outf;
370 static void callback_for_comment_ext(deark *c, lctx *d, struct subblock_reader_data *sbrd)
372 struct comment_ext_ctx *ctx = (struct comment_ext_ctx*)sbrd->userdata;
374 if(sbrd->dlen<1) return;
376 if(ctx->outf) {
377 // GIF comments are supposed to be 7-bit ASCII, so just copy them as-is.
378 dbuf_copy(sbrd->inf, sbrd->dpos, sbrd->dlen, ctx->outf);
381 if(ctx->s->len<DE_DBG_MAX_STRLEN) {
382 dbuf_read_to_ucstring(sbrd->inf, sbrd->dpos, sbrd->dlen, ctx->s, 0, DE_ENCODING_ASCII);
386 static void do_comment_extension(deark *c, lctx *d, i64 pos1)
388 struct comment_ext_ctx ctx;
389 i64 pos = pos1;
391 de_zeromem(&ctx, sizeof(struct comment_ext_ctx));
392 ctx.s = ucstring_create(c);
393 if(c->extract_level>=2) {
394 ctx.outf = dbuf_create_output_file(c, "comment.txt", NULL, DE_CREATEFLAG_IS_AUX);
397 do_read_subblocks_p(c, d, c->infile, callback_for_comment_ext, (void*)&ctx, &pos);
399 de_dbg(c, "comment: \"%s\"", ucstring_getpsz_d(ctx.s));
401 dbuf_close(ctx.outf);
402 ucstring_destroy(ctx.s);
405 static void decode_text_color(deark *c, lctx *d, const char *name, u8 clr_idx,
406 de_color *pclr)
408 de_color clr;
409 const char *alphastr;
410 char csamp[32];
412 clr = d->global_ct[(unsigned int)clr_idx];
413 *pclr = clr;
415 if(d->gce && d->gce->trns_color_idx_valid && d->gce->trns_color_idx==clr_idx) {
416 alphastr = ",A=0";
417 *pclr = DE_SET_ALPHA(*pclr, 0);
419 else {
420 alphastr = "";
422 de_get_colorsample_code(c, clr, csamp, sizeof(csamp));
423 de_dbg(c, "%s color: idx=%3u (%3u,%3u,%3u%s)%s", name,
424 (unsigned int)clr_idx, (unsigned int)DE_COLOR_R(clr),
425 (unsigned int)DE_COLOR_G(clr), (unsigned int)DE_COLOR_B(clr),
426 alphastr, csamp);
429 static void render_plaintext_char(deark *c, lctx *d, u8 ch,
430 i64 pos_x, i64 pos_y, i64 size_x, i64 size_y,
431 de_color fgclr, de_color bgclr)
433 i64 i, j;
434 const u8 *fontdata;
435 const u8 *chardata;
437 fontdata = de_get_8x8ascii_font_ptr();
439 if(ch<32 || ch>127) ch=32;
440 chardata = &fontdata[8 * ((unsigned int)ch - 32)];
442 for(j=0; j<size_y; j++) {
443 for(i=0; i<size_x; i++) {
444 unsigned int x2, y2;
445 int isbg;
446 de_color clr;
448 // TODO: Better character-rendering facilities.
449 // de_font_paint_character_idx() doesn't quite do what we need.
451 x2 = (unsigned int)(0.5+(((double)i)*(8.0/(double)size_x)));
452 y2 = (unsigned int)(0.5+(((double)j)*(8.0/(double)size_y)));
454 if(x2<8 && y2<8 && (chardata[y2]&(1<<(7-x2)))) {
455 isbg = 0;
457 else {
458 isbg = 1;
460 clr = isbg ? bgclr : fgclr;
461 if(DE_COLOR_A(clr)>0) {
462 de_bitmap_setpixel_rgb(d->screen_img, pos_x+i, pos_y+j, clr);
468 struct plaintext_ext_ctx {
469 int header_ok;
470 int ok_to_render; // If 0, something's wrong, and we should't draw the pixels
471 i64 textarea_xpos_in_pixels, textarea_ypos_in_pixels;
472 i64 textarea_xsize_in_pixels, textarea_ysize_in_pixels;
473 i64 text_width_in_chars;
474 i64 char_width, char_height;
475 i64 cur_xpos_in_chars, cur_ypos_in_chars;
476 de_color fgclr, bgclr;
477 unsigned char disposal_method;
478 de_bitmap *prev_img;
479 dbuf *outf_txt;
482 static void do_plaintext_ext_header(deark *c, lctx *d, struct plaintext_ext_ctx *ctx,
483 dbuf *inf, i64 pos1, i64 len)
485 u8 fgclr_idx, bgclr_idx;
486 i64 pos = pos1;
488 if(len<12) goto done;
490 if(d->gce) {
491 ctx->disposal_method = d->gce->disposal_method;
494 ctx->textarea_xpos_in_pixels = dbuf_getu16le(inf, pos);
495 ctx->textarea_ypos_in_pixels = dbuf_getu16le(inf, pos+2);
496 ctx->textarea_xsize_in_pixels = dbuf_getu16le(inf, pos+4);
497 ctx->textarea_ysize_in_pixels = dbuf_getu16le(inf, pos+6);
498 ctx->char_width = (i64)dbuf_getbyte(inf, pos+8);
499 ctx->char_height = (i64)dbuf_getbyte(inf, pos+9);
500 de_dbg(c, "text-area pos: %d,%d pixels", (int)ctx->textarea_xpos_in_pixels,
501 (int)ctx->textarea_ypos_in_pixels);
502 de_dbg(c, "text-area size: %d"DE_CHAR_TIMES"%d pixels", (int)ctx->textarea_xsize_in_pixels,
503 (int)ctx->textarea_ysize_in_pixels);
504 de_dbg(c, "character size: %d"DE_CHAR_TIMES"%d pixels", (int)ctx->char_width, (int)ctx->char_height);
506 if(ctx->char_width<3 || ctx->char_height<3) {
507 ctx->ok_to_render = 0;
510 if(ctx->char_width>0) {
511 ctx->text_width_in_chars = ctx->textarea_xsize_in_pixels / ctx->char_width;
512 if(ctx->text_width_in_chars<1) {
513 ctx->ok_to_render = 0;
514 ctx->text_width_in_chars = 1;
517 else {
518 ctx->text_width_in_chars = 80;
520 de_dbg(c, "calculated chars/line: %d", (int)ctx->text_width_in_chars);
522 fgclr_idx = dbuf_getbyte(inf, pos+10);
523 decode_text_color(c, d, "fg", fgclr_idx, &ctx->fgclr);
524 bgclr_idx = dbuf_getbyte(inf, pos+11);
525 decode_text_color(c, d, "bg", bgclr_idx, &ctx->bgclr);
527 if(d->dump_plaintext_ext) {
528 ctx->outf_txt = dbuf_create_output_file(c, "plaintext.txt", NULL, 0);
531 if(ctx->ok_to_render && (ctx->disposal_method==DISPOSE_PREVIOUS)) {
532 i64 tmpw, tmph;
533 // We need to save a copy of the pixels that may be overwritten.
534 tmpw = ctx->textarea_xsize_in_pixels;
535 if(tmpw>d->screen_w) tmpw = d->screen_w;
536 tmph = ctx->textarea_ysize_in_pixels;
537 if(tmph>d->screen_h) tmph = d->screen_h;
538 ctx->prev_img = de_bitmap_create(c, tmpw, tmph, 4);
539 de_bitmap_copy_rect(d->screen_img, ctx->prev_img,
540 ctx->textarea_xpos_in_pixels, ctx->textarea_ypos_in_pixels,
541 tmpw, tmph, 0, 0, 0);
544 ctx->cur_xpos_in_chars = 0;
545 ctx->cur_ypos_in_chars = 0;
547 ctx->header_ok = 1;
548 done:
552 static void do_plaintext_ext_textsubblock(deark *c, lctx *d, struct plaintext_ext_ctx *ctx,
553 dbuf *inf, i64 pos1, i64 len)
555 i64 k;
557 for(k=0; k<len; k++) {
558 u8 b;
560 b = dbuf_getbyte(inf, pos1+k);
561 if(ctx->outf_txt) dbuf_writebyte(ctx->outf_txt, b);
563 if(ctx->ok_to_render &&
564 ((ctx->cur_ypos_in_chars+1)*ctx->char_height <= ctx->textarea_ysize_in_pixels))
566 render_plaintext_char(c, d, b,
567 ctx->textarea_xpos_in_pixels + ctx->cur_xpos_in_chars*ctx->char_width,
568 ctx->textarea_ypos_in_pixels + ctx->cur_ypos_in_chars*ctx->char_height,
569 ctx->char_width, ctx->char_height, ctx->fgclr, ctx->bgclr);
572 ctx->cur_xpos_in_chars++;
573 if(ctx->cur_xpos_in_chars >= ctx->text_width_in_chars) {
574 ctx->cur_ypos_in_chars++;
575 ctx->cur_xpos_in_chars = 0;
577 if(ctx->outf_txt) {
578 // Insert newlines in appropriate places.
579 dbuf_writebyte(ctx->outf_txt, '\n');
585 static void callback_for_plaintext_ext(deark *c, lctx *d, struct subblock_reader_data *sbrd)
587 struct plaintext_ext_ctx *ctx = (struct plaintext_ext_ctx*)sbrd->userdata;
589 if(sbrd->subblock_idx==0) {
590 // The first sub-block is the header
591 do_plaintext_ext_header(c, d, ctx, sbrd->inf, sbrd->dpos, sbrd->dlen);
593 else {
594 if(ctx->header_ok && sbrd->dlen>0) {
595 do_plaintext_ext_textsubblock(c, d, ctx, sbrd->inf, sbrd->dpos, sbrd->dlen);
600 static void do_plaintext_extension(deark *c, lctx *d, i64 pos1)
602 i64 pos = pos1;
603 struct plaintext_ext_ctx *ctx = NULL;
605 ctx = de_malloc(c, sizeof(struct plaintext_ext_ctx));
606 ctx->disposal_method = 0;
607 ctx->ok_to_render = 1;
609 if(!d->compose) {
610 ctx->ok_to_render = 0;
613 do_read_subblocks_p(c, d, c->infile, callback_for_plaintext_ext, (void*)ctx, &pos);
614 if(!ctx->header_ok) goto done;
616 if(d->compose) {
617 de_bitmap_write_to_file_finfo(d->screen_img, d->fi, DE_CREATEFLAG_OPT_IMAGE);
619 // TODO: Too much code is duplicated with do_image().
620 if(ctx->disposal_method==DISPOSE_BKGD) {
621 de_bitmap_rect(d->screen_img, ctx->textarea_xpos_in_pixels, ctx->textarea_ypos_in_pixels,
622 ctx->textarea_xsize_in_pixels, ctx->textarea_ysize_in_pixels,
623 DE_STOCKCOLOR_TRANSPARENT, 0);
625 else if(ctx->disposal_method==DISPOSE_PREVIOUS && ctx->prev_img) {
626 de_bitmap_copy_rect(ctx->prev_img, d->screen_img,
627 0, 0, ctx->prev_img->width, ctx->prev_img->height,
628 ctx->textarea_xpos_in_pixels, ctx->textarea_ypos_in_pixels, 0);
632 done:
633 discard_current_gce_data(c, d);
634 if(ctx) {
635 dbuf_close(ctx->outf_txt);
636 de_bitmap_destroy(ctx->prev_img);
637 de_free(c, ctx);
641 static void do_animation_extension(deark *c, lctx *d, i64 pos)
643 i64 sub_block_len;
644 u8 sub_block_id;
645 const char *name;
647 sub_block_len = (i64)de_getbyte(pos++);
648 if(sub_block_len<1) return;
650 sub_block_id = de_getbyte(pos++);
651 switch(sub_block_id) {
652 case 1: name="looping"; break;
653 case 2: name="buffering"; break;
654 default: name="?";
656 de_dbg(c, "netscape extension type: %d (%s)", (int)sub_block_id, name);
658 if(sub_block_id==1 && sub_block_len>=3) {
659 i64 loop_count;
660 loop_count = de_getu16le(pos);
661 de_dbg(c, "loop count: %d%s", (int)loop_count,
662 (loop_count==0)?" (infinite)":"");
666 static void do_xmp_extension(deark *c, lctx *d, i64 pos)
668 i64 nbytes_tot, nbytes_payload;
670 // XMP abuses GIF's subblock structure. Instead of being split into
671 // subblocks as GIF expects, XMP is stored as a single blob of bytes,
672 // followed by 258 "magic" bytes that re-sync the GIF decoder.
674 // Calculate the total number of bytes used in this series of subblocks.
675 do_skip_subblocks(c, d, pos, &nbytes_tot);
676 if(nbytes_tot<=258) return;
677 nbytes_payload = nbytes_tot-258;
678 dbuf_create_file_from_slice(c->infile, pos, nbytes_payload, "xmp", NULL, DE_CREATEFLAG_IS_AUX);
681 static void do_iccprofile_extension(deark *c, lctx *d, i64 pos)
683 dbuf *outf = NULL;
685 outf = dbuf_create_output_file(c, "icc", NULL, DE_CREATEFLAG_IS_AUX);
686 do_copy_subblocks_to_dbuf(c, d, outf, pos, 0, 0);
687 dbuf_close(outf);
690 static void do_imagemagick_extension(deark *c, lctx *d, i64 pos)
692 i64 sub_block_len;
693 de_ucstring *s = NULL;
695 sub_block_len = (i64)de_getbyte_p(&pos);
696 if(sub_block_len<1) goto done;
697 s = ucstring_create(c);
698 dbuf_read_to_ucstring(c->infile, pos, sub_block_len, s, 0, DE_ENCODING_ASCII);
699 de_dbg(c, "ImageMagick extension data: \"%s\"", ucstring_getpsz_d(s));
700 done:
701 ucstring_destroy(s);
704 static void do_mgk8bim_extension(deark *c, lctx *d, i64 pos)
706 dbuf *tmpf = NULL;
707 tmpf = dbuf_create_membuf(c, 0, 0);
708 do_copy_subblocks_to_dbuf(c, d, tmpf, pos, 1, 4*1048576);
709 de_dbg(c, "photoshop data at %"I64_FMT, pos);
710 de_dbg_indent(c, 1);
711 fmtutil_handle_photoshop_rsrc(c, tmpf, 0, tmpf->len, 0x0);
712 de_dbg_indent(c, -1);
713 dbuf_close(tmpf);
716 static void do_mgkiptc_extension(deark *c, lctx *d, i64 pos)
718 dbuf *tmpf = NULL;
719 tmpf = dbuf_create_membuf(c, 0, 0);
720 do_copy_subblocks_to_dbuf(c, d, tmpf, pos, 1, 4*1048576);
721 de_dbg(c, "IPTC-IIM data at %"I64_FMT, pos);
722 de_dbg_indent(c, 1);
723 fmtutil_handle_iptc(c, tmpf, 0, tmpf->len, 0x0);
724 de_dbg_indent(c, -1);
725 dbuf_close(tmpf);
728 static void do_unknown_extension(deark *c, lctx *d, i64 pos)
730 dbuf *tmpf = NULL;
731 tmpf = dbuf_create_membuf(c, 0, 0);
732 do_copy_subblocks_to_dbuf(c, d, tmpf, pos, 1, 256);
733 de_dbg_hexdump(c, tmpf, 0, tmpf->len, 256, NULL, 0x1);
734 dbuf_close(tmpf);
737 static void do_application_extension(deark *c, lctx *d, i64 pos)
739 de_ucstring *s = NULL;
740 u8 app_id[11];
741 i64 n;
743 n = (i64)de_getbyte(pos++);
744 if(n<11) return;
746 de_read(app_id, pos, 11);
747 pos += n;
749 s = ucstring_create(c);
750 ucstring_append_bytes(s, app_id, 11, 0, DE_ENCODING_ASCII);
751 de_dbg(c, "app id: \"%s\"", ucstring_getpsz(s));
752 ucstring_destroy(s);
754 if(!de_memcmp(app_id, "NETSCAPE2.0", 11) ||
755 !de_memcmp(app_id, "ANIMEXTS1.0", 11))
757 do_animation_extension(c, d, pos);
759 else if(!de_memcmp(app_id, "XMP DataXMP", 11)) {
760 do_xmp_extension(c, d, pos);
762 else if(!de_memcmp(app_id, "ICCRGBG1012", 11)) {
763 do_iccprofile_extension(c, d, pos);
765 else if(!de_memcmp(app_id, "ImageMagick", 11)) {
766 do_imagemagick_extension(c, d, pos);
768 else if(!de_memcmp(app_id, "MGK8BIM0000", 11)) {
769 do_mgk8bim_extension(c, d, pos);
771 else if(!de_memcmp(app_id, "MGKIPTC0000", 11)) {
772 do_mgkiptc_extension(c, d, pos);
774 else {
775 do_unknown_extension(c, d, pos);
779 static int do_read_extension(deark *c, lctx *d, i64 pos1, i64 *bytesused)
781 i64 bytesused2 = 0;
782 u8 ext_type;
783 i64 pos;
784 const char *ext_name;
786 de_dbg_indent(c, 1);
787 pos = pos1;
788 *bytesused = 0;
789 ext_type = de_getbyte(pos);
791 switch(ext_type) {
792 case 0x01: ext_name="plain text"; break;
793 case 0xf9: ext_name="graphic control"; break;
794 case 0xfe: ext_name="comment"; break;
795 case 0xff: ext_name="application"; break;
796 default: ext_name="?";
799 de_dbg(c, "extension type 0x%02x (%s) at %d", (unsigned int)ext_type, ext_name, (int)pos);
800 pos++;
802 de_dbg_indent(c, 1);
803 switch(ext_type) {
804 case 0x01:
805 do_plaintext_extension(c, d, pos);
806 break;
807 case 0xf9:
808 do_graphic_control_extension(c, d, pos);
809 break;
810 case 0xfe:
811 do_comment_extension(c, d, pos);
812 break;
813 case 0xff:
814 do_application_extension(c, d, pos);
815 break;
817 de_dbg_indent(c, -1);
819 // TODO?: It's inefficient to do this unconditionally, since we usually have
820 // already figured out where the extension ends.
821 do_skip_subblocks(c, d, pos, &bytesused2);
822 pos += bytesused2;
824 *bytesused = pos - pos1;
825 de_dbg_indent(c, -1);
827 return 1;
830 // Read 9-byte image header
831 static void do_read_image_header(deark *c, lctx *d, struct gif_image_data *gi, i64 pos)
833 u8 packed_fields;
834 unsigned int local_color_table_size_code;
836 de_dbg(c, "image descriptor at %d", (int)pos);
837 de_dbg_indent(c, 1);
839 gi->xpos = de_getu16le(pos);
840 gi->ypos = de_getu16le(pos+2);
841 de_dbg(c, "image position: (%d,%d)", (int)gi->xpos, (int)gi->ypos);
842 gi->width = de_getu16le(pos+4);
843 gi->height = de_getu16le(pos+6);
844 de_dbg(c, "image dimensions: %d"DE_CHAR_TIMES"%d", (int)gi->width, (int)gi->height);
846 packed_fields = de_getbyte(pos+8);
847 de_dbg(c, "packed fields: 0x%02x", (unsigned int)packed_fields);
848 de_dbg_indent(c, 1);
849 gi->has_local_color_table = (packed_fields&0x80)?1:0;
850 de_dbg(c, "local color table flag: %d", (int)gi->has_local_color_table);
852 gi->interlaced = (packed_fields&0x40)?1:0;
853 de_dbg(c, "interlaced: %d", (int)gi->interlaced);
855 if(gi->has_local_color_table) {
856 unsigned int sf;
857 sf = (packed_fields&0x08)?1:0;
858 de_dbg(c, "local color table sorted: %u", sf);
861 if(gi->has_local_color_table) {
862 local_color_table_size_code = (unsigned int)(packed_fields&0x07);
863 gi->local_color_table_size = de_pow2((i64)local_color_table_size_code+1);
864 de_dbg(c, "local color table size: %u (%d colors)",
865 local_color_table_size_code, (int)gi->local_color_table_size);
867 de_dbg_indent(c, -1);
869 de_dbg_indent(c, -1);
872 static void do_create_interlace_map(deark *c, lctx *d, struct gif_image_data *gi)
874 int pass;
875 i64 startrow, rowskip;
876 i64 row;
877 i64 rowcount = 0;
879 if(!gi->interlaced) return;
880 gi->interlace_map = de_mallocarray(c, gi->height, sizeof(u16));
882 for(pass=1; pass<=4; pass++) {
883 if(pass==1) { startrow=0; rowskip=8; }
884 else if(pass==2) { startrow=4; rowskip=8; }
885 else if(pass==3) { startrow=2; rowskip=4; }
886 else { startrow=1; rowskip=2; }
888 for(row=startrow; row<gi->height; row+=rowskip) {
889 gi->interlace_map[rowcount] = (u16)row;
890 rowcount++;
895 struct my_giflzw_userdata {
896 deark *c;
897 lctx *d;
898 struct gif_image_data *gi;
901 static void my_giflzw_write_cb(dbuf *f, void *userdata,
902 const u8 *buf, i64 size)
904 i64 i;
905 struct my_giflzw_userdata *u = (struct my_giflzw_userdata*)userdata;
907 for(i=0; i<(i64)size; i++) {
908 do_record_pixel(u->c, u->d, u->gi,
909 buf[i], i);
911 u->gi->pixels_set += (i64)size;
914 static void callback_for_image_subblock(deark *c, lctx *d, struct subblock_reader_data *sbrd)
916 struct gif_image_data *gi = (struct gif_image_data*)sbrd->userdata;
918 if(sbrd->reported_dlen==0) {
919 de_dbg(c, "block terminator at %"I64_FMT, sbrd->subblkpos);
920 return;
922 de_dbg2(c, "sub-block at %"I64_FMT", size=%"I64_FMT, sbrd->subblkpos, sbrd->reported_dlen);
924 if(!gi->failure_flag && !gi->dfctx->finished_flag) {
925 de_dfilter_addslice(gi->dfctx, sbrd->inf, sbrd->dpos, sbrd->dlen);
929 // Returns nonzero if parsing can continue.
930 // If an image was successfully decoded, also sets gi->img.
931 static int do_image_internal(deark *c, lctx *d,
932 struct gif_image_data *gi, i64 pos1, i64 *bytesused)
934 int retval = 0;
935 i64 pos;
936 int bypp;
937 int saved_indent_level;
938 unsigned int lzw_min_code_size;
939 i64 npixels_total;
940 dbuf *custom_outf = NULL;
941 struct de_lzw_params delzwp;
942 struct de_dfilter_out_params dcmpro;
943 struct de_dfilter_results dres;
944 struct my_giflzw_userdata u;
946 de_dbg_indent_save(c, &saved_indent_level);
947 pos = pos1;
948 *bytesused = 0;
949 gi->failure_flag = 0;
950 gi->dfctx = NULL;
952 do_read_image_header(c, d, gi, pos);
953 pos += 9;
955 if(gi->has_local_color_table) {
956 de_dbg(c, "local color table at %d", (int)pos);
957 de_dbg_indent(c, 1);
958 do_read_color_table(c, d, pos, gi->local_color_table_size, gi->local_ct);
959 de_dbg_indent(c, -1);
960 pos += 3*gi->local_color_table_size;
963 if(c->infile->len-pos < 1) {
964 on_unexpected_eof(c, d);
965 goto done;
967 de_dbg(c, "image data at %d", (int)pos);
968 de_dbg_indent(c, 1);
969 lzw_min_code_size = (unsigned int)de_getbyte(pos++);
970 de_dbg(c, "lzw min code size: %u", lzw_min_code_size);
972 // Using a failure_flag variable like this is ugly, but I don't like any
973 // of the other options either, short of a major redesign of this module.
974 // We have to continue to parse the image segment, even after most errors,
975 // so that we know where it ends.
977 if(gi->width==0 || gi->height==0) {
978 // This doesn't seem to be forbidden by the spec.
979 de_warn(c, "Image has zero size (%d"DE_CHAR_TIMES"%d)", (int)gi->width, (int)gi->height);
980 gi->failure_flag = 1;
982 else if(!de_good_image_dimensions(c, gi->width, gi->height)) {
983 gi->failure_flag = 1;
986 if(d->gce && d->gce->trns_color_idx_valid)
987 bypp = 4;
988 else
989 bypp = 3;
991 if(gi->failure_flag) {
992 gi->img = de_bitmap_create(c, 1, 1, 1);
994 else {
995 gi->img = de_bitmap_create(c, gi->width, gi->height, bypp);
998 if(gi->interlaced && !gi->failure_flag) {
999 do_create_interlace_map(c, d, gi);
1002 npixels_total = gi->width * gi->height;
1004 de_dfilter_init_objects(c, NULL, &dcmpro, &dres);
1005 de_zeromem(&delzwp, sizeof(struct de_lzw_params));
1006 de_zeromem(&u, sizeof(struct my_giflzw_userdata));
1007 custom_outf = dbuf_create_custom_dbuf(c, 0, 0);
1008 u.c = c;
1009 u.d = d;
1010 u.gi = gi;
1011 custom_outf->userdata_for_customwrite = (void*)&u;
1012 custom_outf->customwrite_fn = my_giflzw_write_cb;
1013 delzwp.fmt = DE_LZWFMT_GIF;
1014 delzwp.gif_root_code_size = lzw_min_code_size;
1015 dcmpro.f = custom_outf;
1016 dcmpro.len_known = 1;
1017 dcmpro.expected_len = npixels_total;
1019 gi->dfctx = de_dfilter_create(c, dfilter_lzw_codec, &delzwp, &dcmpro, &dres);
1021 do_read_subblocks_p(c, d, c->infile, callback_for_image_subblock, (void*)gi, &pos);
1022 *bytesused = pos - pos1;
1023 retval = 1;
1025 if(gi->failure_flag) {
1026 goto done;
1029 de_dfilter_finish(gi->dfctx);
1031 if(dres.errcode) {
1032 de_err(c, "Decompression failed: %s", de_dfilter_get_errmsg(c, &dres));
1033 goto done;
1036 if(gi->pixels_set < npixels_total) {
1037 de_warn(c, "Expected %"I64_FMT" pixels, only found %"I64_FMT, npixels_total, gi->pixels_set);
1040 done:
1041 if(gi->failure_flag) {
1042 de_bitmap_destroy(gi->img);
1043 gi->img = NULL;
1045 if(gi->dfctx) {
1046 de_dfilter_destroy(gi->dfctx);
1047 gi->dfctx = NULL;
1049 dbuf_close(custom_outf);
1050 de_dbg_indent_restore(c, saved_indent_level);
1051 return retval;
1054 static int do_image(deark *c, lctx *d, i64 pos1, i64 *bytesused)
1056 int retval = 0;
1057 struct gif_image_data *gi = NULL;
1058 de_bitmap *prev_img = NULL;
1059 u8 disposal_method = 0;
1061 de_dbg_indent(c, 1);
1062 gi = de_malloc(c, sizeof(struct gif_image_data));
1064 retval = do_image_internal(c, d, gi, pos1, bytesused);
1066 if(!retval) goto done;
1067 if(d->bad_screen_flag || !gi->img) {
1068 de_warn(c, "Skipping image due to errors");
1069 retval = 1;
1070 goto done;
1073 if(d->compose) {
1074 if(d->gce) {
1075 disposal_method = d->gce->disposal_method;
1078 if(disposal_method == DISPOSE_PREVIOUS) {
1079 // In this case, we need to save a copy of the pixels that may
1080 // be overwritten
1081 prev_img = de_bitmap_create(c, gi->width, gi->height, 4);
1082 de_bitmap_copy_rect(d->screen_img, prev_img,
1083 gi->xpos, gi->ypos, gi->width, gi->height,
1084 0, 0, 0);
1087 de_bitmap_copy_rect(gi->img, d->screen_img,
1088 0, 0, d->screen_img->width, d->screen_img->height,
1089 gi->xpos, gi->ypos, DE_BITMAPFLAG_MERGE);
1091 de_bitmap_write_to_file_finfo(d->screen_img, d->fi, DE_CREATEFLAG_OPT_IMAGE);
1093 if(disposal_method == DISPOSE_BKGD) {
1094 de_bitmap_rect(d->screen_img, gi->xpos, gi->ypos, gi->width, gi->height,
1095 DE_STOCKCOLOR_TRANSPARENT, 0);
1097 else if(disposal_method == DISPOSE_PREVIOUS && prev_img) {
1098 de_bitmap_copy_rect(prev_img, d->screen_img,
1099 0, 0, gi->width, gi->height,
1100 gi->xpos, gi->ypos, 0);
1103 else {
1104 de_bitmap_write_to_file_finfo(gi->img, d->fi, 0);
1107 done:
1108 de_bitmap_destroy(prev_img);
1109 if(gi) {
1110 de_bitmap_destroy(gi->img);
1111 de_free(c, gi->interlace_map);
1112 de_free(c, gi);
1115 // A Graphic Control Extension applies only to the next image (or plaintext
1116 // extension), so if there was one, delete it now that we've used it up.
1117 discard_current_gce_data(c, d);
1119 de_dbg_indent(c, -1);
1120 return retval;
1123 static void do_after_trailer(deark *c, lctx *d, i64 pos1)
1125 i64 extra_bytes_at_eof;
1126 i64 i;
1127 u8 first_byte;
1128 u8 flag = 0;
1130 extra_bytes_at_eof = c->infile->len - pos1;
1131 if(extra_bytes_at_eof<=0) return;
1133 // If all extra bytes are 0x00, or all are 0x1a, don't report it.
1134 first_byte = de_getbyte(pos1);
1135 if(first_byte==0x00 || first_byte==0x1a) {
1136 for(i=1; i<extra_bytes_at_eof; i++) {
1137 if(de_getbyte(pos1+i)!=first_byte) {
1138 flag = 1;
1139 break;
1143 if(!flag) return;
1145 de_info(c, "Note: %"I64_FMT" bytes of unidentified data found at end "
1146 "of file (starting at %"I64_FMT").", extra_bytes_at_eof, pos1);
1149 static void de_run_gif(deark *c, de_module_params *mparams)
1151 lctx *d = NULL;
1152 i64 pos;
1153 i64 bytesused = 0;
1154 u8 block_type;
1155 const char *blk_name;
1157 d = de_malloc(c, sizeof(lctx));
1158 d->compose = 1;
1159 d->fi = de_finfo_create(c);
1161 if(de_get_ext_option(c, "gif:raw")) {
1162 d->compose = 0;
1163 // TODO: It would be more consistent to extract an *image* of each
1164 // plain text extension, but we don't support that, so extract them
1165 // as text files instead.
1166 d->dump_plaintext_ext = 1;
1168 if(de_get_ext_option(c, "gif:dumpplaintext")) {
1169 d->dump_plaintext_ext = 1;
1171 if(de_get_ext_option(c, "gif:dumpscreen")) {
1172 // This lets the user see what the screen looks like after the last
1173 // "graphic rendering block" has been disposed of.
1174 d->dump_screen = 1;
1177 pos = 0;
1178 if(!do_read_header(c, d, pos)) goto done;
1179 pos += 6;
1180 if(!do_read_screen_descriptor(c, d, pos)) goto done;
1181 pos += 7;
1182 if(!do_read_global_color_table(c, d, pos, &bytesused)) goto done;
1183 pos += bytesused;
1185 // If we're fully rendering the frames, create a "screen" image to
1186 // track the current state of the animation.
1187 if(d->compose) {
1188 if(!de_good_image_dimensions(c, d->screen_w, d->screen_h)) {
1189 // Try to continue. There could be other interesting things in the file.
1190 d->bad_screen_flag = 1;
1191 d->screen_w = 1;
1192 d->screen_h = 1;
1194 d->screen_img = de_bitmap_create(c, d->screen_w, d->screen_h, 4);
1197 while(1) {
1198 if(pos >= c->infile->len) {
1199 on_unexpected_eof(c, d);
1200 goto done;
1202 block_type = de_getbyte(pos);
1204 switch(block_type) {
1205 case 0x2c: blk_name="image"; break;
1206 case 0x3b: blk_name="trailer"; break;
1207 case 0x21: blk_name="extension"; break;
1208 default: blk_name="?"; break;
1211 de_dbg(c, "block type 0x%02x (%s) at %"I64_FMT, (UI)block_type, blk_name, pos);
1212 pos++;
1214 switch(block_type) {
1215 case 0x3b:
1216 goto found_trailer;
1217 case 0x21:
1218 if(!do_read_extension(c, d, pos, &bytesused)) goto done;
1219 pos += bytesused;
1220 break;
1221 case 0x2c:
1222 if(!do_image(c, d, pos, &bytesused)) goto done;
1223 pos += bytesused;
1224 break;
1225 default:
1226 de_err(c, "Unknown block type: 0x%02x", (unsigned int)block_type);
1227 goto done;
1231 found_trailer:
1232 do_after_trailer(c, d, pos);
1234 done:
1235 if(d) {
1236 if(d->screen_img) {
1237 if(d->dump_screen) {
1238 de_finfo_set_name_from_sz(c, d->fi, "screen", 0, DE_ENCODING_LATIN1);
1239 de_bitmap_write_to_file_finfo(d->screen_img, d->fi, DE_CREATEFLAG_OPT_IMAGE);
1240 de_finfo_set_name_from_sz(c, d->fi, NULL, 0, DE_ENCODING_LATIN1);
1242 de_bitmap_destroy(d->screen_img);
1244 discard_current_gce_data(c, d);
1245 de_finfo_destroy(c, d->fi);
1246 de_free(c, d);
1250 static int de_identify_gif(deark *c)
1252 u8 buf[6];
1254 de_read(buf, 0, 6);
1255 if(!de_memcmp(buf, "GIF87a", 6)) return 100;
1256 if(!de_memcmp(buf, "GIF89a", 6)) return 100;
1257 return 0;
1260 static void de_help_gif(deark *c)
1262 de_msg(c, "-opt gif:raw : Extract individual component images");
1263 de_msg(c, "-opt gif:dumpplaintext : Also extract plain text extensions to text files");
1264 de_msg(c, "-opt gif:dumpscreen : Also extact the final \"screen\" contents");
1267 void de_module_gif(deark *c, struct deark_module_info *mi)
1269 mi->id = "gif";
1270 mi->desc = "GIF image";
1271 mi->run_fn = de_run_gif;
1272 mi->identify_fn = de_identify_gif;
1273 mi->help_fn = de_help_gif;