예제 #1
0
파일: gifread.c 프로젝트: jaseg/gifsicle
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;
}
예제 #2
0
파일: optimize.c 프로젝트: alberthdev/CEmu
static void
delete_opt_data(Gif_OptData *od)
{
  if (!od) return;
  Gif_DeleteArray(od->needed_colors);
  Gif_Delete(od);
}
예제 #3
0
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);
}
예제 #4
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;
}
예제 #5
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;
}
예제 #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;
}
예제 #7
0
파일: optimize.c 프로젝트: alberthdev/CEmu
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;
}
예제 #8
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);
}
예제 #9
0
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);
}
예제 #10
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);
}
예제 #11
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);
}