Exemple #1
0
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;
}
Exemple #2
0
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;
}
Exemple #3
0
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;
}
Exemple #4
0
static void
load_closest(Gif_XContext *gfx)
{
  XColor *color;
  uint16_t ncolor;
  uint16_t ncolormap;
  int i;

  if (gfx->closest) return;

  ncolormap = ncolor = gfx->ncolormap;
  if (ncolor > 256) ncolor = 256;
  color = Gif_NewArray(XColor, ncolor);

  if (ncolormap > 256)
    for (i = 0; i < ncolor; i++)
      color[i].pixel = (rand() >> 4) % ncolormap;
  else
    for (i = 0; i < ncolor; i++)
Exemple #5
0
static int
read_compressed_image(Gif_Image *gfi, Gif_Reader *grr, int read_flags)
{
  if (grr->is_record) {
    const uint8_t *first = grr->v;
    uint32_t pos;

    /* scan over image */
    pos = 1;			/* skip min code size */
    while (pos < grr->w) {
      int amt = grr->v[pos];
      pos += amt + 1;
      if (amt == 0) break;
    }
    if (pos > grr->w) pos = grr->w;

    gfi->compressed_len = pos;
    if (read_flags & GIF_READ_CONST_RECORD) {
      gfi->compressed = (uint8_t *)first;
      gfi->free_compressed = 0;
    } else {
      gfi->compressed = Gif_NewArray(uint8_t, gfi->compressed_len);
      gfi->free_compressed = Gif_Free;
      if (!gfi->compressed) return 0;
      memcpy(gfi->compressed, first, gfi->compressed_len);
    }

    /* move reader over that image */
    grr->v += pos;
    grr->w -= pos;

  } else {
    /* non-record; have to read it block by block. */
    uint32_t comp_cap = 1024;
    uint32_t comp_len;
    uint8_t *comp = Gif_NewArray(uint8_t, comp_cap);
    int i;
    if (!comp) return 0;

    /* min code size */
    i = gifgetbyte(grr);
    comp[0] = i;
    comp_len = 1;

    i = gifgetbyte(grr);
    while (i > 0) {
      /* add 2 before check so we don't have to check after loop when appending
	 0 block */
      if (comp_len + i + 2 > comp_cap) {
	comp_cap *= 2;
	Gif_ReArray(comp, uint8_t, comp_cap);
	if (!comp) return 0;
      }
      comp[comp_len] = i;
      gifgetblock(comp + comp_len + 1, i, grr);
      comp_len += i + 1;
      i = gifgetbyte(grr);
    }
    comp[comp_len++] = 0;

    gfi->compressed = comp;
    gfi->compressed_len = comp_len;
    gfi->free_compressed = Gif_Free;
  }

  return 1;
}
Exemple #6
0
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;
}
Exemple #7
0
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
get_used_colors(Gif_OptData *bounds, int use_transparency)
{
  int top = bounds->top, width = bounds->width, height = bounds->height;
  int i, x, y;
  int all_ncol = all_colormap->ncol;
  uint8_t *need = Gif_NewArray(uint8_t, all_ncol);

  for (i = 0; i < all_ncol; i++)
    need[i] = 0;

  /* set elements that are in the image. need == 2 means the color
     must be in the map; need == 1 means the color may be replaced by
     transparency. */
  for (y = top; y < top + height; y++) {
    uint16_t *data = this_data + (unsigned) screen_width * y + bounds->left;
    uint16_t *last = last_data + (unsigned) screen_width * y + bounds->left;
    for (x = 0; x < width; x++) {
      if (data[x] != last[x])
	need[data[x]] = REQUIRED;
      else if (need[data[x]] == 0)
	need[data[x]] = REPLACE_TRANSP;
    }
  }
  if (need[TRANSP])
    need[TRANSP] = REQUIRED;

  /* check for too many colors; also force transparency if needed */
  {
    int count[3];
    /* Count distinct pixels in each category */
    count[0] = count[1] = count[2] = 0;
    for (i = 0; i < all_ncol; i++)
      count[need[i]]++;
    /* If use_transparency is large and there's room, add transparency */
    if (use_transparency > 1 && !need[TRANSP] && count[REQUIRED] < 256) {
      need[TRANSP] = REQUIRED;
      count[REQUIRED]++;
    }
    /* If too many "potentially transparent" pixels, force transparency */
    if (count[REPLACE_TRANSP] + count[REQUIRED] > 256)
      use_transparency = 1;
    /* Make sure transparency is marked necessary if we use it */
    if (count[REPLACE_TRANSP] > 0 && use_transparency && !need[TRANSP]) {
      need[TRANSP] = REQUIRED;
      count[REQUIRED]++;
    }
    /* If not using transparency, change "potentially transparent" pixels to
       "actually used" pixels */
    if (!use_transparency) {
      for (i = 0; i < all_ncol; i++)
	if (need[i] == REPLACE_TRANSP)
	  need[i] = REQUIRED;
      count[REQUIRED] += count[REPLACE_TRANSP];
    }
    /* If we can afford to have transparency, and we want to use it, then
       include it */
    if (count[REQUIRED] < 256 && use_transparency && !need[TRANSP]) {
      need[TRANSP] = REQUIRED;
      count[REQUIRED]++;
    }
    bounds->required_color_count = count[REQUIRED];
  }

  bounds->needed_colors = need;
}
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;
}
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
transp_frame_data(Gif_Stream *gfs, Gif_Image *gfi, uint8_t *map,
		  int optimize_flags, Gif_CompressInfo *gcinfo)
{
  Gif_OptBounds ob = safe_bounds(gfi);
  int x, y, transparent = gfi->transparent;
  uint16_t *last = 0;
  uint16_t *cur = 0;
  uint8_t *data, *begin_same;
  uint8_t *t2_data = 0, *last_for_t2;
  int nsame;

  /* First, try w/o transparency. Compare this to the result using
     transparency and pick the better of the two. */
  simple_frame_data(gfi, map);
  Gif_FullCompressImage(gfs, gfi, gcinfo);
  gcinfo->flags |= GIF_WRITE_SHRINK;

  /* Actually copy data to frame.

     Use transparency if possible to shrink the size of the written GIF.

     The written GIF will be small if patterns (sequences of pixel values)
     recur in the image.
     We could conceivably use transparency to produce THE OPTIMAL image,
     with the most recurring patterns of the best kinds; but this would
     be very hard (wouldn't it?). Instead, we settle for a heuristic:
     we try and create RUNS. (Since we *try* to create them, they will
     presumably recur!) A RUN is a series of adjacent pixels all with the
     same value.

     By & large, we just use the regular image's values. However, we might
     create a transparent run *not in* the regular image, if TWO OR MORE
     adjacent runs OF DIFFERENT COLORS *could* be made transparent.

     (An area can be made transparent if the corresponding area in the previous
     frame had the same colors as the area does now.)

     Why? If only one run (say of color C) could be transparent, we get no
     large immediate advantage from making it transparent (it'll be a run of
     the same length regardless). Also, we might LOSE: what if the run was
     adjacent to some more of color C, which couldn't be made transparent? If
     we use color C (instead of the transparent color), then we get a longer
     run.

     This simple heuristic does a little better than Gifwizard's (6/97)
     on some images, but does *worse than nothing at all* on others.

     However, it DOES do better than the complicated, greedy algorithm that
     preceded it; and now we pick either the transparency-optimized version or
     the normal version, whichever compresses smaller, for the best of both
     worlds. (9/98)

     On several images, making SINGLE color runs transparent wins over the
     previous heuristic, so try both at optimize level 3 or above (the cost is
     ~30%). (2/11) */

    data = begin_same = last_for_t2 = gfi->image_data;
    nsame = 0;

    for (y = 0; y < ob.height; ++y) {
	last = last_data + (unsigned) screen_width * (y + ob.top) + ob.left;
	cur = this_data + (unsigned) screen_width * (y + ob.top) + ob.left;
	for (x = 0; x < ob.width; ++x) {
	    if (*cur != *last && map[*cur] != transparent) {
		if (nsame == 1 && data[-1] != transparent
		    && (optimize_flags & GT_OPT_MASK) > 2) {
		    if (!t2_data)
			t2_data = Gif_NewArray(uint8_t, (size_t) ob.width * (size_t) ob.height);
		    memcpy(t2_data + (last_for_t2 - gfi->image_data),
			   last_for_t2, begin_same - last_for_t2);
		    memset(t2_data + (begin_same - gfi->image_data),
			   transparent, data - begin_same);
		    last_for_t2 = data;
		}
		nsame = 0;
	    } else if (nsame == 0) {
		begin_same = data;
		++nsame;
	    } else if (nsame == 1 && map[*cur] != data[-1]) {
		memset(begin_same, transparent, data - begin_same);
		++nsame;
	    }
	    if (nsame > 1)
		*data = transparent;
	    else
		*data = map[*cur];
	    ++data, ++cur, ++last;
	}
    }

    if (t2_data)
	memcpy(t2_data + (last_for_t2 - gfi->image_data),
	       last_for_t2, data - last_for_t2);


    /* Now, try compressed transparent version(s) and pick the better of the
       two (or three). */
    Gif_FullCompressImage(gfs, gfi, gcinfo);
    if (t2_data) {
	Gif_SetUncompressedImage(gfi, t2_data, Gif_DeleteArrayFunc, 0);
        Gif_FullCompressImage(gfs, gfi, gcinfo);
    }
    Gif_ReleaseUncompressedImage(gfi);

    gcinfo->flags &= ~GIF_WRITE_SHRINK;
}
Exemple #14
0
  if (gfx->closest) return;

  ncolormap = ncolor = gfx->ncolormap;
  if (ncolor > 256) ncolor = 256;
  color = Gif_NewArray(XColor, ncolor);

  if (ncolormap > 256)
    for (i = 0; i < ncolor; i++)
      color[i].pixel = (rand() >> 4) % ncolormap;
  else
    for (i = 0; i < ncolor; i++)
      color[i].pixel = i;
  XQueryColors(gfx->display, gfx->colormap, color, ncolor);

  gfx->closest = Gif_NewArray(Gif_Color, ncolor);
  for (i = 0; i < ncolor; i++) {
    Gif_Color *c = &gfx->closest[i];
    c->haspixel = 1;
    c->gfc_red = color[i].red >> 8;
    c->gfc_green = color[i].green >> 8;
    c->gfc_blue = color[i].blue >> 8;
    c->pixel = color[i].pixel;
  }
  gfx->nclosest = ncolor;

  Gif_DeleteArray(color);
}


static unsigned long
Exemple #15
0
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);
}
Exemple #16
0
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;
}