gifsicle  1.92
About: Gifsicle is a UNIX command-line tool for creating, editing, and getting information about GIF images and animations.
  Fossies Dox: gifsicle-1.92.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

gifdiff.c
Go to the documentation of this file.
1 /* gifdiff.c - Gifdiff compares GIF images for identical appearance.
2  Copyright (C) 1998-2019 Eddie Kohler, ekohler@gmail.com
3  This file is part of gifdiff, in the gifsicle package.
4 
5  Gifdiff is free software. It is distributed under the GNU Public License,
6  version 2; you can copy, distribute, or alter it at will, as long
7  as this notice is kept intact and this source code is made available. There
8  is no warranty, express or implied. */
9 
10 #include <config.h>
11 #include <lcdfgif/gif.h>
12 #include <lcdf/clp.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17 #if HAVE_UNISTD_H
18 # include <unistd.h>
19 #endif
20 
21 #define QUIET_OPT 300
22 #define HELP_OPT 301
23 #define VERSION_OPT 302
24 #define IGNORE_REDUNDANCY_OPT 303
25 #define REDUNDANCY_OPT 304
26 #define IGNORE_BACKGROUND_OPT 305
27 #define BACKGROUND_OPT 306
28 
29 const Clp_Option options[] = {
30  { "help", 'h', HELP_OPT, 0, 0 },
31  { "brief", 'q', QUIET_OPT, 0, Clp_Negate },
32  { "redudancy", 0, REDUNDANCY_OPT, 0, Clp_Negate },
33  { "ignore-redundancy", 'w', IGNORE_REDUNDANCY_OPT, 0, Clp_Negate },
34  { "bg", 0, BACKGROUND_OPT, 0, Clp_Negate },
35  { "ignore-bg", 0, IGNORE_BACKGROUND_OPT, 0, Clp_Negate },
36  { "background", 0, BACKGROUND_OPT, 0, Clp_Negate },
37  { "ignore-background", 'B', IGNORE_BACKGROUND_OPT, 0, Clp_Negate },
38  { "version", 'v', VERSION_OPT, 0, 0 }
39 };
40 
41 const char *program_name;
42 
43 static const char *filename1;
44 static const char *filename2;
45 
46 static unsigned screen_width, screen_height;
47 #define TRANSP (0)
48 
49 static uint16_t *gdata[2];
50 static uint16_t *glast[2];
51 static uint16_t *scratch;
52 static uint16_t *line;
53 
54 static int brief;
55 static int ignore_redundancy;
56 static int ignore_background;
57 
58 static Clp_Parser* clp;
59 
60 
61 static void
63 {
64  int i, gfcm_ncol = gfcm ? gfcm->ncol : 0;
65  for (i = 0; i < gfcm_ncol; i++) {
66  Gif_Color *c = &gfcm->col[i];
67  c->pixel = Gif_AddColor(newcm, c, 1);
68  }
69 }
70 
71 static void
72 fill_area(uint16_t *data, int l, int t, int w, int h, uint16_t val)
73 {
74  int x;
75  data += screen_width * t + l;
76  for (; h > 0; --h) {
77  for (x = w; x > 0; --x)
78  *data++ = val;
79  data += screen_width - w;
80  }
81 }
82 
83 static void
84 copy_area(uint16_t *dst, const uint16_t *src, int l, int t, int w, int h)
85 {
86  dst += screen_width * t + l;
87  src += screen_width * t + l;
88  for (; h > 0; --h, dst += screen_width, src += screen_width)
89  memcpy(dst, src, sizeof(uint16_t) * w);
90 }
91 
92 static void
93 expand_bounds(int *lf, int *tp, int *rt, int *bt, const Gif_Image *gfi)
94 {
95  int empty = (*lf >= *rt || *tp >= *bt);
96  if (empty || gfi->left < *lf)
97  *lf = gfi->left;
98  if (empty || gfi->top < *tp)
99  *tp = gfi->top;
100  if (empty || gfi->left + gfi->width > *rt)
101  *rt = gfi->left + gfi->width;
102  if (empty || gfi->top + gfi->height > *bt)
103  *bt = gfi->top + gfi->height;
104 }
105 
106 
107 static int
108 apply_image(int is_second, Gif_Stream *gfs, int imageno, uint16_t background)
109 {
110  int i, x, y, any_change;
111  Gif_Image *gfi = gfs->images[imageno];
112  Gif_Image *pgfi = imageno ? gfs->images[imageno - 1] : 0;
113  int width = gfi->width;
114  uint16_t map[256];
115  uint16_t *data = gdata[is_second];
116  uint16_t *last = glast[is_second];
117  Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global;
118  int gfcm_ncol = gfcm ? gfcm->ncol : 0;
119 
120  /* set up colormap */
121  for (i = 0; i < gfcm_ncol; ++i)
122  map[i] = gfcm->col[i].pixel;
123  for (i = gfcm_ncol; i < 256; ++i)
124  map[i] = 1;
125  if (gfi->transparent >= 0 && gfi->transparent < 256)
126  map[gfi->transparent] = TRANSP;
127 
128  /* if this image's disposal is 'previous', save the post-disposal version in
129  'scratch' */
130  if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) {
131  copy_area(scratch, data, gfi->left, gfi->top, gfi->width, gfi->height);
132  if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS)
133  copy_area(scratch, last, pgfi->left, pgfi->top, pgfi->width, pgfi->height);
134  else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND)
135  fill_area(scratch, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background);
136  }
137 
138  /* uncompress and clip */
139  Gif_UncompressImage(gfs, gfi);
141 
142  any_change = imageno == 0;
143  {
144  int lf = 0, tp = 0, rt = 0, bt = 0;
145  expand_bounds(&lf, &tp, &rt, &bt, gfi);
146  if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS)
147  expand_bounds(&lf, &tp, &rt, &bt, pgfi);
148  else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) {
149  expand_bounds(&lf, &tp, &rt, &bt, pgfi);
150  fill_area(last, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background);
151  } else
152  pgfi = 0;
153  for (y = tp; y < bt; ++y) {
154  uint16_t *outd = data + screen_width * y + lf;
155  if (!any_change)
156  memcpy(line, outd, (rt - lf) * sizeof(uint16_t));
157  if (pgfi && y >= pgfi->top && y < pgfi->top + pgfi->height)
158  memcpy(outd + pgfi->left - lf,
159  last + screen_width * y + pgfi->left,
160  pgfi->width * sizeof(uint16_t));
161  if (y >= gfi->top && y < gfi->top + gfi->height) {
162  uint16_t *xoutd = outd + gfi->left - lf;
163  const uint8_t *ind = gfi->img[y - gfi->top];
164  for (x = 0; x < width; ++x, ++ind, ++xoutd)
165  if (map[*ind] != TRANSP)
166  *xoutd = map[*ind];
167  }
168  if (!any_change && memcmp(line, outd, (rt - lf) * sizeof(uint16_t)) != 0)
169  any_change = 1;
170  }
171  }
172 
175 
176  /* switch 'glast' with 'scratch' if necessary */
177  if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) {
178  uint16_t *x = scratch;
179  scratch = glast[is_second];
180  glast[is_second] = x;
181  }
182 
183  return any_change;
184 }
185 
186 
187 #define SAME 0
188 #define DIFFERENT 1
189 static int was_different;
190 
191 static void
192 different(const char *format, ...)
193 {
194  va_list val;
195  va_start(val, format);
196  if (!brief) {
197  vfprintf(stdout, format, val);
198  fputc('\n', stdout);
199  }
200  va_end(val);
201  was_different = 1;
202 }
203 
204 
205 static void
206 name_loopcount(int loopcount, char *buf)
207 {
208  if (loopcount < 0)
209  strcpy(buf, "none");
210  else if (loopcount == 0)
211  strcpy(buf, "forever");
212  else
213  sprintf(buf, "%d", loopcount);
214 }
215 
216 static void
217 name_delay(int delay, char *buf)
218 {
219  if (delay == 0)
220  strcpy(buf, "none");
221  else
222  sprintf(buf, "%d.%02ds", delay / 100, delay % 100);
223 }
224 
225 static void
226 name_color(int color, Gif_Colormap *gfcm, char *buf)
227 {
228  if (color == TRANSP)
229  strcpy(buf, "transparent");
230  else {
231  Gif_Color *c = &gfcm->col[color];
232  sprintf(buf, "#%02X%02X%02X", c->gfc_red, c->gfc_green, c->gfc_blue);
233  }
234 }
235 
236 
237 int
239 {
240  Gif_Colormap *newcm;
241  int imageno1, imageno2, background1, background2;
242  char buf1[256], buf2[256], fbuf[256];
243 
244  was_different = 0;
245 
246  /* Compare image counts and screen sizes. If either of these differs, quit
247  early. */
250 
251  if (s1->screen_width != s2->screen_width
252  || s1->screen_height != s2->screen_height) {
253  different("screen sizes differ: <%dx%d >%dx%d", s1->screen_width,
255  return DIFFERENT;
256  }
257 
258  if (s1->screen_width == 0 || s1->screen_height == 0
259  || s2->screen_width == 0 || s2->screen_height == 0) {
260  /* paranoia -- don't think this can happen */
261  different("zero screen sizes");
262  return DIFFERENT;
263  }
264 
265  if (s1->nimages == 0 || s2->nimages == 0) {
266  if (s1->nimages != s2->nimages) {
267  different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages);
268  return DIFFERENT;
269  } else
270  return SAME;
271  }
272 
273  /* Create arrays for the image data */
276 
277  gdata[0] = Gif_NewArray(uint16_t, screen_width * screen_height);
278  gdata[1] = Gif_NewArray(uint16_t, screen_width * screen_height);
279  glast[0] = Gif_NewArray(uint16_t, screen_width * screen_height);
280  glast[1] = Gif_NewArray(uint16_t, screen_width * screen_height);
282  line = Gif_NewArray(uint16_t, screen_width);
283 
284  /* Merge all distinct colors from the two images into one colormap, setting
285  the 'pixel' slots in the images' colormaps to the corresponding values
286  in the merged colormap. Don't forget transparency */
287  newcm = Gif_NewFullColormap(1, 256);
288  combine_colormaps(s1->global, newcm);
289  combine_colormaps(s2->global, newcm);
290  for (imageno1 = 0; imageno1 < s1->nimages; ++imageno1)
291  combine_colormaps(s1->images[imageno1]->local, newcm);
292  for (imageno2 = 0; imageno2 < s2->nimages; ++imageno2)
293  combine_colormaps(s2->images[imageno2]->local, newcm);
294 
295  /* Choose the background values */
296  background1 = background2 = TRANSP;
297  if ((s1->nimages == 0 || s1->images[0]->transparent < 0)
298  && s1->global && s1->background < s1->global->ncol)
299  background1 = s1->global->col[ s1->background ].pixel;
300  if ((s2->nimages == 0 || s2->images[0]->transparent < 0)
301  && s2->global && s2->background < s2->global->ncol)
302  background2 = s2->global->col[ s2->background ].pixel;
303 
304  /* Clear screens */
307 
308  /* Loopcounts differ? */
309  if (s1->loopcount != s2->loopcount) {
310  name_loopcount(s1->loopcount, buf1);
311  name_loopcount(s2->loopcount, buf2);
312  different("loop counts differ: <%s >%s", buf1, buf2);
313  }
314 
315  /* Loop over frames, comparing image data and delays */
316  apply_image(0, s1, 0, background1);
317  apply_image(1, s2, 0, background2);
318  imageno1 = imageno2 = 0;
319  while (imageno1 != s1->nimages && imageno2 != s2->nimages) {
320  int fi1 = imageno1, fi2 = imageno2,
321  delay1 = s1->images[fi1]->delay, delay2 = s2->images[fi2]->delay;
322 
323  /* get message right */
324  if (imageno1 == imageno2)
325  sprintf(fbuf, "#%d", imageno1);
326  else
327  sprintf(fbuf, "<#%d >#%d", imageno1, imageno2);
328 
329  /* compare pixels */
330  if (memcmp(gdata[0], gdata[1],
331  screen_width * screen_height * sizeof(uint16_t)) != 0) {
332  unsigned d, c = screen_width * screen_height;
333  uint16_t *d1 = gdata[0], *d2 = gdata[1];
334  for (d = 0; d < c; d++, d1++, d2++)
335  if (*d1 != *d2) {
336  name_color(*d1, newcm, buf1);
337  name_color(*d2, newcm, buf2);
338  different("frame %s pixels differ: %d,%d <%s >%s",
339  fbuf, d % screen_width, d / screen_width, buf1, buf2);
340  break;
341  }
342  }
343 
344  /* compare background */
345  if (!ignore_background && background1 != background2
346  && (imageno1 == 0 || s1->images[imageno1 - 1]->disposal == GIF_DISPOSAL_BACKGROUND)
347  && (imageno2 == 0 || s2->images[imageno2 - 1]->disposal == GIF_DISPOSAL_BACKGROUND)) {
348  unsigned d, c = screen_width * screen_height;
349  uint16_t *d1 = gdata[0], *d2 = gdata[1];
350  for (d = 0; d < c; ++d, ++d1, ++d2)
351  if (*d1 == TRANSP || *d2 == TRANSP) {
352  name_color(background1, newcm, buf1);
353  name_color(background2, newcm, buf2);
354  different("frame %s background pixels differ: %d,%d <%s >%s",
355  fbuf, d % screen_width, d / screen_width, buf1, buf2);
356  background1 = background2 = TRANSP;
357  break;
358  }
359  }
360 
361  /* move to next images, skipping redundancy */
362  for (++imageno1;
363  imageno1 < s1->nimages && !apply_image(0, s1, imageno1, background1);
364  ++imageno1)
365  delay1 += s1->images[imageno1]->delay;
366  for (++imageno2;
367  imageno2 < s2->nimages && !apply_image(1, s2, imageno2, background2);
368  ++imageno2)
369  delay2 += s2->images[imageno2]->delay;
370 
371  if (!ignore_redundancy) {
372  fi1 = (imageno1 - fi1) - (imageno2 - fi2);
373  for (; fi1 > 0; --fi1)
374  different("extra redundant frame: <#%d", imageno1 - fi1);
375  for (; fi1 < 0; ++fi1)
376  different("extra redundant frame: >#%d", imageno2 + fi1);
377  }
378 
379  if (delay1 != delay2) {
380  name_delay(delay1, buf1);
381  name_delay(delay2, buf2);
382  different("frame %s delays differ: <%s >%s", fbuf, buf1, buf2);
383  }
384  }
385 
386  if (imageno1 != s1->nimages || imageno2 != s2->nimages)
387  different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages);
388 
389  /* That's it! */
390  Gif_DeleteColormap(newcm);
397 
398  return was_different ? DIFFERENT : SAME;
399 }
400 
401 
402 void short_usage(void) {
403  Clp_fprintf(clp, stderr, "Usage: %s [OPTION]... FILE1 FILE2\n\
404 Try %<%s --help%> for more information.\n",
406 }
407 
408 void usage(void) {
409  Clp_fprintf(clp, stdout, "\
410 %<Gifdiff%> compares two GIF files (either images or animations) for identical\n\
411 visual appearance. An animation and an optimized version of the same animation\n\
412 should compare as the same. Gifdiff exits with status 0 if the images are\n\
413 the same, 1 if they%,re different, and 2 if there was some error.\n\
414 \n\
415 Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name);
416  Clp_fprintf(clp, stdout, "\
417 Options:\n\
418  -q, --brief Don%,t report detailed differences.\n\
419  -w, --ignore-redundancy Ignore differences in redundant frames.\n\
420  -B, --ignore-background Ignore differences in background colors.\n\
421  -h, --help Print this message and exit.\n\
422  -v, --version Print version number and exit.\n\
423 \n\
424 Report bugs to <ekohler@gmail.com>.\n");
425 }
426 
427 
428 void fatal_error(const char* format, ...) {
429  char buf[BUFSIZ];
430  int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
431  va_list val;
432  va_start(val, format);
433  Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
434  va_end(val);
435  fputs(buf, stderr);
436  exit(2); /* exit(2) for trouble */
437 }
438 
439 void error(const char* format, ...) {
440  char buf[BUFSIZ];
441  int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
442  va_list val;
443  va_start(val, format);
444  Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
445  va_end(val);
446  fputs(buf, stderr);
447 }
448 
450 
451 static void
453  int is_error, const char *message)
454 {
455  static int last_is_error = 0;
456  static int last_which_image = 0;
457  static char last_message[256];
458  static int different_error_count = 0;
459  static int same_error_count = 0;
460  int which_image = Gif_ImageNumber(gfs, gfi);
461  const char *filename = gfs->landmark;
462  if (which_image < 0)
463  which_image = gfs->nimages;
464 
465  if (gifread_error_count == 0) {
466  last_which_image = -1;
467  last_message[0] = 0;
468  different_error_count = 0;
469  }
470 
472  if (last_message[0] && different_error_count <= 10
473  && (last_which_image != which_image || message == 0
474  || strcmp(message, last_message) != 0)) {
475  const char *etype = last_is_error ? "error" : "warning";
476  error("While reading %<%s%> frame #%d:\n", filename, last_which_image);
477  if (same_error_count == 1)
478  error(" %s: %s\n", etype, last_message);
479  else if (same_error_count > 0)
480  error(" %s: %s (%d times)\n", etype, last_message, same_error_count);
481  same_error_count = 0;
482  last_message[0] = 0;
483  }
484 
485  if (message) {
486  if (last_message[0] == 0)
487  different_error_count++;
488  same_error_count++;
489  strcpy(last_message, message);
490  last_which_image = which_image;
491  last_is_error = is_error;
492  } else
493  last_message[0] = 0;
494 
495  if (different_error_count == 11 && message) {
496  error("(more errors while reading %<%s%>)\n", filename);
497  different_error_count++;
498  }
499 }
500 
501 static Gif_Stream *
502 read_stream(const char **filename)
503 {
504  FILE *f;
505  Gif_Stream *gfs;
506  if (*filename == 0) {
507 #if 0
508  /* Since gifdiff always takes explicit filename arguments,
509  allow explicit reads from terminal. */
510 #ifndef OUTPUT_GIF_TO_TERMINAL
511  if (isatty(fileno(stdin))) {
512  fatal_error("<stdin>: is a terminal\n");
513  return NULL;
514  }
515 #endif
516 #endif
517  f = stdin;
518 #if defined(_MSDOS) || defined(_WIN32)
519  _setmode(_fileno(stdin), _O_BINARY);
520 #elif defined(__DJGPP__)
521  setmode(fileno(stdin), O_BINARY);
522 #elif defined(__EMX__)
523  _fsetmode(stdin, "b");
524 #endif
525  *filename = "<stdin>";
526  } else {
527  f = fopen(*filename, "rb");
528  if (!f)
529  fatal_error("%s: %s\n", *filename, strerror(errno));
530  }
532  gfs = Gif_FullReadFile(f, GIF_READ_COMPRESSED, *filename, gifread_error);
533  if (!gfs)
534  fatal_error("%s: file not in GIF format\n", *filename);
535  return gfs;
536 }
537 
538 int
539 main(int argc, char *argv[])
540 {
541  int how_many_inputs = 0;
542  int status;
543  const char **inputp;
544  Gif_Stream *gfs1, *gfs2;
545 
546  clp = Clp_NewParser(argc, (const char * const *)argv,
547  sizeof(options) / sizeof(options[0]), options);
548 
550  brief = 0;
551 
552  while (1) {
553  int opt = Clp_Next(clp);
554  switch (opt) {
555 
556  case HELP_OPT:
557  usage();
558  exit(0);
559  break;
560 
561  case VERSION_OPT:
562  printf("gifdiff (LCDF Gifsicle) %s\n", VERSION);
563  printf("Copyright (C) 1998-2019 Eddie Kohler\n\
564 This is free software; see the source for copying conditions.\n\
565 There is NO warranty, not even for merchantability or fitness for a\n\
566 particular purpose.\n");
567  exit(0);
568  break;
569 
570  case QUIET_OPT:
571  brief = !clp->negated;
572  break;
573 
576  break;
577 
578  case REDUNDANCY_OPT:
580  break;
581 
584  break;
585 
586  case BACKGROUND_OPT:
588  break;
589 
590  case Clp_NotOption:
591  if (how_many_inputs == 2) {
592  error("too many file arguments\n");
593  goto bad_option;
594  }
595  inputp = (how_many_inputs == 0 ? &filename1 : &filename2);
596  how_many_inputs++;
597  if (strcmp(clp->vstr, "-") == 0)
598  *inputp = 0;
599  else
600  *inputp = clp->vstr;
601  break;
602 
603  bad_option:
604  case Clp_BadOption:
605  short_usage();
606  exit(1);
607  break;
608 
609  case Clp_Done:
610  goto done;
611 
612  }
613  }
614 
615  done:
616 
617  if (how_many_inputs < 2)
618  fatal_error("need exactly 2 file arguments\n");
619  if (filename1 == 0 && filename2 == 0)
620  fatal_error("can%,t read both files from stdin\n");
621 
622  gfs1 = read_stream(&filename1);
623  gfs2 = read_stream(&filename2);
624 
625  status = (compare(gfs1, gfs2) == DIFFERENT);
626  if (status == 1 && brief)
627  printf("GIF files %s and %s differ\n", filename1, filename2);
628 
629  Gif_DeleteStream(gfs1);
630  Gif_DeleteStream(gfs2);
631  return status;
632 }
name_color
static void name_color(int color, Gif_Colormap *gfcm, char *buf)
Definition: gifdiff.c:226
glast
static uint16_t * glast[2]
Definition: gifdiff.c:50
name_delay
static void name_delay(int delay, char *buf)
Definition: gifdiff.c:217
QUIET_OPT
#define QUIET_OPT
Definition: gifdiff.c:21
HELP_OPT
#define HELP_OPT
Definition: gifdiff.c:22
Gif_Stream::background
uint16_t background
Definition: gif.h:47
Gif_NewFullColormap
Gif_Colormap * Gif_NewFullColormap(int count, int capacity)
Definition: giffunc.c:90
short_usage
void short_usage(void)
Definition: gifdiff.c:402
Gif_Image::local
Gif_Colormap * local
Definition: gif.h:97
Clp_fprintf
int Clp_fprintf(Clp_Parser *clp, FILE *f, const char *format,...)
Print a message.
Definition: clp.c:2285
Gif_Color::gfc_red
uint8_t gfc_red
Definition: gif.h:173
Gif_Colormap
Definition: gif.h:180
Clp_Negate
#define Clp_Negate
Option flag: option may be negated.
Definition: clp.h:139
Gif_Stream::loopcount
long loopcount
Definition: gif.h:51
Gif_Image::top
uint16_t top
Definition: gif.h:91
ignore_redundancy
static int ignore_redundancy
Definition: gifdiff.c:55
Gif_Color
Definition: gif.h:171
Gif_AddColor
int Gif_AddColor(Gif_Colormap *, Gif_Color *, int look_from)
Definition: giffunc.c:654
options
const Clp_Option options[]
Definition: gifdiff.c:29
was_different
static int was_different
Definition: gifdiff.c:189
Clp_NewParser
Clp_Parser * Clp_NewParser(int argc, const char *const *argv, int nopt, const Clp_Option *opt)
Create a new Clp_Parser.
Definition: clp.c:503
Gif_Stream::landmark
const char * landmark
Definition: gif.h:59
REDUNDANCY_OPT
#define REDUNDANCY_OPT
Definition: gifdiff.c:25
TRANSP
#define TRANSP
Definition: gifdiff.c:47
error
void error(const char *format,...)
Definition: gifdiff.c:439
Clp_Done
#define Clp_Done
Clp_Next value: there are no more arguments.
Definition: clp.h:194
Gif_ReleaseCompressedImage
void Gif_ReleaseCompressedImage(Gif_Image *gfi)
Definition: giffunc.c:702
name_loopcount
static void name_loopcount(int loopcount, char *buf)
Definition: gifdiff.c:206
screen_height
static unsigned screen_height
Definition: gifdiff.c:46
Clp_NotOption
#define Clp_NotOption
Clp_Next value: argument was not an option.
Definition: clp.h:191
compare
int compare(Gif_Stream *s1, Gif_Stream *s2)
Definition: gifdiff.c:238
Clp_Parser
Command line parser.
Definition: clp.h:234
Gif_Image::width
uint16_t width
Definition: gif.h:88
gdata
static uint16_t * gdata[2]
Definition: gifdiff.c:49
Gif_ReleaseUncompressedImage
void Gif_ReleaseUncompressedImage(Gif_Image *gfi)
Definition: giffunc.c:713
combine_colormaps
static void combine_colormaps(Gif_Colormap *gfcm, Gif_Colormap *newcm)
Definition: gifdiff.c:62
SAME
#define SAME
Definition: gifdiff.c:187
Gif_Color::gfc_green
uint8_t gfc_green
Definition: gif.h:174
Gif_DeleteColormap
void Gif_DeleteColormap(Gif_Colormap *)
Definition: giffunc.c:546
Gif_Image::transparent
short transparent
Definition: gif.h:96
Gif_ImageNumber
int Gif_ImageNumber(Gif_Stream *gfs, Gif_Image *gfi)
Definition: giffunc.c:294
Gif_Color::gfc_blue
uint8_t gfc_blue
Definition: gif.h:175
Gif_Image::left
uint16_t left
Definition: gif.h:90
ignore_background
static int ignore_background
Definition: gifdiff.c:56
strerror
char * strerror(int errno)
Definition: strerror.c:13
clp
static Clp_Parser * clp
Definition: gifdiff.c:58
Gif_NewArray
#define Gif_NewArray(t, n)
Definition: gif.h:311
gifread_error
static void gifread_error(Gif_Stream *gfs, Gif_Image *gfi, int is_error, const char *message)
Definition: gifdiff.c:452
Gif_Stream::nimages
int nimages
Definition: gif.h:43
Clp_Parser::negated
int negated
Definition: clp.h:237
clp.h
Functions for parsing command line options.
GIF_READ_COMPRESSED
#define GIF_READ_COMPRESSED
Definition: gif.h:251
Gif_Stream::screen_height
uint16_t screen_height
Definition: gif.h:50
BACKGROUND_OPT
#define BACKGROUND_OPT
Definition: gifdiff.c:27
read_stream
static Gif_Stream * read_stream(const char **filename)
Definition: gifdiff.c:502
Gif_FullReadFile
Gif_Stream * Gif_FullReadFile(FILE *f, int flags, const char *landmark, Gif_ReadErrorHandler handler)
Definition: gifread.c:920
fill_area
static void fill_area(uint16_t *data, int l, int t, int w, int h, uint16_t val)
Definition: gifdiff.c:72
gif.h
Clp_BadOption
#define Clp_BadOption
Clp_Next value: argument was an erroneous option.
Definition: clp.h:197
Gif_Stream::screen_width
uint16_t screen_width
Definition: gif.h:49
Gif_Image::disposal
uint8_t disposal
Definition: gif.h:93
Gif_UncompressImage
#define Gif_UncompressImage(gfs, gfi)
Definition: gif.h:152
VERSION
#define VERSION
Definition: win32cfg.h:147
Gif_Image
Definition: gif.h:84
brief
static int brief
Definition: gifdiff.c:54
Clp_Next
int Clp_Next(Clp_Parser *clp)
Parse and return the next argument from clp.
Definition: clp.c:1835
DIFFERENT
#define DIFFERENT
Definition: gifdiff.c:188
Gif_Stream::images
Gif_Image ** images
Definition: gif.h:42
Clp_Option
Option description.
Definition: clp.h:41
Gif_Stream
Definition: gif.h:41
main
int main(int argc, char *argv[])
Definition: gifdiff.c:539
Gif_CalculateScreenSize
void Gif_CalculateScreenSize(Gif_Stream *, int force)
Definition: giffunc.c:306
Gif_Color::pixel
uint32_t pixel
Definition: gif.h:176
screen_width
static unsigned screen_width
Definition: gifdiff.c:46
Clp_Parser::vstr
const char * vstr
Definition: clp.h:240
Gif_DeleteStream
void Gif_DeleteStream(Gif_Stream *)
Definition: giffunc.c:494
gifread_error_count
static int gifread_error_count
Definition: gifdiff.c:449
background
static unsigned background
Definition: optimize.c:58
Gif_Image::delay
uint16_t delay
Definition: gif.h:92
Gif_Image::img
uint8_t ** img
Definition: gif.h:85
VERSION_OPT
#define VERSION_OPT
Definition: gifdiff.c:23
Clp_vsnprintf
int Clp_vsnprintf(Clp_Parser *clp, char *str, size_t size, const char *format, va_list val)
Format a message.
Definition: clp.c:2336
filename2
static const char * filename2
Definition: gifdiff.c:44
different
static void different(const char *format,...)
Definition: gifdiff.c:192
Gif_DeleteArray
#define Gif_DeleteArray(p)
Definition: gif.h:314
scratch
static uint16_t * scratch
Definition: gifdiff.c:51
Gif_Colormap::col
Gif_Color * col
Definition: gif.h:185
GIF_DISPOSAL_BACKGROUND
#define GIF_DISPOSAL_BACKGROUND
Definition: gif.h:119
usage
void usage(void)
Definition: gifdiff.c:408
GIF_DISPOSAL_PREVIOUS
#define GIF_DISPOSAL_PREVIOUS
Definition: gif.h:120
copy_area
static void copy_area(uint16_t *dst, const uint16_t *src, int l, int t, int w, int h)
Definition: gifdiff.c:84
filename1
static const char * filename1
Definition: gifdiff.c:43
program_name
const char * program_name
Definition: gifdiff.c:41
Clp_ProgramName
const char * Clp_ProgramName(Clp_Parser *clp)
Return clp's program name.
Definition: clp.c:1407
fatal_error
void fatal_error(const char *format,...)
Definition: gifdiff.c:428
line
static uint16_t * line
Definition: gifdiff.c:52
IGNORE_BACKGROUND_OPT
#define IGNORE_BACKGROUND_OPT
Definition: gifdiff.c:26
Gif_Stream::global
Gif_Colormap * global
Definition: gif.h:46
Gif_Colormap::ncol
int ncol
Definition: gif.h:181
IGNORE_REDUNDANCY_OPT
#define IGNORE_REDUNDANCY_OPT
Definition: gifdiff.c:24
apply_image
static int apply_image(int is_second, Gif_Stream *gfs, int imageno, uint16_t background)
Definition: gifdiff.c:108
expand_bounds
static void expand_bounds(int *lf, int *tp, int *rt, int *bt, const Gif_Image *gfi)
Definition: gifdiff.c:93
Gif_ClipImage
int Gif_ClipImage(Gif_Image *gfi, int l, int t, int w, int h)
Definition: giffunc.c:725
Gif_Image::height
uint16_t height
Definition: gif.h:89