void mark_used_colors(Gif_Stream *gfs, Gif_Image *gfi, Gt_Crop *crop, int compress_immediately) { Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; Gif_Color *col; int i, j, l, t, r, b, nleft, ncol, transp = gfi->transparent; /* There might not be a colormap. */ if (!gfcm) return; col = gfcm->col; ncol = gfcm->ncol; /* Mark color used for transparency. */ if (transp >= 0 && transp < ncol) col[transp].haspixel |= 2; /* Only mark colors until we've seen all of them. The left variable keeps track of how many are left. */ for (i = nleft = 0; i < ncol; ++i) if (!(col[i].haspixel & 1) && i != transp) ++nleft; if (nleft == 0) return; if (gfi->img || Gif_UncompressImage(gfs, gfi) == 2) compress_immediately = 0; /* Loop over every pixel (until we've seen all colors) */ if (crop) { Gt_Crop c; combine_crop(&c, crop, gfi); l = c.x; t = c.y; r = l + c.w; b = t + c.h; } else { l = t = 0; r = gfi->width; b = gfi->height; } for (j = t; j != b; ++j) { uint8_t *data = gfi->img[j] + l; for (i = l; i != r; ++i, ++data) if (*data < ncol && !(col[*data].haspixel & 1) && *data != transp) { col[*data].haspixel |= 1; --nleft; if (nleft == 0) goto done; } } done: if (compress_immediately > 0) Gif_ReleaseUncompressedImage(gfi); }
static void apply_frame(uint16_t *dst, Gif_Stream* gfs, Gif_Image* gfi, int replace, int save_uncompressed) { int i, y, was_compressed = 0; uint16_t map[256]; Gif_Colormap *colormap = gfi->local ? gfi->local : in_global_map; Gif_OptBounds ob = safe_bounds(gfi); if (!gfi->img) { was_compressed = 1; Gif_UncompressImage(gfs, gfi); } /* make sure transparency maps to TRANSP */ for (i = 0; i < colormap->ncol; i++) map[i] = colormap->col[i].pixel; /* out-of-bounds colors map to 0, for the sake of argument */ for (i = colormap->ncol; i < 256; i++) map[i] = colormap->col[0].pixel; if (gfi->transparent >= 0 && gfi->transparent < 256) map[gfi->transparent] = TRANSP; else replace = 1; /* map the image */ dst += ob.left + ob.top * (unsigned) screen_width; for (y = 0; y < ob.height; y++) { uint8_t *gfi_pointer = gfi->img[y]; int x; if (replace) for (x = 0; x < ob.width; x++) dst[x] = map[gfi_pointer[x]]; else for (x = 0; x < ob.width; x++) { uint16_t new_pixel = map[gfi_pointer[x]]; if (new_pixel != TRANSP) dst[x] = new_pixel; } dst += screen_width; } if (was_compressed && !save_uncompressed) Gif_ReleaseUncompressedImage(gfi); }
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); }
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; }
static int apply_image(int is_second, Gif_Stream *gfs, int imageno, uint16_t background) { int i, x, y, any_change; Gif_Image *gfi = gfs->images[imageno]; Gif_Image *pgfi = imageno ? gfs->images[imageno - 1] : 0; int width = gfi->width; uint16_t map[256]; uint16_t *data = gdata[is_second]; uint16_t *last = glast[is_second]; Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; /* set up colormap */ for (i = 0; i < 256; i++) map[i] = 1; if (gfs) for (i = 0; i < gfcm->ncol; i++) map[i] = gfcm->col[i].pixel; if (gfi->transparent >= 0 && gfi->transparent < 256) map[gfi->transparent] = TRANSP; /* if this image's disposal is 'previous', save the post-disposal version in 'scratch' */ if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { copy_area(scratch, data, gfi->left, gfi->top, gfi->width, gfi->height); if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS) copy_area(scratch, last, pgfi->left, pgfi->top, pgfi->width, pgfi->height); else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) fill_area(scratch, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background); } /* uncompress and clip */ Gif_UncompressImage(gfs, gfi); Gif_ClipImage(gfi, 0, 0, screen_width, screen_height); any_change = imageno == 0; { int lf = 0, tp = 0, rt = 0, bt = 0; expand_bounds(&lf, &tp, &rt, &bt, gfi); if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS) expand_bounds(&lf, &tp, &rt, &bt, pgfi); else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) { expand_bounds(&lf, &tp, &rt, &bt, pgfi); fill_area(last, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background); } else pgfi = 0; for (y = tp; y < bt; ++y) { uint16_t *outd = data + screen_width * y + lf; if (!any_change) memcpy(line, outd, (rt - lf) * sizeof(uint16_t)); if (pgfi && y >= pgfi->top && y < pgfi->top + pgfi->height) memcpy(outd + pgfi->left - lf, last + screen_width * y + pgfi->left, pgfi->width * sizeof(uint16_t)); if (y >= gfi->top && y < gfi->top + gfi->height) { uint16_t *xoutd = outd + gfi->left - lf; const uint8_t *ind = gfi->img[y - gfi->top]; for (x = 0; x < width; ++x, ++ind, ++xoutd) if (map[*ind] != TRANSP) *xoutd = map[*ind]; } if (!any_change && memcmp(line, outd, (rt - lf) * sizeof(uint16_t)) != 0) any_change = 1; } } Gif_ReleaseUncompressedImage(gfi); Gif_ReleaseCompressedImage(gfi); /* switch 'glast' with 'scratch' if necessary */ if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { uint16_t *x = scratch; scratch = glast[is_second]; glast[is_second] = x; } return any_change; }