Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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;
}