static Gif_Colormap * read_color_table(int size, Gif_Reader *grr) { Gif_Colormap *gfcm = Gif_NewFullColormap(size, size); Gif_Color *c; if (!gfcm) return 0; GIF_DEBUG(("colormap(%d)", size)); for (c = gfcm->col; size; size--, c++) { c->gfc_red = gifgetbyte(grr); c->gfc_green = gifgetbyte(grr); c->gfc_blue = gifgetbyte(grr); c->haspixel = 0; } return gfcm; }
static uint8_t * prepare_colormap(Gif_Image *gfi, uint8_t *need) { uint8_t *map; /* try to map pixel values into the global colormap */ Gif_DeleteColormap(gfi->local); gfi->local = 0; map = prepare_colormap_map(gfi, out_global_map, need); if (!map) { /* that didn't work; add a local colormap. */ gfi->local = Gif_NewFullColormap(0, 256); map = prepare_colormap_map(gfi, gfi->local, need); } return map; }
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 int initialize_optimizer(Gif_Stream *gfs) { int i; if (gfs->nimages < 1) return 0; /* combine colormaps */ all_colormap = Gif_NewFullColormap(1, 384); all_colormap->col[0].gfc_red = 255; all_colormap->col[0].gfc_green = 255; all_colormap->col[0].gfc_blue = 255; in_global_map = gfs->global; if (!in_global_map) { Gif_Color *col; in_global_map = Gif_NewFullColormap(256, 256); col = in_global_map->col; for (i = 0; i < 256; i++, col++) col->gfc_red = col->gfc_green = col->gfc_blue = i; } { int any_globals = 0; int first_transparent = -1; kchist_init(&all_colormap_hist); for (i = 0; i < gfs->nimages; i++) { Gif_Image *gfi = gfs->images[i]; if (gfi->local) all_colormap_add(gfi->local); else any_globals = 1; if (gfi->transparent >= 0 && first_transparent < 0) first_transparent = i; } if (any_globals) all_colormap_add(in_global_map); kchist_cleanup(&all_colormap_hist); /* try and maintain transparency's pixel value */ if (first_transparent >= 0) { Gif_Image *gfi = gfs->images[first_transparent]; Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; all_colormap->col[TRANSP] = gfcm->col[gfi->transparent]; } } /* find screen_width and screen_height, and clip all images to screen */ Gif_CalculateScreenSize(gfs, 0); screen_width = gfs->screen_width; screen_height = gfs->screen_height; for (i = 0; i < gfs->nimages; i++) Gif_ClipImage(gfs->images[i], 0, 0, screen_width, screen_height); /* choose background */ if (gfs->images[0]->transparent < 0 && gfs->global && gfs->background < in_global_map->ncol) background = in_global_map->col[gfs->background].pixel; else background = TRANSP; return 1; }
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 int initialize_optimizer(Gif_Stream *gfs) { int i; unsigned screen_size; if (gfs->nimages < 1) return 0; /* combine colormaps */ all_colormap = Gif_NewFullColormap(1, 384); all_colormap->col[0].gfc_red = 255; all_colormap->col[0].gfc_green = 255; all_colormap->col[0].gfc_blue = 255; in_global_map = gfs->global; if (!in_global_map) { Gif_Color *col; in_global_map = Gif_NewFullColormap(256, 256); col = in_global_map->col; for (i = 0; i < 256; i++, col++) col->gfc_red = col->gfc_green = col->gfc_blue = i; } { int any_globals = 0; int first_transparent = -1; for (i = 0; i < gfs->nimages; i++) { Gif_Image *gfi = gfs->images[i]; if (gfi->local) colormap_combine(all_colormap, gfi->local); else any_globals = 1; if (gfi->transparent >= 0 && first_transparent < 0) first_transparent = i; } if (any_globals) colormap_combine(all_colormap, in_global_map); /* try and maintain transparency's pixel value */ if (first_transparent >= 0) { Gif_Image *gfi = gfs->images[first_transparent]; Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; all_colormap->col[TRANSP] = gfcm->col[gfi->transparent]; } } /* find screen_width and screen_height, and clip all images to screen */ Gif_CalculateScreenSize(gfs, 0); screen_width = gfs->screen_width; screen_height = gfs->screen_height; for (i = 0; i < gfs->nimages; i++) Gif_ClipImage(gfs->images[i], 0, 0, screen_width, screen_height); /* create data arrays */ screen_size = (unsigned) screen_width * (unsigned) screen_height; last_data = Gif_NewArray(uint16_t, screen_size); this_data = Gif_NewArray(uint16_t, screen_size); /* set up colormaps */ gif_color_count = 2; while (gif_color_count < gfs->global->ncol && gif_color_count < 256) gif_color_count *= 2; /* choose background */ if (gfs->images[0]->transparent < 0 && gfs->background < in_global_map->ncol) background = in_global_map->col[gfs->background].pixel; else background = TRANSP; return 1; }
Gif_Image * merge_image(Gif_Stream *dest, Gif_Stream *src, Gif_Image *srci, Gt_Frame* srcfr, int same_compressed_ok) { Gif_Colormap *imagecm; int i; Gif_Colormap *localcm = 0; Gif_Colormap *destcm = dest->global; uint8_t map[256]; /* map[input pixel value] == output pixval */ int trivial_map; /* does the map take input pixval --> the same pixel value for all colors in the image? */ uint8_t inused[256]; /* inused[input pival] == 1 iff used */ uint8_t used[256]; /* used[output pixval K] == 1 iff K was used in the image */ Gif_Image *desti; /* mark colors that were actually used in this image */ imagecm = srci->local ? srci->local : src->global; merge_image_input_colors(inused, srci); for (i = imagecm ? imagecm->ncol : 0; i != 256; ++i) if (inused[i]) { lwarning(srcfr->input_filename, "some colors undefined by colormap"); break; } /* map[old_pixel_value] == new_pixel_value */ for (i = 0; i < 256; i++) map[i] = used[i] = 0; /* Merge the colormap */ if (merge_colormap_if_possible(dest->global, imagecm)) { /* Create 'map' and 'used' for global colormap. */ for (i = 0; i != 256; ++i) if (inused[i]) { if (imagecm && i < imagecm->ncol) map[i] = imagecm->col[i].pixel; else map[i] = 0; } } else { /* Need a local colormap. */ destcm = localcm = Gif_NewFullColormap(0, 256); for (i = 0; i != 256; ++i) if (inused[i]) { map[i] = localcm->ncol; localcm->col[localcm->ncol] = imagecm->col[i]; ++localcm->ncol; } } trivial_map = 1; for (i = 0; i != 256; ++i) if (inused[i]) { used[map[i]] = 1; trivial_map = trivial_map && map[i] == i; } /* Decide on a transparent index */ if (srci->transparent >= 0) { int found_transparent = -1; /* try to keep the map trivial -- prefer same transparent index */ if (trivial_map && !used[srci->transparent]) found_transparent = srci->transparent; else for (i = destcm->ncol - 1; i >= 0; i--) if (!used[i]) found_transparent = i; /* 1.Aug.1999 - Allow for the case that the transparent index is bigger than the number of colors we've created thus far. */ if (found_transparent < 0 || found_transparent >= destcm->ncol) { Gif_Color *c; found_transparent = destcm->ncol; /* 1.Aug.1999 - Don't update destcm->ncol -- we want the output colormap to be as small as possible. */ c = &destcm->col[found_transparent]; if (imagecm && srci->transparent < imagecm->ncol) *c = imagecm->col[srci->transparent]; c->haspixel = 2; assert(c->haspixel == 2 && found_transparent < 256); } map[srci->transparent] = found_transparent; if (srci->transparent != found_transparent) trivial_map = 0; } assert(destcm->ncol <= 256); /* Make the new image. */ desti = Gif_NewImage(); desti->identifier = Gif_CopyString(srci->identifier); if (srci->transparent > -1) desti->transparent = map[srci->transparent]; desti->delay = srci->delay; desti->disposal = srci->disposal; desti->left = srci->left; desti->top = srci->top; desti->interlace = srci->interlace; desti->width = srci->width; desti->height = srci->height; desti->local = localcm; if (trivial_map && same_compressed_ok && srci->compressed) { desti->compressed_len = srci->compressed_len; desti->compressed = Gif_NewArray(uint8_t, srci->compressed_len); desti->free_compressed = Gif_Free; memcpy(desti->compressed, srci->compressed, srci->compressed_len); } else { int i, j; Gif_CreateUncompressedImage(desti, desti->interlace); if (trivial_map) for (j = 0; j < desti->height; j++) memcpy(desti->img[j], srci->img[j], desti->width); else for (j = 0; j < desti->height; j++) { uint8_t *srcdata = srci->img[j]; uint8_t *destdata = desti->img[j]; for (i = 0; i < desti->width; i++, srcdata++, destdata++) *destdata = map[*srcdata]; } } /* comments and extensions */ if (srci->comment) { desti->comment = Gif_NewComment(); merge_comments(desti->comment, srci->comment); } if (srci->extension_list && !srcfr->no_extensions) { Gif_Extension* gfex; for (gfex = srci->extension_list; gfex; gfex = gfex->next) if (gfex->kind != 255 || !srcfr->no_app_extensions) Gif_AddExtension(dest, desti, Gif_CopyExtension(gfex)); } while (srcfr->extensions) { Gif_Extension* next = srcfr->extensions->next; Gif_AddExtension(dest, desti, srcfr->extensions); srcfr->extensions = next; } Gif_AddImage(dest, desti); return desti; }