static void create_subimages(Gif_Stream *gfs, int optimize_flags, int save_uncompressed) { unsigned screen_size; Gif_Image *last_gfi; int next_data_valid; uint16_t *previous_data = 0; int local_color_tables = 0; screen_size = (unsigned) screen_width * (unsigned) screen_height; next_data = Gif_NewArray(uint16_t, screen_size); next_data_valid = 0; /* do first image. Remember to uncompress it if necessary */ erase_screen(last_data); erase_screen(this_data); last_gfi = 0; /* PRECONDITION: previous_data -- garbage last_data -- optimized image after disposal of previous optimized frame this_data -- input image after disposal of previous input frame next_data -- input image after application of current input frame, if next_image_valid */ for (image_index = 0; image_index < gfs->nimages; image_index++) { Gif_Image *gfi = gfs->images[image_index]; Gif_OptData *subimage = new_opt_data(); if (gfi->local) local_color_tables = 1; /* save previous data if necessary */ if (gfi->disposal == GIF_DISPOSAL_PREVIOUS || (local_color_tables && image_index > 0 && last_gfi->disposal > GIF_DISPOSAL_ASIS)) { if (!previous_data) previous_data = Gif_NewArray(uint16_t, screen_size); memcpy(previous_data, this_data, sizeof(uint16_t) * screen_size); } /* set this_data equal to the current image */ if (next_data_valid) { uint16_t *temp = this_data; this_data = next_data; next_data = temp; next_data_valid = 0; } else apply_frame(this_data, gfi, 0, save_uncompressed); retry_frame: /* find minimum area of difference between this image and last image */ subimage->disposal = GIF_DISPOSAL_ASIS; if (image_index > 0) find_difference_bounds(subimage, gfi, last_gfi); else { Gif_OptBounds ob = safe_bounds(gfi); subimage->left = ob.left; subimage->top = ob.top; subimage->width = ob.width; subimage->height = ob.height; } /* might need to expand difference border if transparent background & background disposal */ if ((gfi->disposal == GIF_DISPOSAL_BACKGROUND || gfi->disposal == GIF_DISPOSAL_PREVIOUS) && background == TRANSP && image_index < gfs->nimages - 1) { /* set up next_data */ Gif_Image *next_gfi = gfs->images[image_index + 1]; apply_frame_disposal(next_data, this_data, previous_data, gfi); apply_frame(next_data, next_gfi, 0, save_uncompressed); next_data_valid = 1; /* expand border as necessary */ if (expand_difference_bounds(subimage, gfi)) subimage->disposal = GIF_DISPOSAL_BACKGROUND; } fix_difference_bounds(subimage); /* set map of used colors */ { int use_transparency = (optimize_flags & GT_OPT_MASK) > 1 && image_index > 0; if (image_index == 0 && background == TRANSP) use_transparency = 2; get_used_colors(subimage, use_transparency); /* Gifsicle's optimization strategy normally creates frames with ASIS or BACKGROUND disposal (not PREVIOUS disposal). However, there are cases when PREVIOUS disposal is strictly required, or a frame would require more than 256 colors. Detect this case and try to recover. */ if (subimage->required_color_count > 256) { if (image_index > 0 && local_color_tables) { Gif_OptData *subimage = (Gif_OptData*) last_gfi->user_data; if ((last_gfi->disposal == GIF_DISPOSAL_PREVIOUS || last_gfi->disposal == GIF_DISPOSAL_BACKGROUND) && subimage->disposal != last_gfi->disposal) { subimage->disposal = last_gfi->disposal; memcpy(last_data, previous_data, sizeof(uint16_t) * screen_size); goto retry_frame; } } fatal_error("%d colors required in a frame (256 is max)", subimage->required_color_count); } } gfi->user_data = subimage; last_gfi = gfi; /* Apply optimized disposal to last_data and unoptimized disposal to this_data. Before 9.Dec.1998 I applied unoptimized disposal uniformly to both. This led to subtle bugs. After all, to determine bounds, we want to compare the current image (only obtainable through unoptimized disposal) with what WILL be left after the previous OPTIMIZED image's disposal. This fix is repeated in create_new_image_data */ if (subimage->disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area_subimage(last_data, background, subimage); else copy_data_area_subimage(last_data, this_data, subimage); if (last_gfi->disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area(this_data, background, last_gfi); else if (last_gfi->disposal == GIF_DISPOSAL_PREVIOUS) { uint16_t *temp = previous_data; previous_data = this_data; this_data = temp; } } Gif_DeleteArray(next_data); if (previous_data) Gif_DeleteArray(previous_data); }
static void create_subimages(Gif_Stream *gfs, int optimize_flags, int save_uncompressed) { int screen_size; Gif_Image *last_gfi; int next_data_valid; uint16_t *previous_data = 0; screen_size = screen_width * screen_height; next_data = Gif_NewArray(uint16_t, screen_size); next_data_valid = 0; /* do first image. Remember to uncompress it if necessary */ erase_screen(last_data); erase_screen(this_data); last_gfi = 0; /* PRECONDITION: last_data, previous_data -- garbage this_data -- equal to image data after disposal of previous image next_data -- equal to image data for next image if next_image_valid */ for (image_index = 0; image_index < gfs->nimages; image_index++) { Gif_Image *gfi = gfs->images[image_index]; Gif_OptData *subimage = new_opt_data(); /* save previous data if necessary */ if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { if (!previous_data) previous_data = Gif_NewArray(uint16_t, screen_size); memcpy(previous_data, this_data, sizeof(uint16_t) * screen_size); } /* set this_data equal to the current image */ if (next_data_valid) { uint16_t *temp = this_data; this_data = next_data; next_data = temp; next_data_valid = 0; } else apply_frame(this_data, gfi, 0, save_uncompressed); /* find minimum area of difference between this image and last image */ subimage->disposal = GIF_DISPOSAL_ASIS; if (image_index > 0) find_difference_bounds(subimage, gfi, last_gfi); else { Gif_OptBounds ob = safe_bounds(gfi); subimage->left = ob.left; subimage->top = ob.top; subimage->width = ob.width; subimage->height = ob.height; } /* might need to expand difference border if transparent background & background disposal */ if ((gfi->disposal == GIF_DISPOSAL_BACKGROUND || gfi->disposal == GIF_DISPOSAL_PREVIOUS) && background == TRANSP && image_index < gfs->nimages - 1) { /* set up next_data */ Gif_Image *next_gfi = gfs->images[image_index + 1]; apply_frame_disposal(next_data, this_data, previous_data, gfi); apply_frame(next_data, next_gfi, 0, save_uncompressed); next_data_valid = 1; /* expand border as necessary */ if (expand_difference_bounds(subimage, gfi)) subimage->disposal = GIF_DISPOSAL_BACKGROUND; } fix_difference_bounds(subimage); /* set map of used colors */ { int use_transparency = (optimize_flags & GT_OPT_MASK) > 1 && image_index > 0; if (image_index == 0 && background == TRANSP) use_transparency = 2; get_used_colors(subimage, use_transparency); } gfi->user_data = subimage; last_gfi = gfi; /* Apply optimized disposal to last_data and unoptimized disposal to this_data. Before 9.Dec.1998 I applied unoptimized disposal uniformly to both. This led to subtle bugs. After all, to determine bounds, we want to compare the current image (only obtainable through unoptimized disposal) with what WILL be left after the previous OPTIMIZED image's disposal. This fix is repeated in create_new_image_data */ if (subimage->disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area_subimage(last_data, background, subimage); else copy_data_area_subimage(last_data, this_data, subimage); if (last_gfi->disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area(this_data, background, last_gfi); else if (last_gfi->disposal == GIF_DISPOSAL_PREVIOUS) { uint16_t *temp = previous_data; previous_data = this_data; this_data = temp; } } Gif_DeleteArray(next_data); if (previous_data) Gif_DeleteArray(previous_data); }