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)  

support.c
Go to the documentation of this file.
1 /* support.c - Support functions for gifsicle.
2  Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com
3  This file is part of gifsicle.
4 
5  Gifsicle 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 "gifsicle.h"
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <assert.h>
17 #include <errno.h>
18 
19 const char *program_name = "gifsicle";
20 static int verbose_pos = 0;
21 int error_count = 0;
22 int no_warnings = 0;
23 
24 
25 static void
26 verror(const char* landmark, int need_file,
27  int seriousness, const char *fmt, va_list val)
28 {
29  char pbuf[256], buf[BUFSIZ], xbuf[BUFSIZ];
30  const char* xfmt;
31  int n, i, p;
32  size_t xi;
33 
34  if (!fmt || !*fmt)
35  return;
36 
37  if (!landmark && need_file && active_output_data.active_output_name
38  && mode != BLANK_MODE && mode != MERGING && nested_mode != MERGING)
40  else if (!landmark)
41  landmark = "";
42 
43  if (seriousness > 2)
44  xfmt = "%s:%s%s fatal error: ";
45  else if (seriousness == 1)
46  xfmt = "%s:%s%s warning: ";
47  else
48  xfmt = "%s:%s%s ";
49  snprintf(pbuf, sizeof(pbuf), xfmt, program_name, landmark, *landmark ? ":" : "");
50  p = strlen(pbuf);
51 
52  Clp_vsnprintf(clp, buf, sizeof(buf), fmt, val);
53  n = strlen(buf);
54  if ((size_t) n + 1 < sizeof(buf) && (n == 0 || buf[n - 1] != '\n')) {
55  buf[n++] = '\n';
56  buf[n] = 0;
57  }
58 
59  xi = 0;
60  for (i = 0; i != n; ) {
61  /* char* pos = (char*) memchr(&buf[i], '\n', n - i);
62  int l = (pos ? &pos[1] - &buf[i] : n - i); */
63  int l = n - i;
64  int xd = snprintf(&xbuf[xi], sizeof(xbuf) - xi, "%.*s%.*s",
65  (int) p, pbuf, (int) l, &buf[i]);
66  i += l;
67  xi = (xi + xd > sizeof(xbuf) ? sizeof(xbuf) : xi + xd);
68  }
69 
70  if (seriousness == 1 && no_warnings)
71  return;
72  else if (seriousness > 1)
73  error_count++;
74 
76  fwrite(xbuf, 1, xi, stderr);
77 }
78 
79 void fatal_error(const char* format, ...) {
80  va_list val;
81  va_start(val, format);
82  verror((const char*) 0, 0, 3, format, val);
83  va_end(val);
84  exit(EXIT_USER_ERR);
85 }
86 
87 void lerror(const char* landmark, const char* format, ...) {
88  va_list val;
89  va_start(val, format);
90  verror(landmark, 2, 2, format, val);
91  va_end(val);
92 }
93 
94 void error(int need_file, const char* format, ...) {
95  va_list val;
96  va_start(val, format);
97  verror((const char*) 0, need_file, 2, format, val);
98  va_end(val);
99 }
100 
101 void lwarning(const char* landmark, const char* format, ...) {
102  va_list val;
103  va_start(val, format);
104  verror(landmark, 2, 1, format, val);
105  va_end(val);
106 }
107 
108 void warning(int need_file, const char* format, ...) {
109  va_list val;
110  va_start(val, format);
111  verror((const char*) 0, need_file, 1, format, val);
112  va_end(val);
113 }
114 
115 void
116 clp_error_handler(Clp_Parser *clp, const char *message)
117 {
118  (void) clp;
119  verbose_endline();
120  fputs(message, stderr);
121 }
122 
123 
124 void
126 {
127  fprintf(stderr, "Usage: %s [OPTION | FILE | FRAME]...\n\
128 Try '%s --help' for more information.\n",
130 }
131 
132 
133 void
134 usage(void)
135 {
136  printf("\
137 'Gifsicle' manipulates GIF images. Its most common uses include combining\n\
138 single images into animations, adding transparency, optimizing animations for\n\
139 space, and printing information about GIFs.\n\
140 \n\
141 Usage: %s [OPTION | FILE | FRAME]...\n\n", program_name);
142  printf("\
143 Mode options: at most one, before any filenames.\n\
144  -m, --merge Merge mode: combine inputs, write stdout.\n\
145  -b, --batch Batch mode: modify inputs, write back to\n\
146  same filenames.\n\
147  -e, --explode Explode mode: write N files for each input,\n\
148  one per frame, to 'input.frame-number'.\n\
149  -E, --explode-by-name Explode mode, but write 'input.name'.\n\n");
150  printf("\
151 General options: Also --no-OPTION for info and verbose.\n\
152  -I, --info Print info about input GIFs. Two -I's means\n\
153  normal output is not suppressed.\n\
154  --color-info, --cinfo --info plus colormap details.\n\
155  --extension-info, --xinfo --info plus extension details.\n\
156  --size-info, --sinfo --info plus compression information.\n\
157  -V, --verbose Prints progress information.\n");
158  printf("\
159  -h, --help Print this message and exit.\n\
160  --version Print version number and exit.\n\
161  -o, --output FILE Write output to FILE.\n\
162  -w, --no-warnings Don't report warnings.\n\
163  --no-ignore-errors Quit on very erroneous input GIFs.\n\
164  --conserve-memory Conserve memory at the expense of speed.\n\
165  --multifile Support concatenated GIF files.\n\
166 \n");
167  printf("\
168 Frame selections: #num, #num1-num2, #num1-, #name\n\
169 \n\
170 Frame change options:\n\
171  --delete FRAMES Delete FRAMES from input.\n\
172  --insert-before FRAME GIFS Insert GIFS before FRAMES in input.\n\
173  --append GIFS Append GIFS to input.\n\
174  --replace FRAMES GIFS Replace FRAMES with GIFS in input.\n\
175  --done Done with frame changes.\n\n");
176  printf("\
177 Image options: Also --no-OPTION and --same-OPTION.\n\
178  -B, --background COL Make COL the background color.\n\
179  --crop X,Y+WxH, --crop X,Y-X2,Y2\n\
180  Crop the image.\n\
181  --crop-transparency Crop transparent borders off the image.\n\
182  --flip-horizontal, --flip-vertical\n\
183  Flip the image.\n");
184  printf("\
185  -i, --interlace Turn on interlacing.\n\
186  -S, --logical-screen WxH Set logical screen to WxH.\n\
187  -p, --position X,Y Set frame position to (X,Y).\n\
188  --rotate-90, --rotate-180, --rotate-270, --no-rotate\n\
189  Rotate the image.\n\
190  -t, --transparent COL Make COL transparent.\n\n");
191  printf("\
192 Extension options:\n\
193  --app-extension N D Add an app extension named N with data D.\n\
194  -c, --comment TEXT Add a comment before the next frame.\n\
195  --extension N D Add an extension number N with data D.\n\
196  -n, --name TEXT Set next frame's name.\n\
197  --no-comments, --no-names, --no-extensions\n\
198  Remove comments (names, extensions) from input.\n");
199  printf("\
200 Animation options: Also --no-OPTION and --same-OPTION.\n\
201  -d, --delay TIME Set frame delay to TIME (in 1/100sec).\n\
202  -D, --disposal METHOD Set frame disposal to METHOD.\n\
203  -l, --loopcount[=N] Set loop extension to N (default forever).\n\
204  -O, --optimize[=LEVEL] Optimize output GIFs.\n\
205  -U, --unoptimize Unoptimize input GIFs.\n");
206 #if ENABLE_THREADS
207  printf("\
208  -j, --threads[=THREADS] Use multiple threads to improve speed.\n");
209 #endif
210  printf("\n\
211 Whole-GIF options: Also --no-OPTION.\n\
212  --careful Write larger GIFs that avoid bugs in other\n\
213  programs.\n\
214  --change-color COL1 COL2 Change COL1 to COL2 throughout.\n\
215  -k, --colors N Reduce the number of colors to N.\n\
216  --color-method METHOD Set method for choosing reduced colors.\n\
217  -f, --dither Dither image after changing colormap.\n");
218 #if HAVE_POW
219  printf("\
220  --gamma G Set gamma for color reduction [2.2].\n");
221 #endif
222  printf("\
223  --lossy[=LOSSINESS] Alter image colors to shrink output file size\n\
224  at the cost of artifacts and noise.\n\
225  --resize WxH Resize the output GIF to WxH.\n\
226  --resize-width W Resize to width W and proportional height.\n\
227  --resize-height H Resize to height H and proportional width.\n\
228  --resize-fit WxH Resize if necessary to fit within WxH.\n");
229  printf("\
230  --scale XFACTOR[xYFACTOR] Scale the output GIF by XFACTORxYFACTOR.\n\
231  --resize-method METHOD Set resizing method.\n\
232  --resize-colors N Resize can add new colors up to N.\n\
233  --transform-colormap CMD Transform each output colormap by shell CMD.\n\
234  --use-colormap CMAP Set output GIF's colormap to CMAP, which can\n\
235  be 'web', 'gray', 'bw', or a GIF file.\n\n");
236  printf("\
237 Report bugs to <ekohler@gmail.com>.\n\
238 Too much information? Try '%s --help | more'.\n", program_name);
239 #ifdef GIF_UNGIF
240  printf("\
241 This version of Gifsicle writes uncompressed GIFs, which can be far larger\n\
242 than compressed GIFs. See http://www.lcdf.org/gifsicle for more information.\n");
243 #endif
244 }
245 
246 
247 void
248 verbose_open(char open, const char *name)
249 {
250  int l = strlen(name);
251  if (verbose_pos && verbose_pos + 3 + l > 79) {
252  fputc('\n', stderr);
253  verbose_pos = 0;
254  }
255  if (verbose_pos) {
256  fputc(' ', stderr);
257  verbose_pos++;
258  }
259  fputc(open, stderr);
260  fputs(name, stderr);
261  verbose_pos += 1 + l;
262 }
263 
264 
265 void
266 verbose_close(char close)
267 {
268  fputc(close, stderr);
269  verbose_pos++;
270 }
271 
272 
273 void
275 {
276  if (verbose_pos) {
277  fputc('\n', stderr);
278  fflush(stderr);
279  verbose_pos = 0;
280  }
281 }
282 
283 
284 /*****
285  * Info functions
286  **/
287 
288 const char* debug_color_str(const Gif_Color* gfc) {
289  static int whichbuf = 0;
290  static char buf[4][8];
291  whichbuf = (whichbuf + 1) % 4;
292  sprintf(buf[whichbuf], "#%02X%02X%02X",
293  gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue);
294  return buf[whichbuf];
295 }
296 
297 static void
298 safe_puts(const char *s, uint32_t len, FILE *f)
299 {
300  const char *last_safe = s;
301  for (; len > 0; len--, s++)
302  if (*s < ' ' || *s >= 0x7F || *s == '\\') {
303  if (last_safe != s) {
304  size_t n = s - last_safe;
305  if (fwrite(last_safe, 1, n, f) != n)
306  return;
307  }
308  last_safe = s + 1;
309  switch (*s) {
310  case '\a': fputs("\\a", f); break;
311  case '\b': fputs("\\b", f); break;
312  case '\f': fputs("\\f", f); break;
313  case '\n': fputs("\\n", f); break;
314  case '\r': fputs("\\r", f); break;
315  case '\t': fputs("\\t", f); break;
316  case '\v': fputs("\\v", f); break;
317  case '\\': fputs("\\\\", f); break;
318  case 0: if (len > 1) fputs("\\000", f); break;
319  default: fprintf(f, "\\%03o", (unsigned char) *s); break;
320  }
321  }
322  if (last_safe != s) {
323  size_t n = s - last_safe;
324  if (fwrite(last_safe, 1, n, f) != n)
325  return;
326  }
327 }
328 
329 
330 static void
331 comment_info(FILE *where, Gif_Comment *gfcom, const char *prefix)
332 {
333  int i;
334  for (i = 0; i < gfcom->count; i++) {
335  fputs(prefix, where);
336  safe_puts(gfcom->str[i], gfcom->len[i], where);
337  fputc('\n', where);
338  }
339 }
340 
341 
342 #define COLORMAP_COLS 4
343 
344 static void
345 colormap_info(FILE *where, Gif_Colormap *gfcm, const char *prefix)
346 {
347  int i, j;
348  int nrows = ((gfcm->ncol - 1) / COLORMAP_COLS) + 1;
349 
350  for (j = 0; j < nrows; j++) {
351  int which = j;
352  fputs(prefix, where);
353  for (i = 0; i < COLORMAP_COLS && which < gfcm->ncol; i++, which += nrows) {
354  if (i) fputs(" ", where);
355  fprintf(where, " %3d: #%02X%02X%02X", which, gfcm->col[which].gfc_red,
356  gfcm->col[which].gfc_green, gfcm->col[which].gfc_blue);
357  }
358  fputc('\n', where);
359  }
360 }
361 
362 
363 static void
364 extension_info(FILE *where, Gif_Stream *gfs, Gif_Extension *gfex,
365  int count, int image_position)
366 {
367  uint8_t *data = gfex->data;
368  uint32_t pos = 0;
369  uint32_t len = gfex->length;
370 
371  fprintf(where, " extension %d: ", count);
372  if (gfex->kind == 255) {
373  fprintf(where, "app '");
374  safe_puts(gfex->appname, gfex->applength, where);
375  fprintf(where, "'");
376  } else {
377  if (gfex->kind >= 32 && gfex->kind < 127)
378  fprintf(where, "'%c' (0x%02X)", gfex->kind, gfex->kind);
379  else
380  fprintf(where, "0x%02X", gfex->kind);
381  }
382  if (image_position >= gfs->nimages)
383  fprintf(where, " at end");
384  else
385  fprintf(where, " before #%d", image_position);
386  if (gfex->packetized)
387  fprintf(where, " packetized");
388  fprintf(where, "\n");
389 
390  /* Now, hexl the data. */
391  while (len > 0) {
392  uint32_t row = 16;
393  uint32_t i;
394  if (row > len) row = len;
395  fprintf(where, " %08x: ", pos);
396 
397  for (i = 0; i < row; i += 2) {
398  if (i + 1 >= row)
399  fprintf(where, "%02x ", data[i]);
400  else
401  fprintf(where, "%02x%02x ", data[i], data[i+1]);
402  }
403  for (; i < 16; i += 2)
404  fputs(" ", where);
405 
406  putc(' ', where);
407  for (i = 0; i < row; i++, data++)
408  putc((*data >= ' ' && *data < 127 ? *data : '.'), where);
409  putc('\n', where);
410 
411  pos += row;
412  len -= row;
413  }
414 }
415 
416 
417 void
418 stream_info(FILE *where, Gif_Stream *gfs, const char *filename, int flags)
419 {
420  Gif_Extension *gfex;
421  int n, i;
422 
423  if (!gfs)
424  return;
425 
426  verbose_endline();
427  fprintf(where, "* %s %d image%s\n", (filename ? filename : "<stdin>"),
428  gfs->nimages, gfs->nimages == 1 ? "" : "s");
429  fprintf(where, " logical screen %dx%d\n",
430  gfs->screen_width, gfs->screen_height);
431 
432  if (gfs->global) {
433  fprintf(where, " global color table [%d]\n", gfs->global->ncol);
434  if (flags & INFO_COLORMAPS)
435  colormap_info(where, gfs->global, " |");
436  fprintf(where, " background %d\n", gfs->background);
437  }
438 
439  if (gfs->end_comment)
440  comment_info(where, gfs->end_comment, " end comment ");
441 
442  if (gfs->loopcount == 0)
443  fprintf(where, " loop forever\n");
444  else if (gfs->loopcount > 0)
445  fprintf(where, " loop count %u\n", (unsigned)gfs->loopcount);
446 
447  n = 0;
448  for (i = 0; i < gfs->nimages; ++i)
449  for (gfex = gfs->images[i]->extension_list; gfex; gfex = gfex->next, ++n)
450  if (flags & INFO_EXTENSIONS)
451  extension_info(where, gfs, gfex, n, i);
452  for (gfex = gfs->end_extension_list; gfex; gfex = gfex->next, ++n)
453  if (flags & INFO_EXTENSIONS)
454  extension_info(where, gfs, gfex, n, gfs->nimages);
455  if (n && !(flags & INFO_EXTENSIONS))
456  fprintf(where, " extensions %d\n", n);
457 }
458 
459 
460 static const char *disposal_names[] = {
461  "none", "asis", "background", "previous", "4", "5", "6", "7"
462 };
463 
464 void
465 image_info(FILE *where, Gif_Stream *gfs, Gif_Image *gfi, int flags)
466 {
467  int num;
468  if (!gfs || !gfi)
469  return;
470  num = Gif_ImageNumber(gfs, gfi);
471 
472  verbose_endline();
473  fprintf(where, " + image #%d ", num);
474  if (gfi->identifier)
475  fprintf(where, "#%s ", gfi->identifier);
476 
477  fprintf(where, "%dx%d", gfi->width, gfi->height);
478  if (gfi->left || gfi->top)
479  fprintf(where, " at %d,%d", gfi->left, gfi->top);
480 
481  if (gfi->interlace)
482  fprintf(where, " interlaced");
483 
484  if (gfi->transparent >= 0)
485  fprintf(where, " transparent %d", gfi->transparent);
486 
487  fprintf(where, "\n");
488 
489  if ((flags & INFO_SIZES) && gfi->compressed)
490  fprintf(where, " compressed size %u\n", gfi->compressed_len);
491 
492  if (gfi->comment)
493  comment_info(where, gfi->comment, " comment ");
494 
495  if (gfi->local) {
496  fprintf(where, " local color table [%d]\n", gfi->local->ncol);
497  if (flags & INFO_COLORMAPS)
498  colormap_info(where, gfi->local, " |");
499  }
500 
501  if (gfi->disposal || gfi->delay) {
502  fprintf(where, " ");
503  if (gfi->disposal)
504  fprintf(where, " disposal %s", disposal_names[gfi->disposal]);
505  if (gfi->delay)
506  fprintf(where, " delay %d.%02ds",
507  gfi->delay / 100, gfi->delay % 100);
508  fprintf(where, "\n");
509  }
510 }
511 
512 
513 char *
514 explode_filename(const char *filename, int number, const char *name, int max_nimages)
515 {
516  static char *s;
517  int l = strlen(filename);
518  l += name ? strlen(name) : 10;
519 
520  Gif_Delete(s);
521  s = Gif_NewArray(char, l + 3);
522  if (name)
523  sprintf(s, "%s.%s", filename, name);
524  else if (max_nimages <= 1000)
525  sprintf(s, "%s.%03d", filename, number);
526  else {
527  int digits;
528  unsigned j;
529  unsigned max = (max_nimages < 0 ? 0 : max_nimages);
530  for (digits = 4, j = 10000; max > j; digits++)
531  j *= 10;
532  sprintf(s, "%s.%0*d", filename, digits, number);
533  }
534 
535  return s;
536 }
537 
538 
539 /*****
540  * parsing functions
541  **/
542 
552 double parsed_scale_factor_x;
553 double parsed_scale_factor_y;
554 
555 int
556 parse_frame_spec(Clp_Parser *clp, const char *arg, int complain, void *thunk)
557 {
558  char *c;
559  (void)thunk;
560 
561  frame_spec_1 = 0;
562  frame_spec_2 = -1;
563  frame_spec_name = 0;
564 
565  if (!input && !input_name)
566  input_stream(0);
567  if (!input)
568  return 0;
569 
570  if (arg[0] != '#') {
571  if (complain)
572  return Clp_OptionError(clp, "frame specifications must start with #");
573  else
574  return 0;
575  }
576  arg++;
577  c = (char *)arg;
578 
579  /* Get a number range (#x, #x-y, or #x-). First, read x. */
580  if (isdigit(c[0]))
581  frame_spec_1 = frame_spec_2 = strtol(c, &c, 10);
582  else if (c[0] == '-' && isdigit(c[1]))
583  frame_spec_1 = frame_spec_2 = Gif_ImageCount(input) + strtol(c, &c, 10);
584 
585  /* Then, if the next character is a dash, read y. Be careful to prevent
586  #- from being interpreted as a frame range. */
587  if (c[0] == '-' && (frame_spec_2 >= 0 || c[1] != 0)) {
588  c++;
589  if (isdigit(c[0]))
590  frame_spec_2 = strtol(c, &c, 10);
591  else if (c[0] == '-' && isdigit(c[1]))
592  frame_spec_2 = Gif_ImageCount(input) + strtol(c, &c, 10);
593  else
595  }
596 
597  /* It really was a number range (and not a frame name)
598  only if c is now at the end of the argument. */
599  if (c[0] != 0) {
600  Gif_Image *gfi = Gif_GetNamedImage(input, arg);
601  if (gfi) {
602  frame_spec_name = (char *)arg;
604  return 1;
605  } else if (complain < 0) /* -1 is special value meaning 'don't complain
606  about frame NAMES, but do complain about
607  frame numbers.' */
608  return -97; /* Return -97 on bad frame name. */
609  else if (complain)
610  return Clp_OptionError(clp, "no frame named %<#%s%>", arg);
611  else
612  return 0;
613 
614  } else {
617  return 1;
618  else if (!complain)
619  return 0;
620  else
621  return Clp_OptionError(clp, "frame %<#%s%> out of range, image has %d frames", arg, Gif_ImageCount(input));
622  }
623 }
624 
625 int
626 parse_dimensions(Clp_Parser *clp, const char *arg, int complain, void *thunk)
627 {
628  char *val;
629  (void)thunk;
630 
631  if (*arg == '_' && arg[1] == 'x') {
632  dimensions_x = 0;
633  val = (char *)(arg + 1);
634  } else
635  dimensions_x = strtol(arg, &val, 10);
636  if (*val == 'x') {
637  if (val[1] == '_' && val[2] == 0) {
638  dimensions_y = 0;
639  val = val + 2;
640  } else
641  dimensions_y = strtol(val + 1, &val, 10);
642  if (*val == 0)
643  return 1;
644  }
645 
646  if (complain)
647  return Clp_OptionError(clp, "invalid dimensions %<%s%> (want WxH)", arg);
648  else
649  return 0;
650 }
651 
652 int
653 parse_position(Clp_Parser *clp, const char *arg, int complain, void *thunk)
654 {
655  char *val;
656  (void)thunk;
657 
658  position_x = strtol(arg, &val, 10);
659  if (*val == ',') {
660  position_y = strtol(val + 1, &val, 10);
661  if (*val == 0)
662  return 1;
663  }
664 
665  if (complain)
666  return Clp_OptionError(clp, "invalid position %<%s%> (want 'X,Y')", arg);
667  else
668  return 0;
669 }
670 
671 static double strtod_fraction(const char* arg, char** endptr) {
672  char* val, *val2;
673  double d = strtod(arg, &val);
674  if (arg != val && *val == '/') {
675  double denom = strtod(val + 1, &val2);
676  if (val2 != val + 1 && denom) {
677  d /= denom;
678  val = val2;
679  }
680  }
681  if (endptr)
682  *endptr = val;
683  return d;
684 }
685 
686 int
687 parse_scale_factor(Clp_Parser *clp, const char *arg, int complain, void *thunk)
688 {
689  char *val;
690  (void)thunk;
691 
693  if (*val == 'x') {
694  parsed_scale_factor_y = strtod_fraction(val + 1, &val);
695  if (*val == 0)
696  return 1;
697  } else if (*val == 0) {
699  return 1;
700  }
701 
702  if (complain)
703  return Clp_OptionError(clp, "invalid scale factor %<%s%> (want XxY)", arg);
704  else
705  return 0;
706 }
707 
708 int
709 parse_rectangle(Clp_Parser *clp, const char *arg, int complain, void *thunk)
710 {
711  const char *input_arg = arg;
712  char *val;
713  int x = position_x = strtol(arg, &val, 10);
714  (void)thunk;
715 
716  if (*val == ',') {
717  int y = position_y = strtol(val + 1, &val, 10);
718  if (*val == '-' && parse_position(clp, val + 1, 0, 0)) {
719  if (x >= 0 && y >= 0
720  && (position_x <= 0 || x < position_x)
721  && (position_y <= 0 || y < position_y)) {
722  /* 18.May.2008: Found it unintuitive that X,Y-0,0 acted like X,Y+-Xx-Y.
723  Therefore changed it so that X,Y-0,0 acts like X,Y+0,0, and similar
724  for negative dimensions. Probably safe to change this behavior
725  since only X,Y+0,0 was documented. */
726  dimensions_x = (position_x <= 0 ? -position_x : position_x - x);
727  dimensions_y = (position_y <= 0 ? -position_y : position_y - y);
728  position_x = x;
729  position_y = y;
730  return 1;
731  }
732  } else if (*val == '+' && parse_dimensions(clp, val + 1, 0, 0))
733  return 1;
734  } else if (*val == 'x') {
736  dimensions_y = strtol(val + 1, &val, 10);
737  if (*val == 0) {
738  position_x = position_y = 0;
739  return 1;
740  }
741  }
742 
743  if (complain)
744  return Clp_OptionError(clp, "invalid rectangle %<%s%> (want X1,Y1-X2,Y2 or X1,Y1+WxH", input_arg);
745  else
746  return 0;
747 }
748 
749 static int
750 xvalue(char c)
751 {
752  switch (c) {
753  case '0': case '1': case '2': case '3': case '4':
754  case '5': case '6': case '7': case '8': case '9':
755  return c - '0';
756  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
757  return c - 'A' + 10;
758  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
759  return c - 'a' + 10;
760  default:
761  return -1;
762  }
763 }
764 
765 static int
766 parse_hex_color_channel(const char *s, int ndigits)
767 {
768  int val1 = xvalue(s[0]);
769  if (val1 < 0) return -1;
770  if (ndigits == 1)
771  return val1 * 16 + val1;
772  else {
773  int val2 = xvalue(s[1]);
774  if (val2 < 0) return -1;
775  return val1 * 16 + val2;
776  }
777 }
778 
779 int
780 parse_color(Clp_Parser *clp, const char *arg, int complain, void *thunk)
781 {
782  const char *input_arg = arg;
783  char *str;
784  int red, green, blue;
785  (void)thunk;
786 
787  if (*arg == '#') {
788  int len = strlen(++arg);
789  if (len == 0 || len % 3 != 0
790  || (int)strspn(arg, "0123456789ABCDEFabcdef") != len) {
791  if (complain)
792  Clp_OptionError(clp, "invalid color %<%s%> (want #RGB or #RRGGBB)",
793  input_arg);
794  return 0;
795  }
796 
797  len /= 3;
798  red = parse_hex_color_channel(&arg[ 0 * len ], len);
799  green = parse_hex_color_channel(&arg[ 1 * len ], len);
800  blue = parse_hex_color_channel(&arg[ 2 * len ], len);
801  goto gotrgb;
802 
803  } else if (!isdigit(*arg))
804  goto error;
805 
806  red = strtol(arg, &str, 10);
807  if (*str == 0) {
808  if (red < 0 || red > 255)
809  goto error;
811  parsed_color.pixel = red;
812  return 1;
813 
814  } else if (*str != ',' && *str != '/')
815  goto error;
816 
817  if (*++str == 0) goto error;
818  green = strtol(str, &str, 10);
819  if (*str != ',' && *str != '/') goto error;
820 
821  if (*++str == 0) goto error;
822  blue = strtol(str, &str, 10);
823  if (*str != 0) goto error;
824 
825  gotrgb:
826  if (red < 0 || green < 0 || blue < 0
827  || red > 255 || green > 255 || blue > 255)
828  goto error;
829  parsed_color.gfc_red = red;
830  parsed_color.gfc_green = green;
831  parsed_color.gfc_blue = blue;
833  return 1;
834 
835  error:
836  if (complain)
837  return Clp_OptionError(clp, "invalid color %<%s%>", input_arg);
838  else
839  return 0;
840 }
841 
842 int
843 parse_two_colors(Clp_Parser *clp, const char *arg, int complain, void *thunk)
844 {
845  Gif_Color old_color;
846  if (parse_color(clp, arg, complain, thunk) <= 0)
847  return 0;
848  old_color = parsed_color;
849 
850  arg = Clp_Shift(clp, 0);
851  if (!arg && complain)
852  return Clp_OptionError(clp, "%<%O%> takes two color arguments");
853  else if (!arg)
854  return 0;
855 
856  if (parse_color(clp, arg, complain, thunk) <= 0)
857  return 0;
858 
860  parsed_color = old_color;
861  return 1;
862 }
863 
864 
865 /*****
866  * reading a file as a colormap
867  **/
868 
869 static Gif_Colormap *
870 read_text_colormap(FILE *f, const char *name)
871 {
872  char buf[BUFSIZ];
873  Gif_Colormap *cm = Gif_NewFullColormap(0, 256);
874  Gif_Color *col = cm->col;
875  int ncol = 0;
876  unsigned red, green, blue;
877  float fred, fgreen, fblue;
878 
879  while (fgets(buf, BUFSIZ, f)) {
880 
881  if (sscanf(buf, "%g %g %g", &fred, &fgreen, &fblue) == 3) {
882  if (fred < 0) fred = 0;
883  if (fgreen < 0) fgreen = 0;
884  if (fblue < 0) fblue = 0;
885  red = (unsigned)(fred + .5);
886  green = (unsigned)(fgreen + .5);
887  blue = (unsigned)(fblue + .5);
888  goto found;
889 
890  } else if (buf[0] == '#'
891  && strspn(buf + 1, "0123456789abcdefABCDEF") == 3
892  && (!buf[4] || isspace((unsigned char) buf[4]))) {
893  sscanf(buf + 1, "%1x%1x%1x", &red, &green, &blue);
894  red += red << 4;
895  green += green << 4;
896  blue += blue << 4;
897  goto found;
898 
899  } else if (buf[0] == '#'
900  && strspn(buf + 1, "0123456789abcdefABCDEF") == 6
901  && (!buf[7] || isspace((unsigned char) buf[7]))) {
902  sscanf(buf + 1, "%2x%2x%2x", &red, &green, &blue);
903  found:
904  if (red > 255) red = 255;
905  if (green > 255) green = 255;
906  if (blue > 255) blue = 255;
907  if (ncol >= 256) {
908  lerror(name, "maximum 256 colors allowed in colormap");
909  break;
910  } else {
911  col[ncol].gfc_red = red;
912  col[ncol].gfc_green = green;
913  col[ncol].gfc_blue = blue;
914  ncol++;
915  }
916  }
917 
918  /* handle too-long lines gracefully */
919  if (strchr(buf, '\n') == 0) {
920  int c;
921  for (c = getc(f); c != '\n' && c != EOF; c = getc(f))
922  ;
923  }
924  }
925 
926  if (ncol == 0) {
927  lerror(name, "file not in colormap format");
928  Gif_DeleteColormap(cm);
929  return 0;
930  } else {
931  cm->ncol = ncol;
932  return cm;
933  }
934 }
935 
936 static void
938  int is_error, const char *message) {
939  (void) gfs, (void) gfi, (void) is_error, (void) message;
940 }
941 
942 Gif_Colormap *
943 read_colormap_file(const char *name, FILE *f)
944 {
945  Gif_Colormap *cm = 0;
946  int c;
947  int my_file = 0;
948 
949  if (name && strcmp(name, "-") == 0)
950  name = 0;
951  if (!f) {
952  my_file = 1;
953  if (!name)
954  f = stdin;
955  else
956  f = fopen(name, "rb");
957  if (!f) {
958  lerror(name, "%s", name, strerror(errno));
959  return 0;
960  }
961  }
962 
963  name = name ? name : "<stdin>";
964  if (verbosing) verbose_open('<', name);
965 
966  c = getc(f);
967  ungetc(c, f);
968  if (c == 'G') {
970  if (!gfs)
971  lerror(name, "file not in GIF format");
972  else if (!gfs->global
973  && (gfs->nimages == 0 || !gfs->images[0]->local))
974  lerror(name, "can%,t use as palette (no global color table)");
975  else {
976  if (gfs->errors)
977  lwarning(name, "there were errors reading this GIF");
978  cm = Gif_CopyColormap(gfs->global ? gfs->global : gfs->images[0]->local);
979  }
980 
981  Gif_DeleteStream(gfs);
982  } else
983  cm = read_text_colormap(f, name);
984 
985  if (my_file) fclose(f);
986  if (verbosing) verbose_close('>');
987  return cm;
988 }
989 
990 
991 /*****
992  * Frame stuff
993  **/
994 
995 
996 Gt_Frameset *
997 new_frameset(int initial_cap)
998 {
1000  if (initial_cap < 0) initial_cap = 0;
1001  fs->cap = initial_cap;
1002  fs->count = 0;
1003  fs->f = Gif_NewArray(Gt_Frame, initial_cap);
1004  return fs;
1006 
1007 
1008 void
1010 {
1011  /* Get rid of next-frame-only options.
1012 
1013  This causes problems with frame selection. In the command 'gifsicle
1014  -nblah f.gif', the name should be applied to frame 0 of f.gif. This will
1015  happen automatically when f.gif is read, since all of its frames will be
1016  added when it is input. After frame 0, the name in def_frame will be
1017  cleared.
1018 
1019  Now, 'gifsicle -nblah f.gif #1' should apply the name to frame 1 of
1020  f.gif. But once f.gif is input, its frames are added, and the name
1021  component of def_frame is cleared!! So when #1 comes around it's gone!
1022 
1023  We handle this in gifsicle.c using the _change fields. */
1024 
1025  def_frame.name = 0;
1026  def_frame.comment = 0;
1027  def_frame.extensions = 0;
1029 
1030 
1031 Gt_Frame *
1032 add_frame(Gt_Frameset *fset, Gif_Stream *gfs, Gif_Image *gfi)
1033 {
1034  int number = fset->count++;
1035  while (number >= fset->cap) {
1036  fset->cap *= 2;
1037  Gif_ReArray(fset->f, Gt_Frame, fset->cap);
1038  }
1039 
1040  /* Mark the stream and the image both */
1041  gfs->refcount++;
1042  gfi->refcount++;
1043  fset->f[number] = def_frame;
1044  fset->f[number].stream = gfs;
1045  fset->f[number].image = gfi;
1046 
1048 
1049  return &fset->f[number];
1052 
1053 static Gt_Frame **merger = 0;
1054 static int nmerger = 0;
1055 static int mergercap = 0;
1056 
1057 static void
1058 merger_add(Gt_Frame *fp)
1059 {
1060  while (nmerger >= mergercap)
1061  if (mergercap) {
1062  mergercap *= 2;
1064  } else {
1065  mergercap = 16;
1067  }
1068  merger[ nmerger++ ] = fp;
1070 
1071 
1072 static void
1073 merger_flatten(Gt_Frameset *fset, int f1, int f2)
1074 {
1075  int i;
1076  assert(f1 >= 0 && f2 < fset->count);
1077  for (i = f1; i <= f2; i++) {
1078  Gt_Frameset *nest = FRAME(fset, i).nest;
1079 
1080  if (nest && nest->count > 0) {
1081  if (FRAME(fset, i).use < 0 && nest->count == 1) {
1082  /* use < 0 means use the frame's delay, disposal and name (if not
1083  explicitly overridden), but not the frame itself. */
1084  if (FRAME(nest, 0).delay < 0)
1085  FRAME(nest, 0).delay = FRAME(fset, i).image->delay;
1086  if (FRAME(nest, 0).disposal < 0)
1087  FRAME(nest, 0).disposal = FRAME(fset, i).image->disposal;
1088  if (FRAME(nest, 0).name == 0 && FRAME(nest, 0).no_name == 0)
1089  FRAME(nest, 0).name =
1090  Gif_CopyString(FRAME(fset, i).image->identifier);
1091  }
1092  merger_flatten(nest, 0, nest->count - 1);
1093  }
1094 
1095  if (FRAME(fset, i).use > 0)
1096  merger_add(&FRAME(fset, i));
1097  }
1099 
1100 
1101 static int
1103  const char *color_context)
1104 {
1105  Gif_Colormap *gfcm = gfs->global;
1106  int index;
1107  if (gfi && gfi->local) gfcm = gfi->local;
1108 
1109  if (color->haspixel == 2) { /* have pixel value, not color */
1110  if (color->pixel < (uint32_t)gfcm->ncol)
1111  return color->pixel;
1112  else {
1113  if (color_context)
1114  lwarning(gfs->landmark, "%s color out of range", color_context);
1115  return -1;
1116  }
1117  }
1118 
1119  index = Gif_FindColor(gfcm, color);
1120  if (index < 0 && color_context)
1121  lwarning(gfs->landmark, "%s color not in colormap", color_context);
1122  return index;
1123 }
1124 
1125 static void
1126 set_background(Gif_Stream *gfs, Gt_OutputData *output_data)
1127 {
1129  int i, j, conflict, want_transparent;
1130  Gif_Image *gfi;
1131 
1132  /* Check for user-specified background. */
1133  /* If they specified the number, silently cooperate. */
1134  if (output_data->background.haspixel == 2) {
1135  gfs->background = output_data->background.pixel;
1136  return;
1137  }
1138 
1139  /* Otherwise, if they specified a color, search for it. */
1140  if (output_data->background.haspixel) {
1141  if (gfs->images[0]->transparent >= 0) {
1142  static int context = 0;
1143  if (!context) {
1144  warning(1, "irrelevant background color\n"
1145  " (The background will appear transparent because"
1146  " the first image contains transparency.)");
1147  context = 1;
1148  } else
1149  warning(1, "irrelevant background color");
1150  }
1151  background = output_data->background;
1152  goto search;
1153  }
1154 
1155  /* If we get here, user doesn't care about background. */
1156  /* Search for required background colors. */
1157  conflict = want_transparent = background.haspixel = 0;
1158  for (i = j = 0; i < nmerger; i++) {
1159  if (merger[i]->total_crop) /* frame does not correspond to an image */
1160  continue;
1161  gfi = gfs->images[j];
1162  if (gfi->disposal == GIF_DISPOSAL_BACKGROUND
1163  || (j == 0 && (gfi->left != 0 || gfi->top != 0
1164  || gfi->width != gfs->screen_width
1165  || gfi->height != gfs->screen_height))) {
1166  /* transparent.haspixel set below, at merge_frame_done */
1167  int original_bg_transparent = (merger[i]->transparent.haspixel == 2);
1168  if ((original_bg_transparent && background.haspixel)
1169  || (!original_bg_transparent && want_transparent))
1170  conflict = 2;
1171  else if (original_bg_transparent)
1172  want_transparent = 1;
1173  else if (merger[i]->transparent.haspixel) {
1174  if (background.haspixel
1175  && !GIF_COLOREQ(&background, &merger[i]->transparent))
1176  conflict = 1;
1177  else {
1179  background.haspixel = 1;
1180  }
1181  }
1182  }
1183  j++;
1184  }
1185 
1186  /* Report conflicts. */
1187  if (conflict || (want_transparent && gfs->images[0]->transparent < 0)) {
1188  static int context = 0;
1189  if (!context) {
1190  warning(1, "input images have conflicting background colors\n"
1191  " (This means some animation frames may appear incorrect.)");
1192  context = 1;
1193  } else
1194  warning(1, "input images have conflicting background colors");
1195  }
1196 
1197  /* If no important background color, bag. */
1198  if (!background.haspixel) {
1199  gfs->background = 0;
1200  return;
1201  }
1202 
1203  search:
1204  i = find_color_or_error(&background, gfs, 0, "background");
1205  gfs->background = (i >= 0 ? i : 0);
1206 }
1208 
1209 
1210 static void
1211 fix_total_crop(Gif_Stream *dest, Gif_Image *srci, int merger_index)
1212 {
1213  /* Salvage any relevant information from a frame that's been completely
1214  cropped away. This ends up being comments and delay. */
1215  Gt_Frame *fr = merger[merger_index];
1216  Gt_Frame *next_fr = 0;
1217  Gif_Image *prev_image;
1218  assert(dest->nimages > 0);
1219  prev_image = dest->images[dest->nimages - 1];
1220  if (merger_index < nmerger - 1)
1221  next_fr = merger[merger_index + 1];
1222 
1223  /* Don't save identifiers since the frame that was to be identified, is
1224  gone. Save comments though. */
1225  if (!fr->no_comments && srci->comment && next_fr) {
1226  if (!next_fr->comment)
1227  next_fr->comment = Gif_NewComment();
1228  merge_comments(next_fr->comment, srci->comment);
1229  }
1230  if (fr->comment && next_fr) {
1231  if (!next_fr->comment)
1232  next_fr->comment = Gif_NewComment();
1233  merge_comments(next_fr->comment, fr->comment);
1235  fr->comment = 0;
1236  }
1237 
1238  /* Save delay by adding it to the previous frame's delay. */
1239  if (fr->delay < 0)
1240  fr->delay = srci->delay;
1241  prev_image->delay += fr->delay;
1242 
1243  /* Mark this image as totally cropped. */
1244  fr->total_crop = 1;
1246 
1247 
1248 static void
1249 handle_screen(Gif_Stream *dest, uint16_t width, uint16_t height)
1250 {
1251  /* Set the screen width & height, if the current input width and height are
1252  larger */
1253  if (dest->screen_width < width)
1254  dest->screen_width = width;
1255  if (dest->screen_height < height)
1256  dest->screen_height = height;
1257 }
1258 
1259 static void
1261 {
1262  Gif_Stream* gfs = fr->stream;
1263 
1264  desti->left += fr->left_offset;
1265  desti->top += fr->top_offset;
1266 
1267  if (fr->flip_horizontal)
1268  flip_image(desti, fr, 0);
1269  if (fr->flip_vertical)
1270  flip_image(desti, fr, 1);
1271 
1272  if (fr->rotation == 1)
1273  rotate_image(desti, fr, 1);
1274  else if (fr->rotation == 2) {
1275  flip_image(desti, fr, 0);
1276  flip_image(desti, fr, 1);
1277  } else if (fr->rotation == 3)
1278  rotate_image(desti, fr, 3);
1279 
1280  desti->left -= fr->left_offset;
1281  desti->top -= fr->top_offset;
1282 
1283  /* handle screen size, which might have height & width exchanged */
1284  if (fr->rotation == 1 || fr->rotation == 3)
1285  handle_screen(dest, gfs->screen_height, gfs->screen_width);
1286  else
1288 }
1289 
1290 static void
1291 analyze_crop(int nmerger, Gt_Crop* crop, int compress_immediately)
1292 {
1293  int i, nframes = 0;
1294  int l = 0x7FFFFFFF, r = 0, t = 0x7FFFFFFF, b = 0;
1295  Gif_Stream* cropped_gfs = 0;
1296 
1297  /* count frames to which this crop applies */
1298  for (i = 0; i < nmerger; i++)
1299  if (merger[i]->crop == crop) {
1300  cropped_gfs = merger[i]->stream;
1301  nframes++;
1302  }
1303 
1304  /* find border of frames */
1305  for (i = 0; i < nmerger; i++)
1306  if (merger[i]->crop == crop) {
1307  Gt_Frame *fr = merger[i];
1308  int ll, tt, rr, bb;
1309  if (!fr->position_is_offset) {
1310  ll = fr->image->left;
1311  tt = fr->image->top;
1312  rr = fr->image->left + fr->image->width;
1313  bb = fr->image->top + fr->image->height;
1314  } else {
1315  ll = tt = 0;
1316  rr = fr->stream->screen_width;
1317  bb = fr->stream->screen_height;
1318  }
1319  if (ll < l)
1320  l = ll;
1321  if (tt < t)
1322  t = tt;
1323  if (rr > r)
1324  r = rr;
1325  if (bb > b)
1326  b = bb;
1327  }
1328 
1329  if (t > b)
1330  /* total crop */
1331  l = r = t = b = 0;
1332 
1333  crop->x = crop->spec_x + l;
1334  crop->y = crop->spec_y + t;
1335  crop->w = crop->spec_w + (crop->spec_w <= 0 ? r - crop->x : 0);
1336  crop->h = crop->spec_h + (crop->spec_h <= 0 ? b - crop->y : 0);
1337  crop->left_offset = crop->x;
1338  crop->top_offset = crop->y;
1339  if (crop->x < 0 || crop->y < 0 || crop->w <= 0 || crop->h <= 0
1340  || crop->x + crop->w > r || crop->y + crop->h > b) {
1341  lerror(cropped_gfs ? cropped_gfs->landmark : (const char*) 0,
1342  "cropping dimensions don%,t fit image");
1343  crop->ready = 2;
1344  } else
1345  crop->ready = 1;
1346 
1347  /* Remove transparent edges. */
1348  if (crop->transparent_edges && crop->ready == 1) {
1349  int have_l = crop->x, have_t = crop->y,
1350  have_r = crop->x + crop->w, have_b = crop->y + crop->h;
1351  l = t = 0x7FFFFFFF, r = b = 0;
1352 
1353  for (i = 0;
1354  i < nmerger && (l > have_l || t > have_t || r < have_r || b < have_b);
1355  ++i)
1356  if (merger[i]->crop == crop) {
1357  Gt_Frame *fr = merger[i];
1358  Gif_Image *srci = fr->image;
1359  int ll = constrain(have_l, srci->left, have_r),
1360  tt = constrain(have_t, srci->top, have_b),
1361  rr = constrain(have_l, srci->left + srci->width, have_r),
1362  bb = constrain(have_t, srci->top + srci->height, have_b);
1363 
1364  if (srci->transparent >= 0) {
1365  int x, y;
1366  uint8_t **img;
1367  Gif_UncompressImage(fr->stream, srci);
1368  img = srci->img;
1369 
1370  /* Move top edge down over transparency */
1371  while (tt < bb && tt < t) {
1372  uint8_t *data = img[tt - srci->top];
1373  for (x = 0; x < srci->width; ++x)
1374  if (data[x] != srci->transparent)
1375  goto found_top;
1376  ++tt;
1377  }
1378 
1379  found_top:
1380  /* Move bottom edge up over transparency */
1381  while (bb > tt + 1 && bb > b) {
1382  uint8_t *data = img[bb - 1 - srci->top];
1383  for (x = 0; x < srci->width; ++x)
1384  if (data[x] != srci->transparent)
1385  goto found_bottom;
1386  --bb;
1387  }
1388 
1389  found_bottom:
1390  if (tt < bb) {
1391  /* Move left edge right over transparency */
1392  while (ll < rr && ll < l) {
1393  for (y = tt - srci->top; y < bb - srci->top; ++y)
1394  if (img[y][ll - srci->left] != srci->transparent)
1395  goto found_left;
1396  ++ll;
1397  }
1398 
1399  found_left:
1400  /* Move right edge left over transparency */
1401  while (rr > ll + 1 && rr > r) {
1402  for (y = tt - srci->top; y < bb - srci->top; ++y)
1403  if (img[y][rr - 1 - srci->left] != srci->transparent)
1404  goto found_right;
1405  --rr;
1406  }
1407  }
1408 
1409  found_right:
1410  if (compress_immediately > 0 && srci->compressed)
1412  }
1413 
1414  if (tt < bb) {
1415  if (ll < l)
1416  l = ll;
1417  if (tt < t)
1418  t = tt;
1419  if (rr > r)
1420  r = rr;
1421  if (bb > b)
1422  b = bb;
1423  }
1424  }
1425 
1426  if (t > b)
1427  crop->w = crop->h = 0;
1428  else {
1429  crop->x = l;
1430  crop->y = t;
1431  crop->w = r - l;
1432  crop->h = b - t;
1433  }
1434  }
1436 
1437 
1438 static inline int
1440 {
1441  int old_transparent = gfi->transparent;
1442  if (fr->transparent.haspixel == 255)
1443  gfi->transparent = -1;
1444  else if (fr->transparent.haspixel) {
1445  gfi->transparent =
1446  find_color_or_error(&fr->transparent, fr->stream, gfi,
1447  "transparent");
1448  if (gfi->transparent < 0)
1449  fr->transparent.haspixel = 0;
1450  }
1451  return old_transparent;
1452 }
1453 
1454 static void mark_used_background_color(Gt_Frame* fr) {
1455  Gif_Stream* gfs = fr->stream;
1456  Gif_Image* gfi = fr->image;
1457  if ((fr->transparent.haspixel != 0
1458  ? fr->transparent.haspixel != 255
1459  : gfi->transparent < 0)
1460  && ((fr->disposal >= 0
1461  ? fr->disposal
1463  || gfi->left != 0
1464  || gfi->top != 0
1465  || gfi->width != gfs->screen_width
1466  || gfi->height != gfs->screen_height)
1467  && gfs->global && gfs->background < gfs->global->ncol)
1468  gfs->global->col[gfs->background].haspixel |= 1;
1469 }
1470 
1471 Gif_Stream *
1472 merge_frame_interval(Gt_Frameset *fset, int f1, int f2,
1473  Gt_OutputData *output_data, int compress_immediately,
1474  int *huge_stream)
1475 {
1476  Gif_Stream *dest = Gif_NewStream();
1477  Gif_Colormap *global = Gif_NewFullColormap(256, 256);
1478  int i, same_compressed_ok, all_same_compressed_ok;
1479 
1480  global->ncol = 0;
1481  dest->global = global;
1482  if (output_data->active_output_name)
1483  dest->landmark = output_data->active_output_name;
1484  /* 11/23/98 A new stream's screen size is 0x0; we'll use the max of the
1485  merged-together streams' screen sizes by default (in merge_stream()) */
1486 
1487  if (f2 < 0)
1488  f2 = fset->count - 1;
1489  nmerger = 0;
1490  merger_flatten(fset, f1, f2);
1491  if (nmerger == 0) {
1492  error(1, "empty output GIF not written");
1493  return 0;
1494  }
1495 
1496  /* decide whether stream is huge */
1497  {
1498  size_t s;
1499  for (i = s = 0; i < nmerger; i++)
1500  s += (((unsigned) merger[i]->image->width
1501  * (unsigned) merger[i]->image->height) / 1024) + 1;
1502  *huge_stream = (s > 200 * 1024); /* 200 MB */
1503  if (*huge_stream && !compress_immediately) {
1504  warning(1, "huge GIF, conserving memory (processing may take a while)");
1505  compress_immediately = 1;
1506  }
1507  }
1508 
1509  /* merge stream-specific info and clear colormaps */
1510  for (i = 0; i < nmerger; i++)
1511  merger[i]->stream->user_flags = 1;
1512  for (i = 0; i < nmerger; i++) {
1513  if (merger[i]->stream->user_flags) {
1514  Gif_Stream *src = merger[i]->stream;
1515  Gif_CalculateScreenSize(src, 0);
1516  /* merge_stream() unmarks the global colormap */
1517  merge_stream(dest, src, merger[i]->no_comments);
1518  src->user_flags = 0;
1519  }
1520  if (merger[i]->image->local)
1521  unmark_colors_2(merger[i]->image->local);
1522  }
1523 
1524  /* is it ok to save the same compressed image? This is true only if we
1525  will recompress later from scratch. */
1526  /* 13.Aug.2002 - Can't save the same compressed image if we crop, so turn
1527  off all_same_compressed_ok below ('analyze crops'). Reported by Tom
1528  Schumm <phong@phong.org>. */
1529  if (output_data->colormap_size > 0
1530  || output_data->colormap_fixed
1531  || (output_data->optimizing & GT_OPT_MASK)
1532  || output_data->scaling > 0)
1533  all_same_compressed_ok = 1;
1534  else
1535  all_same_compressed_ok = 0;
1536 
1537  /* analyze crops */
1538  for (i = 0; i < nmerger; i++)
1539  if (merger[i]->crop)
1540  merger[i]->crop->ready = all_same_compressed_ok = 0;
1541  for (i = 0; i < nmerger; i++)
1542  if (merger[i]->crop && !merger[i]->crop->ready)
1543  analyze_crop(nmerger, merger[i]->crop, compress_immediately);
1544 
1545  /* mark used colors */
1546  for (i = 0; i < nmerger; ++i) {
1547  int old_transp = apply_frame_transparent(merger[i]->image, merger[i]);
1548  mark_used_colors(merger[i]->stream, merger[i]->image, merger[i]->crop,
1549  compress_immediately);
1550  merger[i]->image->transparent = old_transp;
1552  }
1553 
1554  /* copy stream-wide information from output_data */
1555  if (output_data->loopcount > -2)
1556  dest->loopcount = output_data->loopcount;
1557  dest->screen_width = dest->screen_height = 0;
1558 
1560  for (i = 0; i < nmerger; i++) {
1561  Gt_Frame *fr = merger[i];
1562  Gif_Image *srci;
1563  Gif_Image *desti;
1564  int old_transp;
1565 
1566  /* Make a copy of the image and crop it if we're cropping */
1567  fr->left_offset = fr->top_offset = 0;
1568  if (fr->crop) {
1569  int preserve_total_crop;
1570  srci = Gif_CopyImage(fr->image);
1571  Gif_UncompressImage(fr->stream, srci);
1572 
1573  /* Zero-delay frames are a special case. You might think it was OK to
1574  get rid of totally-cropped delay-0 frames, but many browsers treat
1575  zero-delay frames as 100ms. So don't completely crop a zero-delay
1576  frame: leave it around. Problem reported by Calle Kabo. */
1577  preserve_total_crop = (dest->nimages == 0 || fr->delay == 0
1578  || (fr->delay < 0 && srci->delay == 0));
1579 
1580  if (!crop_image(srci, fr, preserve_total_crop)) {
1581  /* We cropped the image out of existence! Be careful not to make 0x0
1582  frames. */
1583  fix_total_crop(dest, srci, i);
1584  goto merge_frame_done;
1585  }
1586  } else {
1587  srci = fr->image;
1588  Gif_UncompressImage(fr->stream, srci);
1589  }
1590 
1591  old_transp = apply_frame_transparent(srci, fr);
1592 
1593  /* Is it ok to use the old image's compressed version? */
1594  /* 11.Feb.2003 -- It is NOT ok to use the old image's compressed version
1595  if you are going to flip or rotate the image. Crash reported by Dan
1596  Lasley <Dan_Lasley@hilton.com>. */
1597  same_compressed_ok = all_same_compressed_ok;
1598  if ((fr->interlacing >= 0 && fr->interlacing != srci->interlace)
1599  || fr->flip_horizontal || fr->flip_vertical || fr->rotation)
1600  same_compressed_ok = 0;
1601 
1602  desti = merge_image(dest, fr->stream, srci, fr, same_compressed_ok);
1603 
1604  srci->transparent = old_transp; /* restore real transparent value */
1605 
1606  /* Flipping and rotating, and also setting the screen size */
1607  if (fr->flip_horizontal || fr->flip_vertical || fr->rotation)
1608  handle_flip_and_screen(dest, desti, fr);
1609  else
1611 
1612  /* Names and comments */
1613  if (fr->name || fr->no_name) {
1614  Gif_DeleteArray(desti->identifier);
1615  desti->identifier = Gif_CopyString(fr->name);
1616  }
1617  if (fr->no_comments && desti->comment) {
1618  Gif_DeleteComment(desti->comment);
1619  desti->comment = 0;
1620  }
1621  if (fr->comment) {
1622  if (!desti->comment) desti->comment = Gif_NewComment();
1623  merge_comments(desti->comment, fr->comment);
1624  /* delete the comment early to help with memory; set field to 0 so we
1625  don't re-free it later */
1627  fr->comment = 0;
1628  }
1629 
1630  if (fr->interlacing >= 0)
1631  desti->interlace = fr->interlacing;
1632  if (fr->left >= 0)
1633  desti->left = fr->left + (fr->position_is_offset ? desti->left : 0);
1634  if (fr->top >= 0)
1635  desti->top = fr->top + (fr->position_is_offset ? desti->top : 0);
1636 
1637  if (fr->delay >= 0)
1638  desti->delay = fr->delay;
1639  if (fr->disposal >= 0)
1640  desti->disposal = fr->disposal;
1641 
1642  /* compress immediately if possible to save on memory */
1643  if (desti->img) {
1644  if (compress_immediately > 0) {
1645  Gif_FullCompressImage(dest, desti, &gif_write_info);
1647  } else if (desti->compressed)
1649  } else if (compress_immediately <= 0) {
1650  Gif_UncompressImage(dest, desti);
1652  }
1653 
1654  merge_frame_done:
1655  /* 6/17/02 store information about image's background */
1656  if (fr->stream->images[0]->transparent >= 0)
1657  fr->transparent.haspixel = 2;
1658  else if (fr->stream->global
1659  && fr->stream->background < fr->stream->global->ncol) {
1660  fr->transparent = fr->stream->global->col[fr->stream->background];
1661  fr->transparent.haspixel = 1;
1662  } else
1663  fr->transparent.haspixel = 0;
1664 
1665  /* Destroy the copied, cropped image if necessary */
1666  if (fr->crop)
1667  Gif_DeleteImage(srci);
1668 
1669  /* if we can, delete the image's data right now to save memory */
1670  srci = fr->image;
1671  assert(srci->refcount > 1);
1672  if (--srci->refcount == 1) {
1673  /* only 1 reference ==> the reference is from the input stream itself */
1676  }
1677  fr->image = 0;
1678 
1679  /* 5/26/98 Destroy the stream now to help with memory. Assumes that
1680  all frames are added with add_frame() which properly increments the
1681  stream's refcount. Set field to 0 so we don't redelete */
1682  Gif_DeleteStream(fr->stream);
1683  fr->stream = 0;
1684  }
1687  /* Cropping the whole output? Reset logical screen */
1688  if (merger[0]->crop && merger[0]->crop == merger[nmerger - 1]->crop) {
1689  /* 13.May.2008: Set the logical screen to the cropped dimensions */
1690  /* 18.May.2008: Unless --crop-transparency is on */
1691  Gt_Crop* crop = merger[0]->crop;
1692  if (crop->transparent_edges)
1693  dest->screen_width = dest->screen_height = 0;
1694  else if (merger[0]->rotation == 1 || merger[0]->rotation == 3) {
1695  dest->screen_width = (crop->h > 0 ? crop->h : 0);
1696  dest->screen_height = (crop->w > 0 ? crop->w : 0);
1697  } else {
1698  dest->screen_width = (crop->w > 0 ? crop->w : 0);
1699  dest->screen_height = (crop->h > 0 ? crop->h : 0);
1700  }
1701  }
1702 
1703  /* Set the logical screen from the user's preferences */
1704  if (output_data->screen_width >= 0)
1705  dest->screen_width = output_data->screen_width;
1706  if (output_data->screen_height >= 0)
1707  dest->screen_height = output_data->screen_height;
1708  Gif_CalculateScreenSize(dest, 0);
1709 
1710  /* Find the background color in the colormap, or add it if we can */
1711  set_background(dest, output_data);
1712 
1713  /* The global colormap might be empty even given images that use it, if
1714  those images are entirely transparent. Since absent global colormaps
1715  are surprising, assign a black-and-white colormap */
1716  if (dest->global->ncol == 0) {
1717  for (i = 0; i != dest->nimages; ++i)
1718  if (!dest->images[i]->local) {
1719  GIF_SETCOLOR(&dest->global->col[0], 0, 0, 0);
1720  GIF_SETCOLOR(&dest->global->col[1], 255, 255, 255);
1721  dest->global->ncol = 2;
1722  break;
1723  }
1724  }
1725 
1726  return dest;
1728 
1729 
1730 void
1731 blank_frameset(Gt_Frameset *fset, int f1, int f2, int delete_object)
1732 {
1733  int i;
1734  if (delete_object) f1 = 0, f2 = -1;
1735  if (f2 < 0) f2 = fset->count - 1;
1736  for (i = f1; i <= f2; i++) {
1737  /* We may have deleted stream and image earlier to save on memory; see
1738  above in merge_frame_interval(); but if we didn't, do it now. */
1739  if (FRAME(fset, i).image && FRAME(fset, i).image->refcount > 1)
1740  FRAME(fset, i).image->refcount--;
1741  Gif_DeleteStream(FRAME(fset, i).stream);
1742  Gif_DeleteComment(FRAME(fset, i).comment);
1743  if (FRAME(fset, i).nest)
1744  blank_frameset(FRAME(fset, i).nest, 0, 0, 1);
1745  }
1746  if (delete_object) {
1747  Gif_DeleteArray(fset->f);
1748  Gif_Delete(fset);
1749  }
1751 
1752 
1753 void
1754 clear_frameset(Gt_Frameset *fset, int f1)
1755 {
1756  blank_frameset(fset, f1, -1, 0);
1757  fset->count = f1;
1758 }
Gt_OutputData::background
Gif_Color background
Definition: gifsicle.h:114
nested_mode
int nested_mode
Definition: gifsicle.c:54
Gt_Frame::extensions
Gif_Extension * extensions
Definition: gifsicle.h:59
nmerger
static int nmerger
Definition: support.c:1050
Gt_Frame::left
int left
Definition: gifsicle.h:44
Clp_OptionError
int Clp_OptionError(Clp_Parser *clp, const char *format,...)
Report a parser error.
Definition: clp.c:2261
Gif_NewStream
Gif_Stream * Gif_NewStream(void)
Definition: giffunc.c:22
Gif_Extension
Definition: gif.h:222
Gif_Stream::background
uint16_t background
Definition: gif.h:47
unmark_colors_2
void unmark_colors_2(Gif_Colormap *)
Definition: merge.c:31
strtod_fraction
static double strtod_fraction(const char *arg, char **endptr)
Definition: support.c:669
Gt_Frame::total_crop
unsigned total_crop
Definition: gifsicle.h:65
Gt_OutputData::screen_height
int screen_height
Definition: gifsicle.h:112
Gt_Crop::spec_y
int spec_y
Definition: gifsicle.h:84
GIF_COLOREQ
#define GIF_COLOREQ(c1, c2)
Definition: gif.h:195
stream_info
void stream_info(FILE *where, Gif_Stream *gfs, const char *filename, int flags)
Definition: support.c:417
Gt_Crop::x
int x
Definition: gifsicle.h:87
GIF_SETCOLOR
#define GIF_SETCOLOR(c, r, g, b)
Definition: gif.h:198
Gif_Extension::appname
char * appname
Definition: gif.h:224
parsed_scale_factor_x
double parsed_scale_factor_x
Definition: support.c:550
Gif_NewFullColormap
Gif_Colormap * Gif_NewFullColormap(int count, int capacity)
Definition: giffunc.c:90
Gt_Frame::image
Gif_Image * image
Definition: gifsicle.h:34
merger
static Gt_Frame ** merger
Definition: support.c:1049
parse_hex_color_channel
static int parse_hex_color_channel(const char *s, int ndigits)
Definition: support.c:764
disposal_names
static const char * disposal_names[]
Definition: support.c:459
Gt_Frame::top
int top
Definition: gifsicle.h:45
Gif_Image::local
Gif_Colormap * local
Definition: gif.h:97
extension_info
static void extension_info(FILE *where, Gif_Stream *gfs, Gif_Extension *gfex, int count, int image_position)
Definition: support.c:363
find_color_or_error
static int find_color_or_error(Gif_Color *color, Gif_Stream *gfs, Gif_Image *gfi, const char *color_context)
Definition: support.c:1098
Gif_Color::gfc_red
uint8_t gfc_red
Definition: gif.h:173
position_x
int position_x
Definition: support.c:546
Gt_Frameset::count
int count
Definition: gifsicle.h:74
Gif_Colormap
Definition: gif.h:180
Gt_OutputData::loopcount
int loopcount
Definition: gifsicle.h:115
handle_flip_and_screen
static void handle_flip_and_screen(Gif_Stream *dest, Gif_Image *desti, Gt_Frame *fr)
Definition: support.c:1256
Gif_Stream::loopcount
long loopcount
Definition: gif.h:51
Gif_Image::top
uint16_t top
Definition: gif.h:91
mode
int mode
Definition: gifsicle.c:53
Clp_Shift
const char * Clp_Shift(Clp_Parser *clp, int allow_options)
Return the next argument from clp without option parsing.
Definition: clp.c:1983
Gt_Frame::rotation
unsigned rotation
Definition: gifsicle.h:66
Gif_Comment
Definition: gif.h:207
parsed_scale_factor_y
double parsed_scale_factor_y
Definition: support.c:551
dimensions_y
int dimensions_y
Definition: support.c:545
Gif_Color
Definition: gif.h:171
max
#define max(a, b)
Definition: kcolor.h:31
Gif_Extension::length
uint32_t length
Definition: gif.h:227
Gt_OutputData::scaling
int scaling
Definition: gifsicle.h:129
position_y
int position_y
Definition: support.c:547
parse_rectangle
int parse_rectangle(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:707
Gif_CopyImage
Gif_Image * Gif_CopyImage(Gif_Image *gfi)
Definition: giffunc.c:390
rotate_image
void rotate_image(Gif_Image *gfi, Gt_Frame *fr, int rotation)
Definition: xform.c:318
Gt_Frameset::f
Gt_Frame * f
Definition: gifsicle.h:76
Gif_ImageCount
#define Gif_ImageCount(gfs)
Definition: gif.h:71
Gif_Stream::landmark
const char * landmark
Definition: gif.h:59
Gt_Crop::spec_h
int spec_h
Definition: gifsicle.h:86
MERGING
#define MERGING
Definition: gifsicle.h:322
merge_frame_interval
Gif_Stream * merge_frame_interval(Gt_Frameset *fset, int f1, int f2, Gt_OutputData *output_data, int compress_immediately, int *huge_stream)
Definition: support.c:1468
lerror
void lerror(const char *landmark, const char *format,...)
Definition: support.c:87
verror
static void verror(const char *landmark, int need_file, int seriousness, const char *fmt, va_list val)
Definition: support.c:26
image_info
void image_info(FILE *where, Gif_Stream *gfs, Gif_Image *gfi, int flags)
Definition: support.c:464
no_gifread_error
static void no_gifread_error(Gif_Stream *gfs, Gif_Image *gfi, int is_error, const char *message)
Definition: support.c:934
no_warnings
int no_warnings
Definition: support.c:22
clear_frameset
void clear_frameset(Gt_Frameset *fset, int f1)
Definition: support.c:1750
Gt_OutputData::colormap_fixed
Gif_Colormap * colormap_fixed
Definition: gifsicle.h:118
Gif_ReleaseCompressedImage
void Gif_ReleaseCompressedImage(Gif_Image *gfi)
Definition: giffunc.c:702
INFO_SIZES
#define INFO_SIZES
Definition: gifsicle.h:208
verbosing
int verbosing
Definition: gifsicle.c:57
Gif_Comment::str
char ** str
Definition: gif.h:208
Gif_Comment::len
int * len
Definition: gif.h:209
Gt_Frame::no_comments
int no_comments
Definition: gifsicle.h:40
input_stream
void input_stream(const char *name)
Definition: gifsicle.c:678
parsed_color
Gif_Color parsed_color
Definition: support.c:548
BLANK_MODE
#define BLANK_MODE
Definition: gifsicle.h:321
Gif_Stream::end_extension_list
Gif_Extension * end_extension_list
Definition: gif.h:54
mark_used_colors
void mark_used_colors(Gif_Stream *gfs, Gif_Image *gfi, Gt_Crop *crop, int compress_immediately)
Definition: merge.c:42
input
Gif_Stream * input
Definition: gifsicle.c:34
parsed_color2
Gif_Color parsed_color2
Definition: support.c:549
Gif_DeleteImage
void Gif_DeleteImage(Gif_Image *gfi)
Definition: giffunc.c:519
input_name
const char * input_name
Definition: gifsicle.c:35
Gt_Frame::stream
Gif_Stream * stream
Definition: gifsicle.h:33
Clp_Parser
Command line parser.
Definition: clp.h:234
Gif_Image::width
uint16_t width
Definition: gif.h:88
Gt_OutputData::screen_width
int screen_width
Definition: gifsicle.h:111
FRAME
#define FRAME(fs, i)
Definition: gifsicle.h:308
parse_position
int parse_position(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:651
constrain
static int constrain(int low, int x, int high)
Definition: gifsicle.h:170
Gt_Frame::crop
Gt_Crop * crop
Definition: gifsicle.h:47
def_frame
Gt_Frame def_frame
Definition: gifsicle.c:28
Gt_OutputData::active_output_name
const char * active_output_name
Definition: gifsicle.h:109
Gt_Crop::spec_w
int spec_w
Definition: gifsicle.h:85
set_background
static void set_background(Gif_Stream *gfs, Gt_OutputData *output_data)
Definition: support.c:1122
Gif_Image::compressed
uint8_t * compressed
Definition: gif.h:107
Gt_Frameset::cap
int cap
Definition: gifsicle.h:75
Gif_Extension::kind
int kind
Definition: gif.h:223
analyze_crop
static void analyze_crop(int nmerger, Gt_Crop *crop, int compress_immediately)
Definition: support.c:1287
Gif_ReleaseUncompressedImage
void Gif_ReleaseUncompressedImage(Gif_Image *gfi)
Definition: giffunc.c:713
active_output_data
Gt_OutputData active_output_data
Definition: gifsicle.c:99
parse_two_colors
int parse_two_colors(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:841
Gt_OutputData::optimizing
int optimizing
Definition: gifsicle.h:127
Gt_Crop::transparent_edges
int transparent_edges
Definition: gifsicle.h:82
handle_screen
static void handle_screen(Gif_Stream *dest, uint16_t width, uint16_t height)
Definition: support.c:1245
crop_image
int crop_image(Gif_Image *gfi, Gt_Frame *fr, int preserve_total_crop)
Definition: xform.c:244
Gif_Image::compressed_len
uint32_t compressed_len
Definition: gif.h:105
clp_error_handler
void clp_error_handler(Clp_Parser *clp, const char *message)
Definition: support.c:116
error
void error(int need_file, const char *format,...)
Definition: support.c:94
Gif_Color::gfc_green
uint8_t gfc_green
Definition: gif.h:174
Gt_Frame::flip_vertical
unsigned flip_vertical
Definition: gifsicle.h:62
debug_color_str
const char * debug_color_str(const Gif_Color *gfc)
Definition: support.c:287
Gif_DeleteColormap
void Gif_DeleteColormap(Gif_Colormap *)
Definition: giffunc.c:546
merger_flatten
static void merger_flatten(Gt_Frameset *fset, int f1, int f2)
Definition: support.c:1069
Gt_OutputData::colormap_size
int colormap_size
Definition: gifsicle.h:117
parse_color
int parse_color(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:778
parse_scale_factor
int parse_scale_factor(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:685
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_Extension::applength
int applength
Definition: gif.h:225
Gif_Color::gfc_blue
uint8_t gfc_blue
Definition: gif.h:175
Gif_Image::left
uint16_t left
Definition: gif.h:90
strerror
char * strerror(int errno)
Definition: strerror.c:13
Gif_Image::comment
Gif_Comment * comment
Definition: gif.h:100
Gif_Delete
#define Gif_Delete(p)
Definition: gif.h:313
Gt_Frame::name
const char * name
Definition: gifsicle.h:37
clp
static Clp_Parser * clp
Definition: gifdiff.c:58
Gif_NewArray
#define Gif_NewArray(t, n)
Definition: gif.h:311
Gt_Frame::top_offset
int top_offset
Definition: gifsicle.h:49
Gif_Stream::errors
unsigned errors
Definition: gif.h:56
xvalue
static int xvalue(char c)
Definition: support.c:748
Gif_Stream::nimages
int nimages
Definition: gif.h:43
error_count
int error_count
Definition: support.c:21
merge_comments
void merge_comments(Gif_Comment *destc, Gif_Comment *srcc)
Definition: merge.c:231
Gt_Frame::transparent
Gif_Color transparent
Definition: gifsicle.h:42
Gif_Stream::refcount
int refcount
Definition: gif.h:60
merge_stream
void merge_stream(Gif_Stream *dest, Gif_Stream *src, int no_comments)
Definition: merge.c:207
verbose_open
void verbose_open(char open, const char *name)
Definition: support.c:248
new_frameset
Gt_Frameset * new_frameset(int initial_cap)
Definition: support.c:993
mergercap
static int mergercap
Definition: support.c:1051
Gt_Crop::ready
int ready
Definition: gifsicle.h:81
GIF_READ_COMPRESSED
#define GIF_READ_COMPRESSED
Definition: gif.h:251
dimensions_x
int dimensions_x
Definition: support.c:544
add_frame
Gt_Frame * add_frame(Gt_Frameset *fset, Gif_Stream *gfs, Gif_Image *gfi)
Definition: support.c:1028
Gif_Extension::next
Gif_Extension * next
Definition: gif.h:232
Gt_Crop::w
int w
Definition: gifsicle.h:89
verbose_close
void verbose_close(char close)
Definition: support.c:266
blank_frameset
void blank_frameset(Gt_Frameset *fset, int f1, int f2, int delete_object)
Definition: support.c:1727
Gif_Stream::screen_height
uint16_t screen_height
Definition: gif.h:50
lwarning
void lwarning(const char *landmark, const char *format,...)
Definition: support.c:101
merge_image
Gif_Image * merge_image(Gif_Stream *dest, Gif_Stream *src, Gif_Image *srci, Gt_Frame *srcfr, int same_compressed_ok)
Definition: merge.c:256
Gif_FullReadFile
Gif_Stream * Gif_FullReadFile(FILE *f, int flags, const char *landmark, Gif_ReadErrorHandler handler)
Definition: gifread.c:920
Gt_Frame::disposal
int disposal
Definition: gifsicle.h:52
Gif_Image::refcount
int refcount
Definition: gif.h:113
Gt_Frame::no_name
int no_name
Definition: gifsicle.h:38
verbose_endline
void verbose_endline(void)
Definition: support.c:274
Gif_Extension::packetized
int packetized
Definition: gif.h:228
INFO_EXTENSIONS
#define INFO_EXTENSIONS
Definition: gifsicle.h:207
Gif_CopyColormap
Gif_Colormap * Gif_CopyColormap(Gif_Colormap *)
Definition: giffunc.c:374
comment_info
static void comment_info(FILE *where, Gif_Comment *gfcom, const char *prefix)
Definition: support.c:330
gifsicle.h
Gif_Stream::screen_width
uint16_t screen_width
Definition: gif.h:49
Gif_Image::disposal
uint8_t disposal
Definition: gif.h:93
Gt_Frame::left_offset
int left_offset
Definition: gifsicle.h:48
Gif_UncompressImage
#define Gif_UncompressImage(gfs, gfi)
Definition: gif.h:152
short_usage
void short_usage(void)
Definition: support.c:125
Gif_Image
Definition: gif.h:84
Gt_Frameset
Definition: gifsicle.h:73
Gt_Frame::flip_horizontal
unsigned flip_horizontal
Definition: gifsicle.h:61
frame_spec_2
int frame_spec_2
Definition: support.c:542
frame_spec_1
int frame_spec_1
Definition: support.c:541
Gt_Crop::spec_x
int spec_x
Definition: gifsicle.h:83
Gif_Stream::user_flags
uint32_t user_flags
Definition: gif.h:57
apply_frame_transparent
static int apply_frame_transparent(Gif_Image *gfi, Gt_Frame *fr)
Definition: support.c:1435
Gif_New
#define Gif_New(t)
Definition: gif.h:310
Gif_Stream::images
Gif_Image ** images
Definition: gif.h:42
Gif_Stream
Definition: gif.h:41
Gif_ReArray
#define Gif_ReArray(p, t, n)
Definition: gif.h:312
frame_spec_name
char * frame_spec_name
Definition: support.c:543
Gt_Frame::interlacing
int interlacing
Definition: gifsicle.h:43
Gif_NewComment
Gif_Comment * Gif_NewComment(void)
Definition: giffunc.c:113
Gif_Image::identifier
char * identifier
Definition: gif.h:99
program_name
const char * program_name
Definition: support.c:19
Gif_CalculateScreenSize
void Gif_CalculateScreenSize(Gif_Stream *, int force)
Definition: giffunc.c:306
read_text_colormap
static Gif_Colormap * read_text_colormap(FILE *f, const char *name)
Definition: support.c:867
fatal_error
void fatal_error(const char *format,...)
Definition: support.c:79
Gif_Color::pixel
uint32_t pixel
Definition: gif.h:176
Gif_DeleteComment
void Gif_DeleteComment(Gif_Comment *)
Definition: giffunc.c:562
merger_add
static void merger_add(Gt_Frame *fp)
Definition: support.c:1054
usage
void usage(void)
Definition: support.c:134
colormap_info
static void colormap_info(FILE *where, Gif_Colormap *gfcm, const char *prefix)
Definition: support.c:344
COLORMAP_COLS
#define COLORMAP_COLS
Definition: support.c:341
Gif_DeleteStream
void Gif_DeleteStream(Gif_Stream *)
Definition: giffunc.c:494
verbose_pos
static int verbose_pos
Definition: support.c:20
gif_write_info
Gif_CompressInfo gif_write_info
Definition: gifsicle.c:43
Gif_CopyString
char * Gif_CopyString(const char *)
Definition: giffunc.c:179
safe_puts
static void safe_puts(const char *s, uint32_t len, FILE *f)
Definition: support.c:297
background
static unsigned background
Definition: optimize.c:58
Gif_Color::haspixel
uint8_t haspixel
Definition: gif.h:172
Gif_Image::delay
uint16_t delay
Definition: gif.h:92
Gif_FindColor
int Gif_FindColor(Gif_Colormap *, Gif_Color *)
Definition: giffunc.c:643
Gif_Image::extension_list
Gif_Extension * extension_list
Definition: gif.h:101
Gif_Image::img
uint8_t ** img
Definition: gif.h:85
EXIT_USER_ERR
#define EXIT_USER_ERR
Definition: gifsicle.h:202
Gt_Frame::comment
Gif_Comment * comment
Definition: gifsicle.h:39
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
Gif_Comment::count
int count
Definition: gif.h:210
Gt_Crop::h
int h
Definition: gifsicle.h:90
INFO_COLORMAPS
#define INFO_COLORMAPS
Definition: gifsicle.h:206
Gif_DeleteArray
#define Gif_DeleteArray(p)
Definition: gif.h:314
parse_dimensions
int parse_dimensions(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:624
Gif_Image::interlace
uint8_t interlace
Definition: gif.h:94
Gt_Crop::left_offset
int left_offset
Definition: gifsicle.h:91
Gt_Crop
Definition: gifsicle.h:80
mark_used_background_color
static void mark_used_background_color(Gt_Frame *fr)
Definition: support.c:1450
warning
void warning(int need_file, const char *format,...)
Definition: support.c:108
Gif_Colormap::col
Gif_Color * col
Definition: gif.h:185
GIF_DISPOSAL_BACKGROUND
#define GIF_DISPOSAL_BACKGROUND
Definition: gif.h:119
Gt_Frame
Definition: gifsicle.h:31
GT_OPT_MASK
#define GT_OPT_MASK
Definition: gifsicle.h:162
Gt_Crop::top_offset
int top_offset
Definition: gifsicle.h:92
explode_filename
char * explode_filename(const char *filename, int number, const char *name, int max_nimages)
Definition: support.c:513
Gif_Extension::data
uint8_t * data
Definition: gif.h:226
Gif_Stream::end_comment
Gif_Comment * end_comment
Definition: gif.h:53
read_colormap_file
Gif_Colormap * read_colormap_file(const char *name, FILE *f)
Definition: support.c:940
flip_image
void flip_image(Gif_Image *gfi, Gt_Frame *fr, int is_vert)
Definition: xform.c:281
Gt_Crop::y
int y
Definition: gifsicle.h:88
Gif_Stream::global
Gif_Colormap * global
Definition: gif.h:46
clear_def_frame_once_options
void clear_def_frame_once_options(void)
Definition: support.c:1005
Gif_Colormap::ncol
int ncol
Definition: gif.h:181
Gt_OutputData
Definition: gifsicle.h:106
Gif_GetNamedImage
Gif_Image * Gif_GetNamedImage(Gif_Stream *gfs, const char *name)
Definition: giffunc.c:685
fix_total_crop
static void fix_total_crop(Gif_Stream *dest, Gif_Image *srci, int merger_index)
Definition: support.c:1207
Gt_Frame::position_is_offset
unsigned position_is_offset
Definition: gifsicle.h:64
parse_frame_spec
int parse_frame_spec(Clp_Parser *clp, const char *arg, int complain, void *thunk)
Definition: support.c:554
Gt_Frame::delay
int delay
Definition: gifsicle.h:51
Gif_Image::height
uint16_t height
Definition: gif.h:89
Gif_FullCompressImage
int Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, const Gif_CompressInfo *gcinfo)
Definition: gifwrite.c:752