int Gif_FullUncompressImage(Gif_Image *gfi, Gif_ReadErrorHandler h, void *hthunk) { Gif_Context gfc; Gif_Stream fake_gfs; Gif_Reader grr; int ok = 0; /* return right away if image is already uncompressed. this might screw over people who expect re-uncompressing to restore the compressed version. */ if (gfi->img) return 2; if (gfi->image_data) /* we have uncompressed data, but not an 'img' array; this shouldn't happen */ return 0; fake_gfs.errors = 0; gfc.stream = &fake_gfs; gfc.prefix = Gif_NewArray(Gif_Code, GIF_MAX_CODE); gfc.suffix = Gif_NewArray(uint8_t, GIF_MAX_CODE); gfc.length = Gif_NewArray(uint16_t, GIF_MAX_CODE); gfc.handler = h; gfc.handler_thunk = hthunk; if (gfi && gfc.prefix && gfc.suffix && gfc.length && gfi->compressed) { make_data_reader(&grr, gfi->compressed, gfi->compressed_len); ok = uncompress_image(&gfc, gfi, &grr); } Gif_DeleteArray(gfc.prefix); Gif_DeleteArray(gfc.suffix); Gif_DeleteArray(gfc.length); return ok && !fake_gfs.errors; }
static void delete_opt_data(Gif_OptData *od) { if (!od) return; Gif_DeleteArray(od->needed_colors); Gif_Delete(od); }
static void finalize_optimizer(Gif_Stream *gfs, int optimize_flags) { int i; if (background == TRANSP) gfs->background = (uint8_t)gfs->images[0]->transparent; /* 11.Mar.2010 - remove entirely transparent frames. */ for (i = 1; i < gfs->nimages && !(optimize_flags & GT_OPT_KEEPEMPTY); ++i) { Gif_Image *gfi = gfs->images[i]; if (gfi->width == 1 && gfi->height == 1 && gfi->transparent >= 0 && !gfi->identifier && !gfi->comment && (gfi->disposal == GIF_DISPOSAL_ASIS || gfi->disposal == GIF_DISPOSAL_NONE || gfi->disposal == GIF_DISPOSAL_PREVIOUS) && gfi->delay && gfs->images[i-1]->delay) { Gif_UncompressImage(gfi); if (gfi->img[0][0] == gfi->transparent && (gfs->images[i-1]->disposal == GIF_DISPOSAL_ASIS || gfs->images[i-1]->disposal == GIF_DISPOSAL_NONE)) { gfs->images[i-1]->delay += gfi->delay; Gif_DeleteImage(gfi); memmove(&gfs->images[i], &gfs->images[i+1], sizeof(Gif_Image *) * (gfs->nimages - i - 1)); --gfs->nimages; --i; } } } /* 10.Dec.1998 - prefer GIF_DISPOSAL_NONE to GIF_DISPOSAL_ASIS. This is semantically "wrong" -- it's better to set the disposal explicitly than rely on default behavior -- but will result in smaller GIF files, since the graphic control extension can be left off in many cases. */ for (i = 0; i < gfs->nimages; i++) if (gfs->images[i]->disposal == GIF_DISPOSAL_ASIS && gfs->images[i]->delay == 0 && gfs->images[i]->transparent < 0) gfs->images[i]->disposal = GIF_DISPOSAL_NONE; Gif_DeleteColormap(in_global_map); Gif_DeleteColormap(all_colormap); Gif_DeleteArray(last_data); Gif_DeleteArray(this_data); }
int Gif_FullUnoptimize(Gif_Stream *gfs, int flags) { int ok = 1; int i; unsigned pos, size; uint16_t *screen; uint16_t background; Gif_Image *gfi; if (gfs->nimages < 1) return 1; for (i = 0; i < gfs->nimages; i++) if (gfs->images[i]->local) return 0; if (!gfs->global) return 0; Gif_CalculateScreenSize(gfs, 0); size = gfs->screen_width * gfs->screen_height; screen = Gif_NewArray(uint16_t, size); gfi = gfs->images[0]; if (gfi->transparent < 0 && gfs->global && gfs->background < gfs->global->ncol) background = gfs->background; else background = TRANSPARENT; for (pos = 0; pos != size; ++pos) screen[pos] = background; for (i = 0; i < gfs->nimages; i++) if (!unoptimize_image(gfs, gfs->images[i], screen)) ok = 0; if (ok) { if (flags & GIF_UNOPTIMIZE_SIMPLEST_DISPOSAL) { /* set disposal based on use of transparency. If (every transparent pixel in frame i is also transparent in frame i - 1), then frame i - 1 gets disposal ASIS; otherwise, disposal BACKGROUND. */ for (i = 0; i < gfs->nimages; ++i) if (i == gfs->nimages - 1 || no_more_transparency(gfs->images[i+1], gfs->images[i])) gfs->images[i]->disposal = GIF_DISPOSAL_NONE; else gfs->images[i]->disposal = GIF_DISPOSAL_BACKGROUND; } else for (i = 0; i < gfs->nimages; ++i) gfs->images[i]->disposal = GIF_DISPOSAL_BACKGROUND; } Gif_DeleteArray(screen); return ok; }
static int unoptimize_image(Gif_Stream *gfs, Gif_Image *gfi, uint16_t *screen) { unsigned size = gfs->screen_width * gfs->screen_height; int used_transparent; uint8_t *new_data = Gif_NewArray(uint8_t, size); uint16_t *new_screen = screen; if (!new_data) return 0; /* Oops! May need to uncompress it */ Gif_UncompressImage(gfs, gfi); Gif_ReleaseCompressedImage(gfi); if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { new_screen = Gif_NewArray(uint16_t, size); if (!new_screen) return 0; memcpy(new_screen, screen, size * sizeof(uint16_t)); } put_image_in_screen(gfs, gfi, new_screen); if (!create_image_data(gfs, gfi, new_screen, new_data, &used_transparent)) { Gif_DeleteArray(new_data); return 0; } if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) Gif_DeleteArray(new_screen); else if (gfi->disposal == GIF_DISPOSAL_BACKGROUND) put_background_in_screen(gfs, gfi, screen); gfi->left = 0; gfi->top = 0; gfi->width = gfs->screen_width; gfi->height = gfs->screen_height; gfi->disposal = used_transparent; Gif_SetUncompressedImage(gfi, new_data, Gif_Free, 0); return 1; }
int compare(Gif_Stream *s1, Gif_Stream *s2) { Gif_Colormap *newcm; int imageno1, imageno2, background1, background2; char buf1[256], buf2[256], fbuf[256]; was_different = 0; /* Compare image counts and screen sizes. If either of these differs, quit early. */ Gif_CalculateScreenSize(s1, 0); Gif_CalculateScreenSize(s2, 0); if (s1->nimages != s2->nimages && (s1->nimages == 0 || s2->nimages == 0)) { different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages); return DIFFERENT; } if (s1->screen_width != s2->screen_width || s1->screen_height != s2->screen_height) { different("screen sizes differ: <%dx%d >%dx%d", s1->screen_width, s1->screen_height, s2->screen_width, s2->screen_height); return DIFFERENT; } if (s1->screen_width == 0 || s1->screen_height == 0 || s2->screen_width == 0 || s2->screen_height == 0) { /* paranoia -- don't think this can happen */ different("zero screen sizes"); return DIFFERENT; } /* Create arrays for the image data */ screen_width = s1->screen_width; screen_height = s1->screen_height; gdata[0] = Gif_NewArray(uint16_t, screen_width * screen_height); gdata[1] = Gif_NewArray(uint16_t, screen_width * screen_height); glast[0] = Gif_NewArray(uint16_t, screen_width * screen_height); glast[1] = Gif_NewArray(uint16_t, screen_width * screen_height); scratch = Gif_NewArray(uint16_t, screen_width * screen_height); line = Gif_NewArray(uint16_t, screen_width); /* Merge all distinct colors from the two images into one colormap, setting the 'pixel' slots in the images' colormaps to the corresponding values in the merged colormap. Don't forget transparency */ newcm = Gif_NewFullColormap(1, 256); combine_colormaps(s1->global, newcm); combine_colormaps(s2->global, newcm); for (imageno1 = 0; imageno1 < s1->nimages; ++imageno1) combine_colormaps(s1->images[imageno1]->local, newcm); for (imageno2 = 0; imageno2 < s2->nimages; ++imageno2) combine_colormaps(s2->images[imageno2]->local, newcm); /* Choose the background values and clear the image data arrays */ if (s1->images[0]->transparent >= 0 || !s1->global) background1 = TRANSP; else background1 = s1->global->col[ s1->background ].pixel; if (s2->images[0]->transparent >= 0 || !s2->global) background2 = TRANSP; else background2 = s2->global->col[ s2->background ].pixel; fill_area(gdata[0], 0, 0, screen_width, screen_height, background1); fill_area(gdata[1], 0, 0, screen_width, screen_height, background2); /* Loopcounts differ? */ if (s1->loopcount != s2->loopcount) { name_loopcount(s1->loopcount, buf1); name_loopcount(s2->loopcount, buf2); different("loop counts differ: <%s >%s", buf1, buf2); } /* Loop over frames, comparing image data and delays */ apply_image(0, s1, 0, background1); apply_image(1, s2, 0, background2); imageno1 = imageno2 = 0; while (imageno1 != s1->nimages && imageno2 != s2->nimages) { int fi1 = imageno1, fi2 = imageno2, delay1 = s1->images[fi1]->delay, delay2 = s2->images[fi2]->delay; /* get message right */ if (imageno1 == imageno2) sprintf(fbuf, "#%d", imageno1); else sprintf(fbuf, "<#%d >#%d", imageno1, imageno2); /* compare pixels */ if (memcmp(gdata[0], gdata[1], screen_width * screen_height * sizeof(uint16_t)) != 0) { unsigned d, c = screen_width * screen_height; uint16_t *d1 = gdata[0], *d2 = gdata[1]; for (d = 0; d < c; d++, d1++, d2++) if (*d1 != *d2) { name_color(*d1, newcm, buf1); name_color(*d2, newcm, buf2); different("frame %s pixels differ: %d,%d <%s >%s", fbuf, d % screen_width, d / screen_width, buf1, buf2); break; } } /* move to next images, skipping redundancy */ for (++imageno1; imageno1 < s1->nimages && !apply_image(0, s1, imageno1, background1); ++imageno1) delay1 += s1->images[imageno1]->delay; for (++imageno2; imageno2 < s2->nimages && !apply_image(1, s2, imageno2, background2); ++imageno2) delay2 += s2->images[imageno2]->delay; if (!ignore_redundancy) { fi1 = (imageno1 - fi1) - (imageno2 - fi2); for (; fi1 > 0; --fi1) different("extra redundant frame: <#%d", imageno1 - fi1); for (; fi1 < 0; ++fi1) different("extra redundant frame: >#%d", imageno2 + fi1); } if (delay1 != delay2) { name_delay(delay1, buf1); name_delay(delay2, buf2); different("frame %s delays differ: <%s >%s", fbuf, buf1, buf2); } } if (imageno1 != s1->nimages || imageno2 != s2->nimages) different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages); /* That's it! */ Gif_DeleteColormap(newcm); Gif_DeleteArray(gdata[0]); Gif_DeleteArray(gdata[1]); Gif_DeleteArray(glast[0]); Gif_DeleteArray(glast[1]); Gif_DeleteArray(scratch); Gif_DeleteArray(line); return was_different ? DIFFERENT : SAME; }
static uint8_t * prepare_colormap_map(Gif_Image *gfi, Gif_Colormap *into, uint8_t *need) { int i; int is_global = (into == out_global_map); int all_ncol = all_colormap->ncol; Gif_Color *all_col = all_colormap->col; int ncol = into->ncol; Gif_Color *col = into->col; uint8_t *map = Gif_NewArray(uint8_t, all_ncol); uint8_t into_used[256]; /* keep track of which pixel indices in 'into' have been used; initially, all unused */ for (i = 0; i < 256; i++) into_used[i] = 0; /* go over all non-transparent global pixels which MUST appear (need[P]==REQUIRED) and place them in 'into' */ for (i = 1; i < all_ncol; i++) { int val; if (need[i] != REQUIRED) continue; /* fail if a needed pixel isn't in the global map */ if (is_global) { val = all_col[i].pixel; if (val >= ncol) goto error; } else { /* always place colors in a local colormap */ if (ncol == 256) goto error; val = ncol; col[val] = all_col[i]; col[val].pixel = i; ncol++; } map[i] = val; into_used[val] = 1; } if (!is_global) { qsort(col, ncol, sizeof(Gif_Color), colormap_rgb_permutation_sorter); for (i = 0; i < ncol; ++i) map[col[i].pixel] = i; } /* now check for transparency */ gfi->transparent = -1; if (need[TRANSP]) { int transparent = -1; /* first, look for an unused index in 'into'. Pick the lowest one: the lower transparent index we get, the more likely we can shave a bit off min_code_bits later, thus saving space */ for (i = 0; i < ncol; i++) if (!into_used[i]) { transparent = i; break; } /* otherwise, [1.Aug.1999] use a fake slot for the purely transparent color. Don't actually enter the transparent color into the colormap -- we might be able to output a smaller colormap! If there's no room for it, give up */ if (transparent < 0) { if (ncol < 256) { transparent = ncol; /* 1.Aug.1999 - don't increase ncol */ col[ncol] = all_col[TRANSP]; } else goto error; } /* change mapping */ map[TRANSP] = transparent; for (i = 1; i < all_ncol; i++) if (need[i] == REPLACE_TRANSP) map[i] = transparent; gfi->transparent = transparent; } /* If we get here, it worked! Commit state changes (the number of color cells in 'into') and return the map. */ into->ncol = ncol; return map; error: /* If we get here, it failed! Return 0 and don't change global state. */ Gif_DeleteArray(map); return 0; }
static void create_out_global_map(Gif_Stream *gfs) { int all_ncol = all_colormap->ncol; int32_t *penalty = Gif_NewArray(int32_t, all_ncol); uint16_t *permute = Gif_NewArray(uint16_t, all_ncol); uint16_t *ordering = Gif_NewArray(uint16_t, all_ncol); int cur_ncol, i, imagei; int nglobal_all = (all_ncol <= 257 ? all_ncol - 1 : 256); int permutation_changed; /* initial permutation is null */ for (i = 0; i < all_ncol - 1; i++) permute[i] = i + 1; /* choose appropriate penalties for each image */ for (imagei = 0; imagei < gfs->nimages; imagei++) { Gif_OptData *opt = (Gif_OptData *)gfs->images[imagei]->user_data; opt->global_penalty = opt->colormap_penalty = 1; for (i = 2; i < opt->required_color_count; i *= 2) opt->colormap_penalty *= 3; opt->active_penalty = (all_ncol > 257 ? opt->colormap_penalty : opt->global_penalty); } /* set initial penalties for each color */ for (i = 1; i < all_ncol; i++) penalty[i] = 0; for (imagei = 0; imagei < gfs->nimages; imagei++) { Gif_OptData *opt = (Gif_OptData *)gfs->images[imagei]->user_data; increment_penalties(opt, penalty, opt->active_penalty); } permutation_changed = 1; /* Loop, removing one color at a time. */ for (cur_ncol = all_ncol - 1; cur_ncol; cur_ncol--) { uint16_t removed; /* sort permutation based on penalty */ if (permutation_changed) sort_permutation(permute, cur_ncol, penalty, 1); permutation_changed = 0; /* update reverse permutation */ removed = permute[cur_ncol - 1]; ordering[removed] = cur_ncol - 1; /* decrement penalties for colors that are out of the running */ for (imagei = 0; imagei < gfs->nimages; imagei++) { Gif_OptData *opt = (Gif_OptData *)gfs->images[imagei]->user_data; uint8_t *need = opt->needed_colors; if (opt->global_penalty > 0 && need[removed] == REQUIRED) { increment_penalties(opt, penalty, -opt->active_penalty); opt->global_penalty = 0; opt->colormap_penalty = (cur_ncol > 256 ? -1 : 0); permutation_changed = 1; } } /* change colormap penalties if we're no longer working w/globalmap */ if (cur_ncol == 257) { for (i = 0; i < all_ncol; i++) penalty[i] = 0; for (imagei = 0; imagei < gfs->nimages; imagei++) { Gif_OptData *opt = (Gif_OptData *)gfs->images[imagei]->user_data; opt->active_penalty = opt->global_penalty; increment_penalties(opt, penalty, opt->global_penalty); } permutation_changed = 1; } } /* make sure background is in the global colormap */ if (background != TRANSP && ordering[background] >= 256) { uint16_t other = permute[255]; ordering[other] = ordering[background]; ordering[background] = 255; } /* assign out_global_map based on permutation */ out_global_map = Gif_NewFullColormap(nglobal_all, 256); for (i = 1; i < all_ncol; i++) if (ordering[i] < 256) { out_global_map->col[ordering[i]] = all_colormap->col[i]; all_colormap->col[i].pixel = ordering[i]; } else all_colormap->col[i].pixel = NOT_IN_OUT_GLOBAL; /* set the stream's background color */ if (background != TRANSP) gfs->background = ordering[background]; /* cleanup */ Gif_DeleteArray(penalty); Gif_DeleteArray(permute); Gif_DeleteArray(ordering); }
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_new_image_data(Gif_Stream *gfs, int optimize_flags) { Gif_Image cur_unopt_gfi; /* placehoder; maintains pre-optimization image size so we can apply background disposal */ unsigned screen_size = (unsigned) screen_width * (unsigned) screen_height; uint16_t *previous_data = 0; Gif_CompressInfo gcinfo = gif_write_info; if ((optimize_flags & GT_OPT_MASK) >= 3) gcinfo.flags |= GIF_WRITE_OPTIMIZE; gfs->global = out_global_map; /* do first image. Remember to uncompress it if necessary */ erase_screen(last_data); erase_screen(this_data); for (image_index = 0; image_index < gfs->nimages; image_index++) { Gif_Image *cur_gfi = gfs->images[image_index]; Gif_OptData *opt = (Gif_OptData *)cur_gfi->user_data; int was_compressed = (cur_gfi->img == 0); /* save previous data if necessary */ if (cur_gfi->disposal == GIF_DISPOSAL_PREVIOUS) { if (!previous_data) previous_data = Gif_NewArray(uint16_t, screen_size); copy_data_area(previous_data, this_data, cur_gfi); } /* set up this_data to be equal to the current image */ apply_frame(this_data, cur_gfi, 0, 0); /* save actual bounds and disposal from unoptimized version so we can apply the disposal correctly next time through */ cur_unopt_gfi = *cur_gfi; /* set bounds and disposal from optdata */ Gif_ReleaseUncompressedImage(cur_gfi); cur_gfi->left = opt->left; cur_gfi->top = opt->top; cur_gfi->width = opt->width; cur_gfi->height = opt->height; cur_gfi->disposal = opt->disposal; if (image_index > 0) cur_gfi->interlace = 0; /* find the new image's colormap and then make new data */ { uint8_t *map = prepare_colormap(cur_gfi, opt->needed_colors); uint8_t *data = Gif_NewArray(uint8_t, (size_t) cur_gfi->width * (size_t) cur_gfi->height); Gif_SetUncompressedImage(cur_gfi, data, Gif_DeleteArrayFunc, 0); /* don't use transparency on first frame */ if ((optimize_flags & GT_OPT_MASK) > 1 && image_index > 0 && cur_gfi->transparent >= 0) transp_frame_data(gfs, cur_gfi, map, optimize_flags, &gcinfo); else simple_frame_data(cur_gfi, map); if (cur_gfi->img) { if (was_compressed || (optimize_flags & GT_OPT_MASK) > 1) { Gif_FullCompressImage(gfs, cur_gfi, &gcinfo); Gif_ReleaseUncompressedImage(cur_gfi); } else /* bug fix 22.May.2001 */ Gif_ReleaseCompressedImage(cur_gfi); } Gif_DeleteArray(map); } delete_opt_data(opt); cur_gfi->user_data = 0; /* Set up last_data and this_data. last_data must contain this_data + new disposal. this_data must contain this_data + old disposal. */ if (cur_gfi->disposal == GIF_DISPOSAL_NONE || cur_gfi->disposal == GIF_DISPOSAL_ASIS) copy_data_area(last_data, this_data, cur_gfi); else if (cur_gfi->disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area(last_data, background, cur_gfi); else if (cur_gfi->disposal != GIF_DISPOSAL_PREVIOUS) assert(0 && "optimized frame has strange disposal"); if (cur_unopt_gfi.disposal == GIF_DISPOSAL_BACKGROUND) fill_data_area(this_data, background, &cur_unopt_gfi); else if (cur_unopt_gfi.disposal == GIF_DISPOSAL_PREVIOUS) copy_data_area(this_data, previous_data, &cur_unopt_gfi); } 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); }