1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
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
18 u8 trns_color_idx_valid
;
22 typedef struct localctx_struct
{
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
39 // Data about a single image
40 struct gif_image_data
{
46 int has_local_color_table
;
48 struct de_dfilter_ctx
*dfctx
;
49 i64 local_color_table_size
;
51 de_color local_ct
[256];
54 struct subblock_reader_data
{
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
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
;
85 de_zeromem(&sbrd
, sizeof(struct subblock_reader_data
));
86 sbrd
.userdata
= userdata
;
90 sbrd
.subblkpos
= *ppos
;
92 if(*ppos
>= inf
->len
) {
93 on_unexpected_eof(c
, d
);
97 sbrd
.reported_dlen
= (i64
)de_getbyte_p(ppos
);
101 if(sbrd
.dpos
+ sbrd
.reported_dlen
> inf
->len
) {
103 sbrd
.dlen
= inf
->len
- sbrd
.dpos
;
106 sbrd
.dlen
= sbrd
.reported_dlen
;
111 if(sbrd
.dlen
>0 || !eof_flag
) {
116 on_unexpected_eof(c
, d
);
121 if(sbrd
.reported_dlen
==0) break;
126 static void do_record_pixel(deark
*c
, lctx
*d
, struct gif_image_data
*gi
, unsigned int coloridx
,
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
];
146 if(gi
->has_local_color_table
&& coloridx
<gi
->local_color_table_size
) {
147 clr
= gi
->local_ct
[coloridx
];
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);
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
);
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
);
180 static int do_read_screen_descriptor(deark
*c
, lctx
*d
, i64 pos
)
184 u8 aspect_ratio_code
;
186 unsigned int global_color_table_size_code
;
188 de_dbg(c
, "screen descriptor at %d", (int)pos
);
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
);
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
) {
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);
238 static void do_read_color_table(deark
*c
, lctx
*d
, i64 pos
, i64 ncolors
,
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
);
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
;
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
)
265 do_read_subblocks_p(c
, d
, c
->infile
, callback_for_skip_subblocks
, NULL
, &pos
);
266 *bytesused
= pos
- pos1
;
269 struct copy_subblocks_ctx
{
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
;
281 if(ctx
->has_max
&& (ctx
->nbytes_copied
>= ctx
->maxlen
)) return;
282 if(sbrd
->dlen
<1) return;
284 nbytes_to_copy
= sbrd
->dlen
;
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
)
298 struct copy_subblocks_ctx ctx
;
301 ctx
.has_max
= has_max
;
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
)
315 static void do_graphic_control_extension(deark
*c
, lctx
*d
, i64 pos
)
324 discard_current_gce_data(c
, d
);
326 n
= (i64
)de_getbyte(pos
);
328 de_warn(c
, "Wrong graphic control ext. block size (expected 4, is %d)",
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
);
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;
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
{
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;
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
;
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
,
409 const char *alphastr
;
412 clr
= d
->global_ct
[(unsigned int)clr_idx
];
415 if(d
->gce
&& d
->gce
->trns_color_idx_valid
&& d
->gce
->trns_color_idx
==clr_idx
) {
417 *pclr
= DE_SET_ALPHA(*pclr
, 0);
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
),
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
)
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
++) {
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
)))) {
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
{
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
;
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
;
488 if(len
<12) goto done
;
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;
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
)) {
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;
552 static void do_plaintext_ext_textsubblock(deark
*c
, lctx
*d
, struct plaintext_ext_ctx
*ctx
,
553 dbuf
*inf
, i64 pos1
, i64 len
)
557 for(k
=0; k
<len
; k
++) {
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;
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
);
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
)
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;
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
;
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);
633 discard_current_gce_data(c
, d
);
635 dbuf_close(ctx
->outf_txt
);
636 de_bitmap_destroy(ctx
->prev_img
);
641 static void do_animation_extension(deark
*c
, lctx
*d
, i64 pos
)
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;
656 de_dbg(c
, "netscape extension type: %d (%s)", (int)sub_block_id
, name
);
658 if(sub_block_id
==1 && sub_block_len
>=3) {
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
)
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);
690 static void do_imagemagick_extension(deark
*c
, lctx
*d
, i64 pos
)
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
));
704 static void do_mgk8bim_extension(deark
*c
, lctx
*d
, i64 pos
)
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
);
711 fmtutil_handle_photoshop_rsrc(c
, tmpf
, 0, tmpf
->len
, 0x0);
712 de_dbg_indent(c
, -1);
716 static void do_mgkiptc_extension(deark
*c
, lctx
*d
, i64 pos
)
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
);
723 fmtutil_handle_iptc(c
, tmpf
, 0, tmpf
->len
, 0x0);
724 de_dbg_indent(c
, -1);
728 static void do_unknown_extension(deark
*c
, lctx
*d
, i64 pos
)
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);
737 static void do_application_extension(deark
*c
, lctx
*d
, i64 pos
)
739 de_ucstring
*s
= NULL
;
743 n
= (i64
)de_getbyte(pos
++);
746 de_read(app_id
, pos
, 11);
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
));
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
);
775 do_unknown_extension(c
, d
, pos
);
779 static int do_read_extension(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytesused
)
784 const char *ext_name
;
789 ext_type
= de_getbyte(pos
);
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
);
805 do_plaintext_extension(c
, d
, pos
);
808 do_graphic_control_extension(c
, d
, pos
);
811 do_comment_extension(c
, d
, pos
);
814 do_application_extension(c
, d
, pos
);
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
);
824 *bytesused
= pos
- pos1
;
825 de_dbg_indent(c
, -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
)
834 unsigned int local_color_table_size_code
;
836 de_dbg(c
, "image descriptor at %d", (int)pos
);
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
);
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
) {
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
)
875 i64 startrow
, rowskip
;
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
;
895 struct my_giflzw_userdata
{
898 struct gif_image_data
*gi
;
901 static void my_giflzw_write_cb(dbuf
*f
, void *userdata
,
902 const u8
*buf
, i64 size
)
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
,
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
);
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
)
937 int saved_indent_level
;
938 unsigned int lzw_min_code_size
;
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
);
949 gi
->failure_flag
= 0;
952 do_read_image_header(c
, d
, gi
, pos
);
955 if(gi
->has_local_color_table
) {
956 de_dbg(c
, "local color table at %d", (int)pos
);
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
);
967 de_dbg(c
, "image data at %d", (int)pos
);
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
)
991 if(gi
->failure_flag
) {
992 gi
->img
= de_bitmap_create(c
, 1, 1, 1);
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);
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
;
1025 if(gi
->failure_flag
) {
1029 de_dfilter_finish(gi
->dfctx
);
1032 de_err(c
, "Decompression failed: %s", de_dfilter_get_errmsg(c
, &dres
));
1036 if(gi
->pixels_set
< npixels_total
) {
1037 de_warn(c
, "Expected %"I64_FMT
" pixels, only found %"I64_FMT
, npixels_total
, gi
->pixels_set
);
1041 if(gi
->failure_flag
) {
1042 de_bitmap_destroy(gi
->img
);
1046 de_dfilter_destroy(gi
->dfctx
);
1049 dbuf_close(custom_outf
);
1050 de_dbg_indent_restore(c
, saved_indent_level
);
1054 static int do_image(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytesused
)
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");
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
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
,
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);
1104 de_bitmap_write_to_file_finfo(gi
->img
, d
->fi
, 0);
1108 de_bitmap_destroy(prev_img
);
1110 de_bitmap_destroy(gi
->img
);
1111 de_free(c
, gi
->interlace_map
);
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);
1123 static void do_after_trailer(deark
*c
, lctx
*d
, i64 pos1
)
1125 i64 extra_bytes_at_eof
;
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
) {
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
)
1155 const char *blk_name
;
1157 d
= de_malloc(c
, sizeof(lctx
));
1159 d
->fi
= de_finfo_create(c
);
1161 if(de_get_ext_option(c
, "gif:raw")) {
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.
1178 if(!do_read_header(c
, d
, pos
)) goto done
;
1180 if(!do_read_screen_descriptor(c
, d
, pos
)) goto done
;
1182 if(!do_read_global_color_table(c
, d
, pos
, &bytesused
)) goto done
;
1185 // If we're fully rendering the frames, create a "screen" image to
1186 // track the current state of the animation.
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;
1194 d
->screen_img
= de_bitmap_create(c
, d
->screen_w
, d
->screen_h
, 4);
1198 if(pos
>= c
->infile
->len
) {
1199 on_unexpected_eof(c
, d
);
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
);
1214 switch(block_type
) {
1218 if(!do_read_extension(c
, d
, pos
, &bytesused
)) goto done
;
1222 if(!do_image(c
, d
, pos
, &bytesused
)) goto done
;
1226 de_err(c
, "Unknown block type: 0x%02x", (unsigned int)block_type
);
1232 do_after_trailer(c
, d
, pos
);
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
);
1250 static int de_identify_gif(deark
*c
)
1255 if(!de_memcmp(buf
, "GIF87a", 6)) return 100;
1256 if(!de_memcmp(buf
, "GIF89a", 6)) return 100;
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
)
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
;