Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

support.c

Go to the documentation of this file.
00001 /* support.c - Support functions for gifsicle.
00002    Copyright (C) 1997-2001 Eddie Kohler, eddietwo@lcs.mit.edu
00003    This file is part of gifsicle.
00004 
00005    Gifsicle is free software. It is distributed under the GNU Public License,
00006    version 2 or later; you can copy, distribute, or alter it at will, as long
00007    as this notice is kept intact and this source code is made available. There
00008    is no warranty, express or implied. */
00009 
00010 #include "config.h"
00011 #include "gifsicle.h"
00012 #include <stdio.h>
00013 #include <stdarg.h>
00014 #include <string.h>
00015 #include <ctype.h>
00016 #include <assert.h>
00017 #include <errno.h>
00018 
00019 const char *program_name = "gifsicle";
00020 static int verbose_pos = 0;
00021 int error_count = 0;
00022 int no_warnings = 0;
00023 
00024 
00025 static void
00026 verror(int seriousness, char *message, va_list val)
00027 {
00028   char pattern[BUFSIZ];
00029   char buffer[BUFSIZ];
00030   verbose_endline();
00031   
00032   if (seriousness > 2)
00033     sprintf(pattern, "%s: fatal error: %%s\n", program_name);
00034   else if (seriousness == 1)
00035     sprintf(pattern, "%s: warning: %%s\n", program_name);
00036   else
00037     sprintf(pattern, "%s: %%s\n", program_name);
00038   
00039   if (seriousness > 1)
00040     error_count++;
00041   else if (no_warnings)
00042     return;
00043   
00044   /* try and keep error messages together (no interleaving of error messages
00045      from two gifsicle processes in the same command line) by calling fprintf
00046      only once */
00047   if (strlen(message) + strlen(pattern) < BUFSIZ) {
00048     sprintf(buffer, pattern, message);
00049     vfprintf(stderr, buffer, val);
00050   } else {
00051     fwrite(pattern, 1, strlen(pattern) - 3, stderr);
00052     vfprintf(stderr, message, val);
00053     putc('\n', stderr);
00054   }
00055 }
00056 
00057 void
00058 fatal_error(char *message, ...)
00059 {
00060   va_list val;
00061   va_start(val, message);
00062   verror(3, message, val);
00063   va_end(val);
00064   exit(EXIT_USER_ERR);
00065 }
00066 
00067 void
00068 error(char *message, ...)
00069 {
00070   va_list val;
00071   va_start(val, message);
00072   verror(2, message, val);
00073   va_end(val);
00074 }
00075 
00076 void
00077 warning(char *message, ...)
00078 {
00079   va_list val;
00080   va_start(val, message);
00081   verror(1, message, val);
00082   va_end(val);
00083 }
00084 
00085 void
00086 warncontext(char *message, ...)
00087 {
00088   va_list val;
00089   va_start(val, message);
00090   verror(0, message, val);
00091   va_end(val);
00092 }
00093 
00094 void
00095 clp_error_handler(char *message)
00096 {
00097   verbose_endline();
00098   fputs(message, stderr);
00099 }
00100 
00101 
00102 void
00103 short_usage(void)
00104 {
00105   fprintf(stderr, "Usage: %s [OPTION | FILE | FRAME]...\n\
00106 Try `%s --help' for more information.\n",
00107           program_name, program_name);
00108 }
00109 
00110 
00111 void
00112 usage(void)
00113 {
00114   printf("\
00115 `Gifsicle' manipulates GIF images. Its most common uses include combining\n\
00116 single images into animations, adding transparency, optimizing animations for\n\
00117 space, and printing information about GIFs.\n\
00118 \n\
00119 Usage: %s [OPTION | FILE | FRAME]...\n\
00120 \n\
00121 Mode options: at most one, before any filenames.\n\
00122   -m, --merge                   Merge mode: combine inputs, write stdout.\n\
00123   -b, --batch                   Batch mode: modify inputs, write back to\n\
00124                                 same filenames.\n\
00125   -e, --explode                 Explode mode: write N files for each input,\n\
00126                                 one per frame, to `input.frame-number'.\n\
00127   -E, --explode-by-name         Explode mode, but write `input.name'.\n\
00128 \n\
00129 General options: Also --no-OPTION for info and verbose.\n\
00130   -I, --info                    Print info about input GIFs. Two -I's means\n\
00131                                 normal output is not suppressed.\n\
00132       --color-info, --cinfo     --info plus colormap details.\n\
00133       --extension-info, --xinfo --info plus extension details.\n\
00134   -v, --verbose                 Prints progress information.\n\
00135   -h, --help                    Print this message and exit.\n\
00136       --version                 Print version number and exit.\n\
00137   -o, --output FILE             Write output to FILE.\n\
00138   -w, --no-warnings             Don't report warnings.\n\
00139 \n", program_name);
00140   printf("\
00141 Frame selections:               #num, #num1-num2, #num1-, #name\n\
00142 \n\
00143 Frame change options:\n\
00144   --delete FRAMES               Delete FRAMES from input.\n\
00145   --insert-before FRAME GIFS    Insert GIFS before FRAMES in input.\n\
00146   --append GIFS                 Append GIFS to input.\n\
00147   --replace FRAMES GIFS         Replace FRAMES with GIFS in input.\n\
00148   --done                        Done with frame changes.\n\
00149 \n\
00150 Image options: Also --no-OPTION and --same-OPTION.\n\
00151   -B, --background COL          Makes COL the background color.\n\
00152       --crop X,Y+WxH, --crop X,Y-X2,Y2\n\
00153                                 Crops the image.\n\
00154       --flip-horizontal, --flip-vertical\n\
00155                                 Flips the image.\n\
00156   -i, --interlace               Turns on interlacing.\n\
00157   -S, --logical-screen WxH      Sets logical screen to WxH.\n\
00158   -p, --position X,Y            Sets frame position to (X,Y).\n\
00159       --rotate-90, --rotate-180, --rotate-270, --no-rotate\n\
00160                                 Rotates the image.\n\
00161   -t, --transparent COL         Makes COL transparent.\n\
00162 \n");
00163   printf("\
00164 Extension options: Also --no-OPTION and --same-OPTION.\n\
00165   -x, --app-extension N D       Adds an app extension named N with data D.\n\
00166   -c, --comment TEXT            Adds a comment before the next frame.\n\
00167       --extension N D           Adds an extension number N with data D.\n\
00168   -n, --name TEXT               Sets next frame's name.\n\
00169 \n\
00170 Animation options: Also --no-OPTION and --same-OPTION.\n\
00171   -d, --delay TIME              Sets frame delay to TIME (in 1/100sec).\n\
00172   -D, --disposal METHOD         Sets frame disposal to METHOD.\n\
00173   -l, --loopcount[=N]           Sets loop extension to N (default forever).\n\
00174   -O, --optimize[=LEV]          Optimize output GIFs.\n\
00175   -U, --unoptimize              Unoptimize input GIFs.\n\
00176 \n");
00177   printf("\
00178 Whole-GIF options: Also --no-OPTION.\n\
00179       --careful                 Write larger GIFs that avoid bugs in other\n\
00180                                 programs.\n\
00181       --change-color COL1 COL2  Changes COL1 to COL2 throughout.\n\
00182   -k, --colors N                Reduces the number of colors to N.\n\
00183       --color-method METHOD     Set method for choosing reduced colors.\n\
00184   -f, --dither                  Dither image after changing colormap.\n\
00185       --resize WxH              Resizes the output GIF to WxH.\n\
00186       --resize-width W          Resizes to width W and proportional height.\n\
00187       --resize-height H         Resizes to height H and proportional width.\n\
00188       --scale XFACTOR[xYFACTOR] Scales the output GIF by XFACTORxYFACTOR.\n\
00189       --transform-colormap CMD  Transform each output colormap by shell CMD.\n\
00190       --use-colormap CMAP       Set output GIF's colormap to CMAP, which can\n\
00191                                 be `web', `gray', `bw', or a GIF file.\n\
00192 \n\
00193 Report bugs to <eddietwo@lcs.mit.edu>.\n\
00194 Too much information? Try `%s --help | more'.\n", program_name);
00195 #ifdef GIF_UNGIF
00196   printf("\
00197 This version of Gifsicle writes uncompressed GIFs, which can be far larger\n\
00198 than compressed GIFs. See http://www.lcdf.org/gifsicle for more information.\n");
00199 #endif
00200 }
00201 
00202 
00203 void
00204 verbose_open(char open, const char *name)
00205 {
00206   int l = strlen(name);
00207   if (verbose_pos && verbose_pos + 3 + l > 79) {
00208     fputc('\n', stderr);
00209     verbose_pos = 0;
00210   }
00211   if (verbose_pos) {
00212     fputc(' ', stderr);
00213     verbose_pos++;
00214   }
00215   fputc(open, stderr);
00216   fputs(name, stderr);
00217   verbose_pos += 1 + l;
00218 }
00219 
00220 
00221 void
00222 verbose_close(char close)
00223 {
00224   fputc(close, stderr);
00225   verbose_pos++;
00226 }
00227 
00228 
00229 void
00230 verbose_endline(void)
00231 {
00232   if (verbose_pos) {
00233     fputc('\n', stderr);
00234     fflush(stderr);
00235     verbose_pos = 0;
00236   }
00237 }
00238 
00239 
00240 /*****
00241  * Info functions
00242  **/
00243 
00244 
00245 static void
00246 safe_puts(const char *s, u_int32_t len, FILE *f)
00247 {
00248   const char *last_safe = s;
00249   for (; len > 0; len--, s++)
00250     if (*s < ' ' || *s >= 0x7F || *s == '\\') {
00251       if (last_safe != s)
00252         fwrite(last_safe, 1, s - last_safe, f);
00253       last_safe = s + 1;
00254       switch (*s) {
00255        case '\a': fputs("\\a", f); break;
00256        case '\b': fputs("\\b", f); break;
00257        case '\f': fputs("\\f", f); break;
00258        case '\n': fputs("\\n", f); break;
00259        case '\r': fputs("\\r", f); break;
00260        case '\t': fputs("\\t", f); break;
00261        case '\v': fputs("\\v", f); break;
00262        case '\\': fputs("\\\\", f); break;
00263        case 0:    if (len > 1) fputs("\\000", f); break;
00264        default:   fprintf(f, "\\%03o", *s); break;
00265       }
00266     }
00267   if (last_safe != s)
00268     fwrite(last_safe, 1, s - last_safe, f);
00269 }
00270 
00271 
00272 static void
00273 comment_info(FILE *where, Gif_Comment *gfcom, char *prefix)
00274 {
00275   int i;
00276   for (i = 0; i < gfcom->count; i++) {
00277     fputs(prefix, where);
00278     safe_puts(gfcom->str[i], gfcom->len[i], where);
00279     fputc('\n', where);
00280   }
00281 }
00282 
00283 
00284 #define COLORMAP_COLS   4
00285 
00286 static void
00287 colormap_info(FILE *where, Gif_Colormap *gfcm, char *prefix)
00288 {
00289   int i, j;
00290   int nrows = ((gfcm->ncol - 1) / COLORMAP_COLS) + 1;
00291   
00292   for (j = 0; j < nrows; j++) {
00293     int which = j;
00294     fputs(prefix, where);
00295     for (i = 0; i < COLORMAP_COLS && which < gfcm->ncol; i++, which += nrows) {
00296       if (i) fputs("    ", where);
00297       fprintf(where, " %3d: #%02X%02X%02X", which, gfcm->col[which].red,
00298               gfcm->col[which].green, gfcm->col[which].blue);
00299     }
00300     fputc('\n', where);
00301   }
00302 }
00303 
00304 
00305 static void
00306 extension_info(FILE *where, Gif_Stream *gfs, Gif_Extension *gfex, int count)
00307 {
00308   byte *data = gfex->data;
00309   u_int32_t pos = 0;
00310   u_int32_t len = gfex->length;
00311   
00312   fprintf(where, "  extension %d: ", count);
00313   if (gfex->kind == 255) {
00314     fprintf(where, "app `");
00315     safe_puts(gfex->application, strlen(gfex->application), where);
00316     fprintf(where, "'");
00317   } else {
00318     if (gfex->kind >= 32 && gfex->kind < 127)
00319       fprintf(where, "`%c' (0x%02X)", gfex->kind, gfex->kind);
00320     else
00321       fprintf(where, "0x%02X", gfex->kind);
00322   }
00323   if (gfex->position >= gfs->nimages)
00324     fprintf(where, " at end\n");
00325   else
00326     fprintf(where, " before #%d\n", gfex->position);
00327   
00328   /* Now, hexl the data. */
00329   while (len > 0) {
00330     u_int32_t row = 16;
00331     u_int32_t i;
00332     if (row > len) row = len;
00333     fprintf(where, "    %08x: ", pos);
00334     
00335     for (i = 0; i < row; i += 2) {
00336       if (i + 1 >= row)
00337         fprintf(where, "%02x   ", data[i]);
00338       else
00339         fprintf(where, "%02x%02x ", data[i], data[i+1]);
00340     }
00341     for (; i < 16; i += 2)
00342       fputs("     ", where);
00343     
00344     putc(' ', where);
00345     for (i = 0; i < row; i++, data++)
00346       putc((*data >= ' ' && *data < 127 ? *data : '.'), where);
00347     putc('\n', where);
00348     
00349     pos += row;
00350     len -= row;
00351   }
00352 }
00353 
00354 
00355 void
00356 stream_info(FILE *where, Gif_Stream *gfs, const char *filename,
00357             int colormaps, int extensions)
00358 {
00359   Gif_Extension *gfex;
00360   int n;
00361   
00362   if (!gfs) return;
00363   
00364   verbose_endline();
00365   fprintf(where, "* %s %d image%s\n", (filename ? filename : "<stdin>"),
00366           gfs->nimages, gfs->nimages == 1 ? "" : "s");
00367   fprintf(where, "  logical screen %dx%d\n",
00368           gfs->screen_width, gfs->screen_height);
00369   
00370   if (gfs->global) {
00371     fprintf(where, "  global color table [%d]\n", gfs->global->ncol);
00372     if (colormaps) colormap_info(where, gfs->global, "  |");
00373     fprintf(where, "  background %d\n", gfs->background);
00374   }
00375   
00376   if (gfs->comment)
00377     comment_info(where, gfs->comment, "  end comment ");
00378   
00379   if (gfs->loopcount == 0)
00380     fprintf(where, "  loop forever\n");
00381   else if (gfs->loopcount > 0)
00382     fprintf(where, "  loop count %u\n", (unsigned)gfs->loopcount);
00383   
00384   for (n = 0, gfex = gfs->extensions; gfex; gfex = gfex->next, n++)
00385     if (extensions)
00386       extension_info(where, gfs, gfex, n);
00387   if (n && !extensions)
00388     fprintf(where, "  extensions %d\n", n);
00389 }
00390 
00391 
00392 static char *disposal_names[] = {
00393   "none", "asis", "background", "previous", "4", "5", "6", "7"
00394 };
00395 
00396 void
00397 image_info(FILE *where, Gif_Stream *gfs, Gif_Image *gfi, int colormaps)
00398 {
00399   int num;
00400   if (!gfs || !gfi) return;
00401   num = Gif_ImageNumber(gfs, gfi);
00402   
00403   verbose_endline();
00404   fprintf(where, "  + image #%d ", num);
00405   if (gfi->identifier)
00406     fprintf(where, "#%s ", gfi->identifier);
00407   
00408   fprintf(where, "%dx%d", gfi->width, gfi->height);
00409   if (gfi->left || gfi->top)
00410     fprintf(where, " at %d,%d", gfi->left, gfi->top);
00411   
00412   if (gfi->interlace)
00413     fprintf(where, " interlaced");
00414   
00415   if (gfi->transparent >= 0)
00416     fprintf(where, " transparent %d", gfi->transparent);
00417   
00418 #if defined(PRINT_SIZE)
00419   if (gfi->compressed)
00420     fprintf(where, " compressed size %u min_code_size %d", gfi->compressed_len, *gfi->compressed);
00421 #endif
00422   
00423   fprintf(where, "\n");
00424   
00425   if (gfi->comment)
00426     comment_info(where, gfi->comment, "    comment ");
00427   
00428   if (gfi->local) {
00429     fprintf(where, "    local color table [%d]\n", gfi->local->ncol);
00430     if (colormaps) colormap_info(where, gfi->local, "    |");
00431   }
00432   
00433   if (gfi->disposal || gfi->delay) {
00434     fprintf(where, "   ");
00435     if (gfi->disposal)
00436       fprintf(where, " disposal %s", disposal_names[gfi->disposal]);
00437     if (gfi->delay)
00438       fprintf(where, " delay %d.%02ds",
00439               gfi->delay / 100, gfi->delay % 100);
00440     fprintf(where, "\n");
00441   }
00442 }
00443 
00444 
00445 char *
00446 explode_filename(char *filename, int number, char *name, int max_nimages)
00447 {
00448   static char *s;
00449   int l = strlen(filename);
00450   l += name ? strlen(name) : 10;
00451   
00452   Gif_Delete(s);
00453   s = Gif_NewArray(char, l + 3);
00454   if (name)
00455     sprintf(s, "%s.%s", filename, name);
00456   else if (max_nimages <= 1000)
00457     sprintf(s, "%s.%03d", filename, number);
00458   else {
00459     int digits;
00460     unsigned j;
00461     unsigned max = (max_nimages < 0 ? 0 : max_nimages);
00462     for (digits = 4, j = 10000; max > j; digits++)
00463       j *= 10;
00464     sprintf(s, "%s.%0*d", filename, digits, number);
00465   }
00466   
00467   return s;
00468 }
00469 
00470 
00471 /*****
00472  * parsing functions
00473  **/
00474 
00475 int frame_spec_1;
00476 int frame_spec_2;
00477 char *frame_spec_name;
00478 int dimensions_x;
00479 int dimensions_y;
00480 int position_x;
00481 int position_y;
00482 Gif_Color parsed_color;
00483 Gif_Color parsed_color2;
00484 double parsed_scale_factor_x;
00485 double parsed_scale_factor_y;
00486 
00487 int
00488 parse_frame_spec(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00489 {
00490   char *c;
00491   
00492   frame_spec_1 = 0;
00493   frame_spec_2 = -1;
00494   frame_spec_name = 0;
00495   
00496   if (!input && !input_name)
00497     input_stream(0);
00498   if (!input)
00499     return 0;
00500   
00501   if (arg[0] != '#') {
00502     if (complain)
00503       return Clp_OptionError(clp, "frame specifications must start with #");
00504     else
00505       return 0;
00506   }
00507   arg++;
00508   c = (char *)arg;
00509   
00510   /* Get a number range (#x, #x-y, or #x-). First, read x. */
00511   if (isdigit(c[0]))
00512     frame_spec_1 = frame_spec_2 = strtol(c, &c, 10);
00513   else if (c[0] == '-' && isdigit(c[1])) {
00514     frame_spec_1 = frame_spec_2 = Gif_ImageCount(input) + strtol(c, &c, 10);
00515     if (frame_spec_1 < 0)
00516       return complain ? Clp_OptionError(clp, "there are only %d frames",
00517                                         Gif_ImageCount(input)) : 0;
00518   }
00519   
00520   /* Then, if the next character is a dash, read y. Be careful to prevent
00521      #- from being interpreted as a frame range. */
00522   if (c[0] == '-' && (frame_spec_2 > 0 || c[1] != 0)) {
00523     c++;
00524     if (isdigit(c[0]))
00525       frame_spec_2 = strtol(c, &c, 10);
00526     else if (c[0] == '-' && isdigit(c[1]))
00527       frame_spec_2 = Gif_ImageCount(input) + strtol(c, &c, 10);
00528     else
00529       frame_spec_2 = Gif_ImageCount(input) - 1;
00530   }
00531   
00532   /* It really was a number range (and not a frame name)
00533      only if c is now at the end of the argument. */
00534   if (c[0] != 0) {
00535     Gif_Image *gfi = Gif_GetNamedImage(input, arg);
00536     if (gfi) {
00537       frame_spec_name = (char *)arg;
00538       frame_spec_1 = frame_spec_2 = Gif_ImageNumber(input, gfi);
00539       return 1;
00540     } else if (complain < 0)    /* -1 is special value meaning `don't complain
00541                                    about frame NAMES, but do complain about
00542                                    frame numbers.' */
00543       return -97;               /* Return -97 on bad frame name. */
00544     else if (complain)
00545       return Clp_OptionError(clp, "no frame named `#%s'", arg);
00546     else
00547       return 0;
00548     
00549   } else {
00550     if (frame_spec_1 >= 0 && frame_spec_1 <= frame_spec_2
00551         && frame_spec_2 < Gif_ImageCount(input))
00552       return 1;
00553     else if (!complain)
00554       return 0;
00555     
00556     if (frame_spec_1 == frame_spec_2)
00557       return Clp_OptionError(clp, "no frame number #%d", frame_spec_1);
00558     else if (frame_spec_1 < 0)
00559       return Clp_OptionError(clp, "frame numbers can't be negative");
00560     else if (frame_spec_1 > frame_spec_2)
00561       return Clp_OptionError(clp, "empty frame range");
00562     else
00563       return Clp_OptionError(clp, "there are only %d frames",
00564                              Gif_ImageCount(input));
00565   }
00566 }
00567 
00568 int
00569 parse_dimensions(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00570 {
00571   char *val;
00572 
00573   if (*arg == '_' && arg[1] == 'x') {
00574     dimensions_x = 0;
00575     val = (char *)(arg + 1);
00576   } else
00577     dimensions_x = strtol(arg, &val, 10);
00578   if (*val == 'x') {
00579     if (val[1] == '_' && val[2] == 0) {
00580       dimensions_y = 0;
00581       val = val + 2;
00582     } else
00583       dimensions_y = strtol(val + 1, &val, 10);
00584     if (*val == 0)
00585       return 1;
00586   }
00587   
00588   if (complain)
00589     return Clp_OptionError(clp, "invalid dimensions `%s' (want WxH)", arg);
00590   else
00591     return 0;
00592 }
00593 
00594 int
00595 parse_position(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00596 {
00597   char *val;
00598   
00599   position_x = strtol(arg, &val, 10);
00600   if (*val == ',') {
00601     position_y = strtol(val + 1, &val, 10);
00602     if (*val == 0)
00603       return 1;
00604   }
00605   
00606   if (complain)
00607     return Clp_OptionError(clp, "invalid position `%s' (want `X,Y')", arg);
00608   else
00609     return 0;
00610 }
00611 
00612 int
00613 parse_scale_factor(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00614 {
00615   char *val;
00616   
00617   parsed_scale_factor_x = strtod(arg, &val);
00618   if (*val == 'x') {
00619     parsed_scale_factor_y = strtod(val + 1, &val);
00620     if (*val == 0)
00621       return 1;
00622   } else if (*val == 0) {
00623     parsed_scale_factor_y = parsed_scale_factor_x;
00624     return 1;
00625   }
00626   
00627   if (complain)
00628     return Clp_OptionError(clp, "invalid scale factor `%s' (want XxY)", arg);
00629   else
00630     return 0;
00631 }
00632 
00633 int
00634 parse_rectangle(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00635 {
00636   const char *input_arg = arg;
00637   char *val;
00638   
00639   int x = position_x = strtol(arg, &val, 10);
00640   if (*val == ',') {
00641     int y = position_y = strtol(val + 1, &val, 10);
00642     if (*val == '-' && parse_position(clp, val + 1, 0, 0)) {
00643       if (x >= 0 && y >= 0 && x < position_x && y < position_y) {
00644         dimensions_x = position_x - x;
00645         dimensions_y = position_y - y;
00646         position_x = x;
00647         position_y = y;
00648         return 1;
00649       }
00650     } else if (*val == '+' && parse_dimensions(clp, val + 1, 0, 0))
00651       return 1;
00652   } else if (*val == 'x') {
00653     dimensions_x = position_x;
00654     dimensions_y = strtol(val + 1, &val, 10);
00655     if (*val == 0) {
00656       position_x = position_y = 0;
00657       return 1;
00658     }
00659   }
00660   
00661   if (complain)
00662     return Clp_OptionError(clp, "invalid rectangle `%s' (want `X1,Y1-X2,Y2' or `X1,Y1+WxH'", input_arg);
00663   else
00664     return 0;
00665 }
00666 
00667 static int
00668 xvalue(char c)
00669 {
00670   switch (c) {
00671    case '0': case '1': case '2': case '3': case '4':
00672    case '5': case '6': case '7': case '8': case '9':
00673     return c - '0';
00674    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
00675     return c - 'A' + 10;
00676    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
00677     return c - 'a' + 10;
00678    default:
00679     return -1;
00680   }
00681 }
00682 
00683 static int
00684 parse_hex_color_channel(const char *s, int ndigits)
00685 {
00686   int val1 = xvalue(s[0]);
00687   if (val1 < 0) return -1;
00688   if (ndigits == 1)
00689     return val1 * 16 + val1;
00690   else {
00691     int val2 = xvalue(s[1]);
00692     if (val2 < 0) return -1;
00693     return val1 * 16 + val2;
00694   }
00695 }
00696 
00697 int
00698 parse_color(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00699 {
00700   const char *input_arg = arg;
00701   char *str;
00702   int red, green, blue;
00703   
00704   if (*arg == '#') {
00705     int len = strlen(++arg);
00706     if (!len || len % 3 != 0 || strspn(arg, "0123456789ABCDEFabcdef") != len) {
00707       if (complain)
00708         Clp_OptionError(clp, "invalid color `%s' (want `#RGB' or `#RRGGBB')",
00709                         input_arg);
00710       return 0;
00711     }
00712     
00713     len /= 3;
00714     red   = parse_hex_color_channel(&arg[ 0 * len ], len);
00715     green = parse_hex_color_channel(&arg[ 1 * len ], len);
00716     blue  = parse_hex_color_channel(&arg[ 2 * len ], len);
00717     goto gotrgb;
00718     
00719   } else if (!isdigit(*arg))
00720     goto error;
00721   
00722   red = strtol(arg, &str, 10);
00723   if (*str == 0) {
00724     if (red < 0 || red > 255)
00725       goto error;
00726     parsed_color.haspixel = 1;
00727     parsed_color.pixel = red;
00728     return 1;
00729     
00730   } else if (*str != ',' && *str != '/')
00731     goto error;
00732   
00733   if (*++str == 0) goto error;
00734   green = strtol(str, &str, 10);
00735   if (*str != ',' && *str != '/') goto error;
00736   
00737   if (*++str == 0) goto error;
00738   blue = strtol(str, &str, 10);
00739   if (*str != 0) goto error;
00740   
00741  gotrgb:
00742   if (red < 0 || green < 0 || blue < 0
00743       || red > 255 || green > 255 || blue > 255)
00744     goto error;
00745   parsed_color.red = red;
00746   parsed_color.green = green;
00747   parsed_color.blue = blue;
00748   parsed_color.haspixel = 0;
00749   return 1;
00750   
00751  error:
00752   if (complain)
00753     return Clp_OptionError(clp, "invalid color `%s'", input_arg);
00754   else
00755     return 0;
00756 }
00757 
00758 int
00759 parse_two_colors(Clp_Parser *clp, const char *arg, int complain, void *thunk)
00760 {
00761   Gif_Color old_color;
00762   if (parse_color(clp, arg, complain, thunk) <= 0)
00763     return 0;
00764   old_color = parsed_color;
00765   
00766   arg = Clp_Shift(clp, 0);
00767   if (!arg && complain)
00768     return Clp_OptionError(clp, "`%O' takes two color arguments");
00769   else if (!arg)
00770     return 0;
00771   
00772   if (parse_color(clp, arg, complain, thunk) <= 0)
00773     return 0;
00774   
00775   parsed_color2 = parsed_color;
00776   parsed_color = old_color;
00777   return 1;
00778 }
00779 
00780 
00781 /*****
00782  * reading a file as a colormap
00783  **/
00784 
00785 static Gif_Colormap *
00786 read_text_colormap(FILE *f, char *name)
00787 {
00788   char buf[BUFSIZ];
00789   Gif_Colormap *cm = Gif_NewFullColormap(0, 256);
00790   Gif_Color *col = cm->col;
00791   int ncol = 0;
00792   unsigned red, green, blue;
00793   float fred, fgreen, fblue;
00794   
00795   while (fgets(buf, BUFSIZ, f)) {
00796     
00797     if (sscanf(buf, "%g %g %g", &fred, &fgreen, &fblue) == 3) {
00798       if (fred < 0) fred = 0;
00799       if (fgreen < 0) fgreen = 0;
00800       if (fblue < 0) fblue = 0;
00801       red = (unsigned)(fred + .5);
00802       green = (unsigned)(fgreen + .5);
00803       blue = (unsigned)(fblue + .5);
00804       goto found;
00805       
00806     } else if (sscanf(buf, "#%2x%2x%2x", &red, &green, &blue) == 3) {
00807      found:
00808       if (red > 255) red = 255;
00809       if (green > 255) green = 255;
00810       if (blue > 255) blue = 255;
00811       if (ncol >= 256) {
00812         error("%s: maximum 256 colors allowed in colormap", name);
00813         break;
00814       } else {
00815         col[ncol].red = red;
00816         col[ncol].green = green;
00817         col[ncol].blue = blue;
00818         ncol++;
00819       }
00820     }
00821     
00822     /* handle too-long lines gracefully */
00823     if (strchr(buf, '\n') == 0) {
00824       int c;
00825       for (c = getc(f); c != '\n' && c != EOF; c = getc(f))
00826         ;
00827     }
00828   }
00829   
00830   if (ncol == 0) {
00831     error("`%s' doesn't seem to contain a colormap", name);
00832     Gif_DeleteColormap(cm);
00833     return 0;
00834   } else {
00835     cm->ncol = ncol;
00836     return cm;
00837   }
00838 }
00839 
00840 Gif_Colormap *
00841 read_colormap_file(char *name, FILE *f)
00842 {
00843   Gif_Colormap *cm = 0;
00844   int c;
00845   int my_file = 0;
00846   
00847   if (name && strcmp(name, "-") == 0)
00848     name = 0;
00849   if (!f) {
00850     my_file = 1;
00851     if (!name)
00852       f = stdin;
00853     else
00854       f = fopen(name, "rb");
00855     if (!f) {
00856       error("%s: %s", name, strerror(errno));
00857       return 0;
00858     }
00859   }
00860   
00861   name = name ? name : "<stdin>";
00862   if (verbosing) verbose_open('<', name);
00863   
00864   c = getc(f);
00865   ungetc(c, f);
00866   if (c == 'G') {
00867     Gif_Stream *gfs = Gif_ReadFile(f);
00868     if (!gfs)
00869       error("`%s' doesn't seem to contain a GIF", name);
00870     else if (!gfs->global)
00871       error("can't use `%s' as a palette (no global color table)", name);
00872     else {
00873       if (gfs->errors)
00874         warning("there were errors reading `%s'", name);
00875       cm = Gif_CopyColormap(gfs->global);
00876     }
00877     
00878     Gif_DeleteStream(gfs);
00879   } else
00880     cm = read_text_colormap(f, name);
00881   
00882   if (my_file) fclose(f);
00883   if (verbosing) verbose_close('>');
00884   return cm;
00885 }
00886 
00887 
00888 /*****
00889  * Frame stuff
00890  **/
00891 
00892 
00893 Gt_Frameset *
00894 new_frameset(int initial_cap)
00895 {
00896   Gt_Frameset *fs = Gif_New(Gt_Frameset);
00897   if (initial_cap < 0) initial_cap = 0;
00898   fs->cap = initial_cap;
00899   fs->count = 0;
00900   fs->f = Gif_NewArray(Gt_Frame, initial_cap);
00901   return fs;
00902 }
00903 
00904 
00905 void
00906 clear_def_frame_once_options(void)
00907 {
00908   /* Get rid of next-frame-only options.
00909      
00910      This causes problems with frame selection. In the command `gifsicle
00911      -nblah f.gif', the name should be applied to frame 0 of f.gif. This will
00912      happen automatically when f.gif is read, since all of its frames will be
00913      added when it is input. After frame 0, the name in def_frame will be
00914      cleared.
00915      
00916      Now, `gifsicle -nblah f.gif #1' should apply the name to frame 1 of
00917      f.gif. But once f.gif is input, its frames are added, and the name
00918      component of def_frame is cleared!! So when #1 comes around it's gone!
00919      
00920      We handle this in gifsicle.c using the _change fields. */
00921   
00922   def_frame.name = 0;
00923   def_frame.comment = 0;
00924   def_frame.extensions = 0;
00925 }
00926 
00927 
00928 Gt_Frame *
00929 add_frame(Gt_Frameset *fset, int number, Gif_Stream *gfs, Gif_Image *gfi)
00930 {
00931   if (number < 0) {
00932     while (fset->count >= fset->cap) {
00933       fset->cap *= 2;
00934       Gif_ReArray(fset->f, Gt_Frame, fset->cap);
00935     }
00936     number = fset->count++;
00937   } else {
00938     assert(number < fset->count);
00939     blank_frameset(fset, number, number, 0);
00940   }
00941 
00942   /* Mark the stream and the image both */
00943   gfs->refcount++;
00944   gfi->refcount++;
00945   fset->f[number] = def_frame;
00946   fset->f[number].stream = gfs;
00947   fset->f[number].image = gfi;
00948   
00949   clear_def_frame_once_options();
00950   
00951   return &fset->f[number];
00952 }
00953 
00954 
00955 static Gif_Extension *
00956 copy_extension(Gif_Extension *src)
00957 {
00958   Gif_Extension *dest = Gif_NewExtension(src->kind, src->application);
00959   if (!dest) return 0;
00960   dest->data = Gif_NewArray(byte, src->length);
00961   dest->length = src->length;
00962   dest->free_data = Gif_DeleteArrayFunc;
00963   if (!dest->data) {
00964     Gif_DeleteExtension(dest);
00965     return 0;
00966   }
00967   memcpy(dest->data, src->data, src->length);
00968   return dest;
00969 }
00970 
00971 
00972 static Gt_Frame **merger = 0;
00973 static int nmerger = 0;
00974 static int mergercap = 0;
00975 
00976 static void
00977 merger_add(Gt_Frame *fp)
00978 {
00979   while (nmerger >= mergercap)
00980     if (mergercap) {
00981       mergercap *= 2;
00982       Gif_ReArray(merger, Gt_Frame *, mergercap);
00983     } else {
00984       mergercap = 16;
00985       merger = Gif_NewArray(Gt_Frame *, mergercap);
00986     }
00987   merger[ nmerger++ ] = fp;
00988 }
00989 
00990 
00991 static void
00992 merger_flatten(Gt_Frameset *fset, int f1, int f2)
00993 {
00994   int i;
00995   assert(f1 >= 0 && f2 < fset->count);
00996   for (i = f1; i <= f2; i++) {
00997     Gt_Frameset *nest = FRAME(fset, i).nest;
00998     
00999     if (nest && nest->count > 0) {
01000       if (FRAME(fset, i).use < 0 && nest->count == 1) {
01001         /* use < 0 means use the frame's delay, disposal and name (if not
01002            explicitly overridden), but not the frame itself. */
01003         if (FRAME(nest, 0).delay < 0)
01004           FRAME(nest, 0).delay = FRAME(fset, i).image->delay;
01005         if (FRAME(nest, 0).disposal < 0)
01006           FRAME(nest, 0).disposal = FRAME(fset, i).image->disposal;
01007         if (FRAME(nest, 0).name == 0 && FRAME(nest, 0).no_name == 0)
01008           FRAME(nest, 0).name =
01009             Gif_CopyString(FRAME(fset, i).image->identifier);
01010       }
01011       merger_flatten(nest, 0, nest->count - 1);
01012     }
01013     
01014     if (FRAME(fset, i).use > 0)
01015       merger_add(&FRAME(fset, i));
01016   }
01017 }
01018 
01019 
01020 static void
01021 find_background(Gif_Colormap *dest_global, Gif_Color *background)
01022 {
01023   int i;
01024   /* This code is SUCH a PAIN in the PATOOTIE!! */
01025   
01026   /* 0. report warnings if the user wants an impossible background setting */
01027   if (background->haspixel) {
01028     int first_img_transp = 0;
01029     if (merger[0]->transparent.haspixel < 255)
01030       first_img_transp = (merger[0]->image->transparent >= 0
01031                           || merger[0]->transparent.haspixel);
01032     if (first_img_transp) {
01033       static int context = 0;
01034       warning("irrelevant background color");
01035       if (!context) {
01036         warncontext("(The background will appear transparent because");
01037         warncontext("the first image contains transparency.)");
01038         context = 1;
01039       }
01040     }
01041   }
01042   
01043   /* 1. user set the background to a color index
01044      -> find the associated color */
01045   if (background->haspixel == 2) {
01046     Gif_Stream *gfs = merger[0]->stream;
01047     if (gfs->global && background->pixel < gfs->global->ncol) {
01048       *background = gfs->global->col[ background->pixel ];
01049       background->haspixel = 1;
01050     } else {
01051       error("background color index `%d' out of range", background->pixel);
01052       background->haspixel = 0;
01053     }
01054   }
01055   
01056   /* 2. search the existing streams and images for background colors
01057         (prefer colors from images with disposal == BACKGROUND)
01058      -> choose the color. warn if two such images have conflicting colors */
01059   if (background->haspixel == 0) {
01060     int relevance = 0;
01061     int saved_bg_transparent = 0;
01062     for (i = 0; i < nmerger; i++) {
01063       Gif_Stream *gfs = merger[i]->stream;
01064       int bg_disposal = merger[i]->image->disposal == GIF_DISPOSAL_BACKGROUND;
01065       int bg_transparent = gfs->images[0]->transparent >= 0;
01066       int bg_exists = gfs->global && gfs->background < gfs->global->ncol;
01067       
01068       if ((!bg_exists && !bg_transparent) || (bg_disposal + 1 < relevance))
01069         continue;
01070       else if (bg_disposal + 1 > relevance) {
01071         if (bg_exists)
01072           *background = gfs->global->col[gfs->background];
01073         else
01074           background->red = background->green = background->blue = 0;
01075         saved_bg_transparent = bg_transparent;
01076         relevance = bg_disposal + 1;
01077         continue;
01078       }
01079       
01080       /* check for conflicting background requirements */
01081       if (bg_transparent != saved_bg_transparent
01082           || (!saved_bg_transparent &&
01083               !GIF_COLOREQ(background, &gfs->global->col[gfs->background]))) {
01084         static int context = 0;
01085         warning("input images have conflicting background colors");
01086         if (!context) {
01087           warncontext("(This means some animation frames may appear incorrect.)");
01088           context = 1;
01089         }
01090         break;
01091       }
01092     }
01093     background->haspixel = relevance != 0;
01094   }
01095   
01096   /* 3. we found a background color
01097      -> force the merging process to keep it in the global colormap with
01098      COLORMAP_ENSURE_SLOT_255. See merge.c function ensure_slot_255 */
01099   if (background->haspixel) {
01100     dest_global->userflags |= COLORMAP_ENSURE_SLOT_255;
01101     dest_global->col[255] = *background;
01102   }
01103 }
01104 
01105 
01106 static int
01107 find_color_or_error(Gif_Color *color, Gif_Stream *gfs, Gif_Image *gfi,
01108                     char *color_context)
01109 {
01110   Gif_Colormap *gfcm = gfs->global;
01111   int index;
01112   if (gfi && gfi->local) gfcm = gfi->local;
01113   
01114   if (color->haspixel == 2) {   /* have pixel value, not color */
01115     if (color->pixel < gfcm->ncol)
01116       return color->pixel;
01117     else {
01118       if (color_context) error("%s color out of range", color_context);
01119       return -1;
01120     }
01121   }
01122   
01123   index = Gif_FindColor(gfcm, color);
01124   if (index < 0 && color_context)
01125     error("%s color not in colormap", color_context);
01126   return index;
01127 }
01128 
01129 
01130 static void
01131 fix_total_crop(Gif_Stream *dest, Gif_Image *srci, int merger_index)
01132 {
01133   /* Salvage any relevant information from a frame that's been completely
01134      cropped away. This ends up being comments and delay. */
01135   Gt_Frame *fr = merger[merger_index];
01136   Gt_Frame *next_fr = 0;
01137   Gif_Image *prev_image = 0;
01138   if (dest->nimages > 0) prev_image = dest->images[dest->nimages - 1];
01139   if (merger_index < nmerger - 1) next_fr = merger[merger_index + 1];
01140   
01141   /* Don't save identifiers since the frame that was to be identified, is
01142      gone. Save comments though. */
01143   if (!fr->no_comments && srci->comment && next_fr) {
01144     if (!next_fr->comment) next_fr->comment = Gif_NewComment();
01145     merge_comments(next_fr->comment, srci->comment);
01146   }
01147   if (fr->comment && next_fr) {
01148     if (!next_fr->comment) next_fr->comment = Gif_NewComment();
01149     merge_comments(next_fr->comment, fr->comment);
01150     Gif_DeleteComment(fr->comment);
01151     fr->comment = 0;
01152   }
01153   
01154   /* Save delay by adding it to the previous frame's delay. */
01155   if (fr->delay < 0)
01156     fr->delay = srci->delay;
01157   prev_image->delay += fr->delay;
01158 }
01159 
01160 
01161 static void
01162 handle_screen(Gif_Stream *dest, u_int16_t width, u_int16_t height)
01163 {
01164   /* Set the screen width & height, if the current input width and height are
01165      larger */
01166   if (dest->screen_width < width)
01167     dest->screen_width = width;
01168   if (dest->screen_height < height)
01169     dest->screen_height = height;
01170 }
01171 
01172 static void
01173 handle_flip_and_screen(Gif_Stream *dest, Gif_Image *desti,
01174                        Gt_Frame *fr, int first_image)
01175 {
01176   Gif_Stream *gfs = fr->stream;
01177   
01178   u_int16_t screen_width = gfs->screen_width;
01179   u_int16_t screen_height = gfs->screen_height;
01180   
01181   if (fr->flip_horizontal)
01182     flip_image(desti, screen_width, screen_height, 0);
01183   if (fr->flip_vertical)
01184     flip_image(desti, screen_width, screen_height, 1);
01185   
01186   if (fr->rotation == 1)
01187     rotate_image(desti, screen_width, screen_height, 1);
01188   else if (fr->rotation == 2) {
01189     flip_image(desti, screen_width, screen_height, 0);
01190     flip_image(desti, screen_width, screen_height, 1);
01191   } else if (fr->rotation == 3)
01192     rotate_image(desti, screen_width, screen_height, 3);
01193   
01194   /* handle screen size, which might have height & width exchanged */
01195   if (fr->rotation == 1 || fr->rotation == 3)
01196     handle_screen(dest, screen_height, screen_width);
01197   else
01198     handle_screen(dest, screen_width, screen_height);
01199 }
01200 
01201 
01202 Gif_Stream *
01203 merge_frame_interval(Gt_Frameset *fset, int f1, int f2,
01204                      Gt_OutputData *output_data, int compress_immediately)
01205 {
01206   Gif_Stream *dest = Gif_NewStream();
01207   Gif_Colormap *global = Gif_NewFullColormap(256, 256);
01208   Gif_Color dest_background;
01209   int i;
01210   
01211   global->ncol = 0;
01212   dest->global = global;
01213   /* 11/23/98 A new stream's screen size is 0x0; we'll use the max of the
01214      merged-together streams' screen sizes by default (in merge_stream()) */
01215   
01216   if (f2 < 0) f2 = fset->count - 1;
01217   nmerger = 0;
01218   merger_flatten(fset, f1, f2);
01219   if (nmerger == 0) {
01220     error("empty output GIF not written");
01221     return 0;
01222   }
01223   
01224   /* merge stream-specific info and clear colormaps */
01225   for (i = 0; i < nmerger; i++)
01226     merger[i]->stream->userflags = 1;
01227   for (i = 0; i < nmerger; i++) {
01228     if (merger[i]->stream->userflags) {
01229       Gif_Stream *src = merger[i]->stream;
01230       Gif_CalculateScreenSize(src, 0);
01231       /* merge_stream() unmarks the global colormap */
01232       merge_stream(dest, src, merger[i]->no_comments);
01233       src->userflags = 0;
01234     }
01235     if (merger[i]->image->local)
01236       unmark_colors_2(merger[i]->image->local);
01237   }
01238   
01239   /* decide on the background. use the one from output_data */
01240   dest_background = output_data->background;
01241   find_background(global, &dest_background);
01242   
01243   /* check for cropping the whole stream */
01244   for (i = 0; i < nmerger; i++)
01245     if (merger[i]->crop)
01246       merger[i]->crop->whole_stream = 0;
01247   if (merger[0]->crop) {
01248     merger[0]->crop->whole_stream = 1;
01249     for (i = 1; i < nmerger; i++)
01250       if (merger[i]->crop != merger[0]->crop)
01251         merger[0]->crop->whole_stream = 0;
01252   }
01253   
01254   /* copy stream-wide information from output_data */
01255   if (output_data->loopcount > -2)
01256     dest->loopcount = output_data->loopcount;
01257   dest->screen_width = dest->screen_height = 0;
01258   
01259   /** ACTUALLY MERGE FRAMES INTO THE NEW STREAM **/
01260   for (i = 0; i < nmerger; i++) {
01261     Gt_Frame *fr = merger[i];
01262     Gif_Image *srci;
01263     Gif_Image *desti;
01264     int old_transparent;
01265     
01266     /* First, check for extensions */
01267     {
01268       int j;
01269       Gif_Extension *gfex = fr->stream->extensions;
01270       for (j = 0; fr->stream->images[j] != fr->image; j++) ;
01271       while (gfex && gfex->position < j)
01272         gfex = gfex->next;
01273       while (!fr->no_extensions && gfex && gfex->position == j) {
01274         Gif_AddExtension(dest, copy_extension(gfex), i);
01275         gfex = gfex->next;
01276       }
01277       gfex = fr->extensions;
01278       while (gfex) {
01279         Gif_Extension *next = gfex->next;
01280         Gif_AddExtension(dest, gfex, i);
01281         gfex = next;
01282       }
01283     }
01284     
01285     /* Make a copy of the image and crop it if we're cropping */
01286     if (fr->crop) {
01287       srci = Gif_CopyImage(fr->image);
01288       Gif_UncompressImage(srci);
01289       if (!crop_image(srci, fr->crop)) {
01290         /* We cropped the image out of existence! Be careful not to make 0x0
01291            frames. */
01292         fix_total_crop(dest, srci, i);
01293         goto merge_frame_done;
01294       }
01295     } else {
01296       srci = fr->image;
01297       Gif_UncompressImage(srci);
01298     }
01299     
01300     /* It was pretty stupid to remove this code, which I did between 1.2b6 and
01301        1.2 */
01302     old_transparent = srci->transparent;
01303     if (fr->transparent.haspixel == 255)
01304       srci->transparent = -1;
01305     else if (fr->transparent.haspixel)
01306       srci->transparent =
01307         find_color_or_error(&fr->transparent, fr->stream, srci, "transparent");
01308     
01309     desti = merge_image(dest, fr->stream, srci);
01310     
01311     srci->transparent = old_transparent; /* restore real transparent value */
01312     
01313     /* Flipping and rotating, and also setting the screen size */
01314     if (fr->flip_horizontal || fr->flip_vertical || fr->rotation)
01315       handle_flip_and_screen(dest, desti, fr, i == 0);
01316     else
01317       handle_screen(dest, fr->stream->screen_width, fr->stream->screen_height);
01318     
01319     /* Names and comments */
01320     if (fr->name || fr->no_name) {
01321       Gif_DeleteArray(desti->identifier);
01322       desti->identifier = Gif_CopyString(fr->name);
01323     }
01324     if (fr->no_comments && desti->comment) {
01325       Gif_DeleteComment(desti->comment);
01326       desti->comment = 0;
01327     }
01328     if (fr->comment) {
01329       if (!desti->comment) desti->comment = Gif_NewComment();
01330       merge_comments(desti->comment, fr->comment);
01331       /* delete the comment early to help with memory; set field to 0 so we
01332          don't re-free it later */
01333       Gif_DeleteComment(fr->comment);
01334       fr->comment = 0;
01335     }
01336     
01337     if (fr->interlacing >= 0)
01338       desti->interlace = fr->interlacing;
01339     if (fr->left >= 0)
01340       desti->left = fr->left + (fr->position_is_offset ? desti->left : 0);
01341     if (fr->top >= 0)
01342       desti->top = fr->top + (fr->position_is_offset ? desti->top : 0);
01343     
01344     if (fr->delay >= 0)
01345       desti->delay = fr->delay;
01346     if (fr->disposal >= 0)
01347       desti->disposal = fr->disposal;
01348     
01349     /* compress immediately if possible to save on memory */
01350     if (compress_immediately) {
01351       Gif_FullCompressImage(dest, desti, gif_write_flags);
01352       Gif_ReleaseUncompressedImage(desti);
01353     }
01354     
01355    merge_frame_done:
01356     /* Destroy the copied, cropped image if necessary */
01357     if (fr->crop)
01358       Gif_DeleteImage(srci);
01359     
01360     /* if we can, delete the image's data right now to save memory */
01361     srci = fr->image;
01362     assert(srci->refcount > 1);
01363     if (--srci->refcount == 1) {
01364       /* only 1 reference ==> the reference is from the input stream itself */
01365       Gif_ReleaseUncompressedImage(srci);
01366       Gif_ReleaseCompressedImage(srci);
01367       fr->image = 0;
01368     }
01369     
01370     /* 5/26/98 Destroy the stream now to help with memory. Assumes that
01371        all frames are added with add_frame() which properly increments the
01372        stream's refcount. Set field to 0 so we don't redelete */
01373     Gif_DeleteStream(fr->stream);
01374     fr->stream = 0;
01375   }
01376   /** END MERGE LOOP **/
01377 
01378   /* Cropping the whole output? Clear the logical screen */
01379   if (merger[0]->crop && merger[0]->crop == merger[nmerger-1]->crop)
01380     dest->screen_width = dest->screen_height = 0;
01381   /* Set the logical screen from the user's preferences */
01382   if (output_data->screen_width >= 0)
01383     dest->screen_width = output_data->screen_width;
01384   if (output_data->screen_height >= 0)
01385     dest->screen_height = output_data->screen_height;
01386   
01387   /* Find the background color in the colormap, or add it if we can */
01388   {
01389     int bg = find_color_or_error(&dest_background, dest, 0, 0);
01390     if (bg < 0 && dest->images[0]->transparent >= 0)
01391       dest->background = dest->images[0]->transparent;
01392     else if (bg < 0 && global->ncol < 256) {
01393       dest->background = global->ncol;
01394       global->col[ global->ncol ] = dest_background;
01395       global->ncol++;
01396     } else
01397       dest->background = bg;
01398   }
01399 
01400   return dest;
01401 }
01402 
01403 
01404 void
01405 blank_frameset(Gt_Frameset *fset, int f1, int f2, int delete_object)
01406 {
01407   int i;
01408   if (delete_object) f1 = 0, f2 = -1;
01409   if (f2 < 0) f2 = fset->count - 1;
01410   for (i = f1; i <= f2; i++) {
01411     /* We may have deleted stream and image earlier to save on memory; see
01412        above in merge_frame_interval(); but if we didn't, do it now. */
01413     if (FRAME(fset, i).image && FRAME(fset, i).image->refcount > 1)
01414       FRAME(fset, i).image->refcount--;
01415     Gif_DeleteStream(FRAME(fset, i).stream);
01416     Gif_DeleteComment(FRAME(fset, i).comment);
01417     if (FRAME(fset, i).nest)
01418       blank_frameset(FRAME(fset, i).nest, 0, 0, 1);
01419   }
01420   if (delete_object) {
01421     Gif_DeleteArray(fset->f);
01422     Gif_Delete(fset);
01423   }
01424 }
01425 
01426 
01427 void
01428 clear_frameset(Gt_Frameset *fset, int f1)
01429 {
01430   blank_frameset(fset, f1, -1, 0);
01431   fset->count = f1;
01432 }
 

Powered by Plone

This site conforms to the following standards: