00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 #include "config.h"
00011 #include "gifsicle.h"
00012 #include <string.h>
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <ctype.h>
00016 #include <assert.h>
00017 #include <errno.h>
00018 
00019 
00020 
00021 #if defined(_MSDOS) || defined(_WIN32) || defined(__EMX__)
00022 # include <fcntl.h>
00023 # include <io.h>
00024 #endif
00025 
00026 
00027 Gt_Frame def_frame;
00028 
00029 Gt_Frameset *frames = 0;
00030 int first_input_frame = 0;
00031 Gt_Frameset *nested_frames = 0;
00032 
00033 Gif_Stream *input = 0;
00034 char *input_name = 0;
00035 static int unoptimizing = 0;
00036 
00037 int gif_read_flags = 0;
00038 int gif_write_flags = 0;
00039 
00040 static int frames_done = 0;
00041 static int files_given = 0;
00042 
00043 int warn_local_colormaps = 1;
00044 
00045 static Gt_ColorTransform *input_transforms;
00046 static Gt_ColorTransform *output_transforms;
00047 
00048 #define BLANK_MODE      0
00049 #define MERGING         1
00050 #define BATCHING        2
00051 #define EXPLODING       3
00052 #define DELETING        4
00053 #define INSERTING       5
00054 static int mode = BLANK_MODE;
00055 static int nested_mode = 0;
00056 
00057 static int infoing = 0;
00058 int verbosing = 0;
00059 
00060 
00061 #define CHANGED(next, flag)     (((next) & 1<<(flag)) != 0)
00062 #define UNCHECKED_MARK_CH(where, what)                  \
00063   next_##where |= 1<<what;
00064 #define MARK_CH(where, what)                            \
00065   if (CHANGED(next_##where, what))                      \
00066     redundant_option_warning(where##_option_types[what]); \
00067   UNCHECKED_MARK_CH(where, what)
00068 
00069 
00070 static int next_frame = 0;
00071 #define CH_INTERLACE            0
00072 #define CH_DISPOSAL             1
00073 #define CH_DELAY                2
00074 #define CH_TRANSPARENT          3
00075 #define CH_COMMENT              4
00076 #define CH_NAME                 5
00077 #define CH_POSITION             6
00078 #define CH_CROP                 7
00079 #define CH_EXTENSION            8
00080 #define CH_FLIP                 9
00081 #define CH_ROTATE               10
00082 static const char *frame_option_types[] = {
00083   "interlace", "disposal", "delay", "transparency",
00084   "comment", "name", "position", "crop",
00085   "extension", "flip", "rotation"
00086 };
00087 
00088 
00089 static int next_input = 0;
00090 #define CH_UNOPTIMIZE           0
00091 #define CH_CHANGE_COLOR         1
00092 static const char *input_option_types[] = {
00093   "unoptimization", "color change"
00094 };
00095 
00096 
00097 static Gt_OutputData def_output_data;
00098 static Gt_OutputData active_output_data;
00099 static int next_output = 0;
00100 static int active_next_output = 0;
00101 static int any_output_successful = 0;
00102 #define CH_LOOPCOUNT            0
00103 #define CH_LOGICAL_SCREEN       1
00104 #define CH_OPTIMIZE             2
00105 #define CH_OUTPUT               3
00106 #define CH_COLORMAP             4
00107 #define CH_DITHER               5
00108 #define CH_USE_COLORMAP         6
00109 #define CH_COLORMAP_METHOD      7
00110 #define CH_BACKGROUND           8
00111 #define CH_COLOR_TRANSFORM      9
00112 #define CH_RESIZE               10
00113 static const char *output_option_types[] = {
00114   "loopcount", "logical screen", "optimization", "output file",
00115   "colormap size", "dither", "colormap", "colormap method",
00116   "background", "color transformation", "resize"
00117 };
00118 
00119 
00120 #define SAME_INTERLACE_OPT      300
00121 #define INFO_OPT                301
00122 #define DISPOSAL_OPT            302
00123 #define SAME_LOOPCOUNT_OPT      303
00124 #define SAME_DISPOSAL_OPT       304
00125 #define SAME_DELAY_OPT          305
00126 #define SAME_TRANSPARENT_OPT    308
00127 #define LOGICAL_SCREEN_OPT      309
00128 #define COMMENT_OPT             310
00129 #define UNOPTIMIZE_OPT          311
00130 #define CAREFUL_OPT             312
00131 #define OPTIMIZE_OPT            313
00132 #define SAME_LOGICAL_SCREEN_OPT 314
00133 #define DELETE_OPT              315
00134 #define REPLACE_OPT             316
00135 #define INSERT_OPT              317
00136 #define ALTER_DONE_OPT          318
00137 #define APPEND_OPT              319
00138 #define COLOR_INFO_OPT          320
00139 #define VERBOSE_OPT             321
00140 #define NO_COMMENTS_OPT         322
00141 #define SAME_COMMENTS_OPT       323
00142 #define NAME_OPT                324
00143 #define SAME_NAME_OPT           325
00144 #define NO_NAME_OPT             326
00145 #define POSITION_OPT            327
00146 #define SAME_POSITION_OPT       328
00147 #define VERSION_OPT             329
00148 #define HELP_OPT                330
00149 #define OUTPUT_OPT              331
00150 #define CROP_OPT                332
00151 #define SAME_CROP_OPT           333
00152 #define CHANGE_COLOR_OPT        334
00153 #define COLORMAP_OPT            335
00154 #define COLORMAP_ALGORITHM_OPT  336
00155 #define DITHER_OPT              337
00156 #define USE_COLORMAP_OPT        338
00157 #define NO_EXTENSIONS_OPT       339
00158 #define SAME_EXTENSIONS_OPT     340
00159 #define EXTENSION_INFO_OPT      341
00160 #define BACKGROUND_OPT          342
00161 #define SAME_BACKGROUND_OPT     343
00162 #define FLIP_HORIZ_OPT          344
00163 #define FLIP_VERT_OPT           345
00164 #define NO_FLIP_OPT             346
00165 #define ROTATE_90_OPT           347
00166 #define ROTATE_180_OPT          348
00167 #define ROTATE_270_OPT          349
00168 #define NO_ROTATE_OPT           350
00169 #define APP_EXTENSION_OPT       351
00170 #define EXTENSION_OPT           352
00171 #define COLOR_TRANSFORM_OPT     353
00172 #define RESIZE_OPT              354
00173 #define SCALE_OPT               355
00174 #define NO_WARNINGS_OPT         356
00175 #define WARNINGS_OPT            357
00176 #define RESIZE_WIDTH_OPT        358
00177 #define RESIZE_HEIGHT_OPT       359
00178 
00179 #define LOOP_TYPE               (Clp_MaxDefaultType + 1)
00180 #define DISPOSAL_TYPE           (Clp_MaxDefaultType + 2)
00181 #define DIMENSIONS_TYPE         (Clp_MaxDefaultType + 3)
00182 #define FRAME_SPEC_TYPE         (Clp_MaxDefaultType + 4)
00183 #define COLOR_TYPE              (Clp_MaxDefaultType + 5)
00184 #define POSITION_TYPE           (Clp_MaxDefaultType + 6)
00185 #define RECTANGLE_TYPE          (Clp_MaxDefaultType + 7)
00186 #define TWO_COLORS_TYPE         (Clp_MaxDefaultType + 8)
00187 #define COLORMAP_ALG_TYPE       (Clp_MaxDefaultType + 9)
00188 #define SCALE_FACTOR_TYPE       (Clp_MaxDefaultType + 10)
00189 
00190 Clp_Option options[] = {
00191   
00192   { "append", 0, APPEND_OPT, 0, 0 },
00193   { "app-extension", 'x', APP_EXTENSION_OPT, Clp_ArgString, 0 },
00194   
00195   { "background", 'B', BACKGROUND_OPT, COLOR_TYPE, Clp_Negate },
00196   { "batch", 'b', 'b', 0, 0 },
00197   { "bg", 0, BACKGROUND_OPT, COLOR_TYPE, Clp_Negate },
00198 
00199   { "careful", 0, CAREFUL_OPT, 0, Clp_Negate },
00200   { "change-color", 0, CHANGE_COLOR_OPT, TWO_COLORS_TYPE, Clp_Negate },
00201   { "cinfo", 0, COLOR_INFO_OPT, 0, Clp_Negate },
00202   { "clip", 0, CROP_OPT, RECTANGLE_TYPE, Clp_Negate },
00203   { "colors", 'k', COLORMAP_OPT, Clp_ArgInt, Clp_Negate },
00204   { "color-method", 0, COLORMAP_ALGORITHM_OPT, COLORMAP_ALG_TYPE, 0 },
00205   { "color-info", 0, COLOR_INFO_OPT, 0, Clp_Negate },
00206   { "comment", 'c', COMMENT_OPT, Clp_ArgString, 0 },
00207   { "no-comments", 'c', NO_COMMENTS_OPT, 0, Clp_OnlyNegated },
00208   { "crop", 0, CROP_OPT, RECTANGLE_TYPE, Clp_Negate },
00209   
00210   { "delay", 'd', 'd', Clp_ArgInt, Clp_Negate },
00211   { "delete", 0, DELETE_OPT, 0, 0 },
00212   { "disposal", 'D', DISPOSAL_OPT, DISPOSAL_TYPE, Clp_Negate },
00213   { "dither", 'f', DITHER_OPT, 0, Clp_Negate },
00214   { "done", 0, ALTER_DONE_OPT, 0, 0 },
00215   
00216   { "explode", 'e', 'e', 0, 0 },
00217   { "explode-by-name", 'E', 'E', 0, 0 },
00218   { "extension", 0, EXTENSION_OPT, Clp_ArgString, 0 },
00219   { "no-extensions", 'x', NO_EXTENSIONS_OPT, 0, 0 },
00220   { "extension-info", 0, EXTENSION_INFO_OPT, 0, Clp_Negate },
00221   
00222   { "flip-horizontal", 0, FLIP_HORIZ_OPT, 0, Clp_Negate },
00223   { "flip-vertical", 0, FLIP_VERT_OPT, 0, Clp_Negate },
00224   { "no-flip", 0, NO_FLIP_OPT, 0, 0 },
00225   
00226   { "help", 'h', HELP_OPT, 0, 0 },
00227   
00228   { "info", 'I', INFO_OPT, 0, Clp_Negate },  
00229   { "insert-before", 0, INSERT_OPT, FRAME_SPEC_TYPE, 0 },
00230   { "interlace", 'i', 'i', 0, Clp_Negate },
00231   
00232   { "logical-screen", 'S', LOGICAL_SCREEN_OPT, DIMENSIONS_TYPE, Clp_Negate },
00233   { "loopcount", 'l', 'l', LOOP_TYPE, Clp_Optional | Clp_Negate },
00234   
00235   { "merge", 'm', 'm', 0, 0 },
00236   { "method", 0, COLORMAP_ALGORITHM_OPT, COLORMAP_ALG_TYPE, 0 },
00237   
00238   { "name", 'n', NAME_OPT, Clp_ArgString, 0 },
00239   { "no-names", 'n', NO_NAME_OPT, 0, Clp_OnlyNegated },
00240   
00241   { "optimize", 'O', OPTIMIZE_OPT, Clp_ArgInt, Clp_Negate | Clp_Optional },
00242   { "output", 'o', OUTPUT_OPT, Clp_ArgStringNotOption, 0 },
00243   
00244   { "position", 'p', POSITION_OPT, POSITION_TYPE, Clp_Negate },
00245   
00246   { "replace", 0, REPLACE_OPT, FRAME_SPEC_TYPE, 0 },
00247   { "resize", 0, RESIZE_OPT, DIMENSIONS_TYPE, Clp_Negate },
00248   { "resize-width", 0, RESIZE_WIDTH_OPT, Clp_ArgUnsigned, Clp_Negate },
00249   { "resize-height", 0, RESIZE_HEIGHT_OPT, Clp_ArgUnsigned, Clp_Negate },
00250   { "resiz", 0, RESIZE_OPT, DIMENSIONS_TYPE, Clp_Negate },
00251   { "resi", 0, RESIZE_OPT, DIMENSIONS_TYPE, Clp_Negate },
00252   { "res", 0, RESIZE_OPT, DIMENSIONS_TYPE, Clp_Negate },
00253   { "rotate-90", 0, ROTATE_90_OPT, 0, 0 },
00254   { "rotate-180", 0, ROTATE_180_OPT, 0, 0 },
00255   { "rotate-270", 0, ROTATE_270_OPT, 0, 0 },
00256   { "no-rotate", 0, NO_ROTATE_OPT, 0, 0 },
00257 
00258   { "scale", 0, SCALE_OPT, SCALE_FACTOR_TYPE, Clp_Negate },
00259   { "screen", 0, LOGICAL_SCREEN_OPT, DIMENSIONS_TYPE, Clp_Negate },
00260   { "same-background", 0, SAME_BACKGROUND_OPT, 0, 0 },
00261   { "same-bg", 0, SAME_BACKGROUND_OPT, 0, 0 },
00262   { "same-clip", 0, SAME_CROP_OPT, 0, 0 },
00263   { "same-comments", 0, SAME_COMMENTS_OPT, 0, 0 },
00264   { "same-crop", 0, SAME_CROP_OPT, 0, 0 },
00265   { "same-extensions", 0, SAME_EXTENSIONS_OPT, 0, 0 },
00266   { "same-interlace", 0, SAME_INTERLACE_OPT, 0, 0 },
00267   { "same-logical-screen", 0, SAME_LOGICAL_SCREEN_OPT, 0, 0 },
00268   { "same-loopcount", 0, SAME_LOOPCOUNT_OPT, 0, 0 },
00269   { "same-disposal", 0, SAME_DISPOSAL_OPT, 0, 0 },
00270   { "same-delay", 0, SAME_DELAY_OPT, 0, 0 },
00271   { "same-names", 0, SAME_NAME_OPT, 0, 0 },
00272   { "same-position", 0, SAME_POSITION_OPT, 0, 0 },
00273   { "same-screen", 0, SAME_LOGICAL_SCREEN_OPT, 0, 0 },
00274   { "same-transparent", 0, SAME_TRANSPARENT_OPT, 0, 0 },
00275   
00276   { "transform-colormap", 0, COLOR_TRANSFORM_OPT, Clp_ArgStringNotOption,
00277     Clp_Negate },
00278   { "transparent", 't', 't', COLOR_TYPE, Clp_Negate },
00279   
00280   { "unoptimize", 'U', UNOPTIMIZE_OPT, 0, Clp_Negate },
00281   { "use-colormap", 0, USE_COLORMAP_OPT, Clp_ArgString, Clp_Negate },
00282   
00283   { "verbose", 'v', VERBOSE_OPT, 0, Clp_Negate },
00284   { "version", 0, VERSION_OPT, 0, 0 },
00285   
00286   { 0, 'w', NO_WARNINGS_OPT, 0, Clp_Negate },
00287   { "warnings", 0, WARNINGS_OPT, 0, Clp_Negate },
00288   
00289   { "xinfo", 0, EXTENSION_INFO_OPT, 0, Clp_Negate },
00290   
00291 };
00292 
00293 
00294 static void combine_output_options(void);
00295 static void initialize_def_frame(void);
00296 static void redundant_option_warning(const char *);
00297 
00298 
00299 static void
00300 set_mode(int newmode)
00301 {
00302   if (mode == BLANK_MODE)
00303     mode = newmode;
00304   else if (mode == newmode)
00305     ;
00306   else
00307     fatal_error("too late to change modes");
00308 }
00309 
00310 
00311 void
00312 set_frame_change(int kind)
00313 {
00314   int i;
00315   Gt_Frameset *fset;
00316   
00317   if (mode == BLANK_MODE)
00318     set_mode(MERGING);
00319   if (mode < DELETING && frames_done) {
00320     fatal_error("frame selection and frame changes don't mix");
00321     return;
00322   }
00323   assert(!nested_mode);
00324   nested_mode = mode;
00325   
00326   switch (kind) {
00327     
00328    case DELETE_OPT:
00329     mode = DELETING;
00330     break;
00331     
00332    case REPLACE_OPT:
00333     for (i = frame_spec_1; i < frame_spec_2; i++)
00334       FRAME(frames, i).use = 0;
00335     
00336     FRAME(frames, i).use = -1;
00337     
00338     
00339    case INSERT_OPT:
00340     
00341     fset = FRAME(frames, frame_spec_2).nest;
00342     if (!fset) fset = new_frameset(8);
00343     FRAME(frames, frame_spec_2).nest = fset;
00344     
00345     
00346     mode = INSERTING;
00347     nested_frames = frames;
00348     frames = fset;
00349     break;
00350     
00351    case APPEND_OPT:
00352     
00353     mode = INSERTING;
00354     break;
00355     
00356   }
00357 }
00358 
00359 void
00360 frame_change_done(void)
00361 {
00362   if (nested_mode)
00363     mode = nested_mode;
00364   if (nested_frames)
00365     frames = nested_frames;
00366   nested_mode = 0;
00367   nested_frames = 0;
00368 }
00369 
00370 
00371 void
00372 show_frame(int imagenumber, int usename)
00373 {
00374   Gif_Image *gfi;
00375   Gt_Frame *frame;
00376   
00377   if (!input) return;
00378   gfi = Gif_GetImage(input, imagenumber);
00379   if (!gfi) return;
00380   
00381   switch (mode) {
00382     
00383    case MERGING:
00384    case INSERTING:
00385    case EXPLODING:
00386     if (!frames_done) clear_frameset(frames, first_input_frame);
00387     frame = add_frame(frames, -1, input, gfi);
00388     if (usename) frame->explode_by_name = 1;
00389     break;
00390     
00391    case BATCHING:
00392     add_frame(frames, first_input_frame + imagenumber, input, gfi);
00393     break;
00394     
00395    case DELETING:
00396     frame = &FRAME(frames, first_input_frame + imagenumber);
00397     frame->use = 0;
00398     break;
00399     
00400   }
00401   
00402   next_frame = 0;
00403   frames_done = 1;
00404 }
00405 
00406 
00407 
00408 
00409 
00410 
00411 static int gifread_error_count;
00412 
00413 static void
00414 gifread_error(const char *message, int which_image, void *thunk)
00415 {
00416   static int last_which_image = 0;
00417   static char last_message[256];
00418   static int different_error_count = 0;
00419   static int same_error_count = 0;
00420   const char *filename = (const char *)thunk;
00421   
00422   if (gifread_error_count == 0) {
00423     last_which_image = -1;
00424     last_message[0] = 0;
00425     different_error_count = 0;
00426   }
00427   
00428   gifread_error_count++;
00429   if (last_message[0] && different_error_count <= 10
00430       && (last_which_image != which_image || message == 0
00431           || strcmp(message, last_message) != 0)) {
00432     if (same_error_count == 1)
00433       error("  %s", last_message);
00434     else if (same_error_count > 0)
00435       error("  %s (%d times)", last_message, same_error_count);
00436     same_error_count = 0;
00437     last_message[0] = 0;
00438   }
00439 
00440   if (last_message[0] == 0)
00441     different_error_count++;
00442   
00443   same_error_count++;
00444   if (message)
00445     strcpy(last_message, message);
00446   else
00447     last_message[0] = 0;
00448   if (last_which_image != which_image && different_error_count <= 10
00449       && message) {
00450     error("Error while reading `%s' frame #%d:", filename, which_image);
00451     last_which_image = which_image;
00452   }
00453   
00454   if (different_error_count == 11 && message) {
00455     error("(more errors while reading `%s')", filename);
00456     different_error_count++;
00457   }
00458 }
00459 
00460 void
00461 input_stream(char *name)
00462 {
00463   FILE *f;
00464   Gif_Stream *gfs;
00465   int i;
00466   int saved_next_frame = next_frame;
00467   Gt_Frame old_def_frame;
00468   
00469   input = 0;
00470   input_name = name;
00471   frames_done = 0;
00472   next_frame = 0;
00473   next_input = 0;
00474   if (next_output) combine_output_options();
00475   files_given++;
00476   
00477   if (name == 0 || strcmp(name, "-") == 0) {
00478 #if defined(_MSDOS) || defined(_WIN32)
00479     _setmode(_fileno(stdin), _O_BINARY);
00480 #elif defined(__EMX__)
00481     _fsetmode(stdin, "b");
00482 #endif
00483     f = stdin;
00484     name = "<stdin>";
00485   } else
00486     f = fopen(name, "rb");
00487   if (!f) {
00488     error("%s: %s", name, strerror(errno));
00489     return;
00490   }
00491   
00492   
00493   i = getc(f);
00494   if (i == EOF) {
00495     error("%s: empty file", name);
00496     return;
00497   }
00498   ungetc(i, f);
00499   
00500   if (verbosing) verbose_open('<', name);
00501   gifread_error_count = 0;
00502   gfs = Gif_FullReadFile(f, gif_read_flags | GIF_READ_COMPRESSED,
00503                          gifread_error, (void *)name);
00504   fclose(f);
00505   gifread_error(0, -1, (void *)name); 
00506   
00507   if (!gfs || (Gif_ImageCount(gfs) == 0 && gfs->errors > 0)) {
00508     error("%s: not a GIF", name);
00509     Gif_DeleteStream(gfs);
00510     if (verbosing) verbose_close('>');
00511     return;
00512   }
00513   
00514   input = gfs;
00515   
00516   
00517   if (mode == BLANK_MODE)
00518     set_mode(MERGING);
00519   
00520   if (active_output_data.output_name == 0) {
00521     
00522 
00523     if (mode == BATCHING)
00524       active_output_data.output_name = input_name;
00525     else if (mode == EXPLODING) {
00526       
00527       char *explode_name = (input_name ? input_name : "#stdin#");
00528       char *slash = strrchr(explode_name, PATHNAME_SEPARATOR);
00529       if (slash)
00530         active_output_data.output_name = slash + 1;
00531       else
00532         active_output_data.output_name = explode_name;
00533     }
00534   }
00535   
00536   
00537 
00538 
00539 
00540 
00541 
00542 
00543 
00544 
00545 
00546 
00547 
00548 
00549 
00550 
00551 
00552 
00553 
00554   
00555   
00556   if (!CHANGED(saved_next_frame, CH_NAME))
00557     def_frame.name = 0;
00558   if (!CHANGED(saved_next_frame, CH_COMMENT))
00559     def_frame.comment = 0;
00560   if (!CHANGED(saved_next_frame, CH_EXTENSION))
00561     def_frame.extensions = 0;
00562   def_frame.input_filename = input_name;
00563   
00564   old_def_frame = def_frame;
00565   first_input_frame = frames->count;
00566   if (gfs->nimages > 1)
00567     def_frame.position_is_offset = 1;
00568   for (i = 0; i < gfs->nimages; i++)
00569     add_frame(frames, -1, gfs, gfs->images[i]);
00570   def_frame = old_def_frame;
00571   
00572   if (unoptimizing)
00573     if (!Gif_Unoptimize(gfs)) {
00574       static int context = 0;
00575       warning("`%s' is too complex to unoptimize", name);
00576       if (!context) {
00577         warncontext("(The reason was local color tables or complex transparency.");
00578         warncontext("Try running the GIF through `gifsicle --colors=255' first.)");
00579       }
00580       context = 1;
00581     }
00582   
00583   apply_color_transforms(input_transforms, gfs);
00584   gfs->refcount++;
00585 }
00586 
00587 void
00588 input_done(void)
00589 {
00590   if (!input) return;
00591   
00592   if (verbosing) verbose_close('>');
00593   
00594 
00595 
00596 
00597 
00598 
00599 
00600 
00601 
00602   
00603   Gif_DeleteStream(input);
00604   input = 0;
00605   
00606   if (mode == DELETING)
00607     frame_change_done();
00608   if (mode == BATCHING || mode == EXPLODING)
00609     output_frames();
00610 }
00611 
00612 
00613 
00614 
00615 
00616 
00617 static void
00618 set_new_fixed_colormap(char *name)
00619 {
00620   int i;
00621   if (name && strcmp(name, "web") == 0) {
00622     Gif_Colormap *cm = Gif_NewFullColormap(216, 256);
00623     Gif_Color *col = cm->col;
00624     for (i = 0; i < 216; i++) {
00625       col[i].red = (i / 36) * 0x33;
00626       col[i].green = ((i / 6) % 6) * 0x33;
00627       col[i].blue = (i % 6) * 0x33;
00628     }
00629     def_output_data.colormap_fixed = cm;
00630     
00631   } else if (name && (strcmp(name, "gray") == 0
00632                       || strcmp(name, "grey") == 0)) {
00633     Gif_Colormap *cm = Gif_NewFullColormap(256, 256);
00634     Gif_Color *col = cm->col;
00635     for (i = 0; i < 256; i++)
00636       col[i].red = col[i].green = col[i].blue = i;
00637     def_output_data.colormap_fixed = cm;
00638     
00639   } else if (name && strcmp(name, "bw") == 0) {
00640     Gif_Colormap *cm = Gif_NewFullColormap(2, 256);
00641     cm->col[0].red = cm->col[0].green = cm->col[0].blue = 0;
00642     cm->col[1].red = cm->col[1].green = cm->col[1].blue = 255;
00643     def_output_data.colormap_fixed = cm;
00644     
00645   } else
00646     def_output_data.colormap_fixed = read_colormap_file(name, 0);
00647 }
00648 
00649 static void
00650 do_set_colormap(Gif_Stream *gfs, Gif_Colormap *gfcm)
00651 {
00652   colormap_image_func image_func;
00653   if (active_output_data.colormap_dither)
00654     image_func = colormap_image_floyd_steinberg;
00655   else
00656     image_func = colormap_image_posterize;
00657   colormap_stream(gfs, gfcm, image_func);
00658 }
00659 
00660 static void
00661 do_colormap_change(Gif_Stream *gfs)
00662 {
00663   if (active_output_data.colormap_fixed)
00664     do_set_colormap(gfs, active_output_data.colormap_fixed);
00665   
00666   if (active_output_data.colormap_size > 0) {
00667     int nhist;
00668     Gif_Color *hist;
00669     Gif_Colormap *(*adapt_func)(Gif_Color *, int, int);
00670     Gif_Colormap *new_cm;
00671     
00672     
00673     {
00674       int i, any_locals = 0;
00675       for (i = 0; i < gfs->nimages; i++)
00676         if (gfs->images[i]->local)
00677           any_locals = 1;
00678       hist = histogram(gfs, &nhist);
00679       if (nhist <= active_output_data.colormap_size && !any_locals) {
00680         warncontext("trivial adaptive palette (only %d colors in source)", nhist);
00681         return;
00682       }
00683     }
00684     
00685     switch (active_output_data.colormap_algorithm) {
00686       
00687      case COLORMAP_DIVERSITY:
00688       adapt_func = &colormap_flat_diversity;
00689       break;
00690       
00691      case COLORMAP_BLEND_DIVERSITY:
00692       adapt_func = &colormap_blend_diversity;
00693       break;
00694       
00695      case COLORMAP_MEDIAN_CUT:
00696       adapt_func = &colormap_median_cut;
00697       break;
00698       
00699      default:
00700       fatal_error("can't happen");
00701       
00702     }
00703     
00704     new_cm = (*adapt_func)(hist, nhist, active_output_data.colormap_size);
00705     do_set_colormap(gfs, new_cm);
00706     
00707     Gif_DeleteArray(hist);
00708     Gif_DeleteColormap(new_cm);
00709   }
00710 }
00711 
00712 
00713 
00714 
00715 
00716 
00717 static void
00718 write_stream(char *output_name, Gif_Stream *gfs)
00719 {
00720   FILE *f;
00721   
00722   if (output_name)
00723     f = fopen(output_name, "wb");
00724   else {
00725 #ifndef OUTPUT_GIF_TO_TERMINAL
00726     extern int isatty(int);
00727     if (isatty(fileno(stdout))) {
00728       error("not writing to <stdout>: it's a terminal");
00729       return;
00730     }
00731 #endif
00732 #if defined(_MSDOS) || defined(_WIN32)
00733     _setmode(_fileno(stdout), _O_BINARY);
00734 #elif defined(__EMX__)
00735     _fsetmode(stdout, "b");
00736 #endif
00737     f = stdout;
00738     output_name = "<stdout>";
00739   }
00740   
00741   if (f) {
00742     Gif_FullWriteFile(gfs, gif_write_flags, f);
00743     fclose(f);
00744     any_output_successful = 1;
00745   } else
00746     error("%s: %s", output_name, strerror(errno));
00747 }
00748 
00749 static void
00750 merge_and_write_frames(char *outfile, int f1, int f2)
00751 {
00752   Gif_Stream *out;
00753   int compress_immediately;
00754   int colormap_change;
00755   assert(!nested_mode);
00756   if (verbosing) verbose_open('[', outfile ? outfile : "#stdout#");
00757   
00758   colormap_change = active_output_data.colormap_size > 0
00759     || active_output_data.colormap_fixed;
00760   compress_immediately = !colormap_change
00761     && active_output_data.scaling == 0
00762     && active_output_data.optimizing <= 0;
00763   warn_local_colormaps = !colormap_change;
00764   
00765   out = merge_frame_interval(frames, f1, f2, &active_output_data,
00766                              compress_immediately);
00767   
00768   if (out) {
00769     if (active_output_data.scaling == 1)
00770       resize_stream(out, active_output_data.resize_width,
00771                     active_output_data.resize_height);
00772     else if (active_output_data.scaling == 2)
00773       resize_stream(out, active_output_data.scale_x * out->screen_width,
00774                     active_output_data.scale_y * out->screen_height);
00775     if (colormap_change)
00776       do_colormap_change(out);
00777     if (output_transforms)
00778       apply_color_transforms(output_transforms, out);
00779     if (active_output_data.optimizing > 0)
00780       optimize_fragments(out, active_output_data.optimizing);
00781     write_stream(outfile, out);
00782     Gif_DeleteStream(out);
00783   }
00784   
00785   if (verbosing) verbose_close(']');
00786 }
00787 
00788 static void
00789 output_information(const char *outfile)
00790 {
00791   FILE *f;
00792   int i, j;
00793   Gt_Frame *fr;
00794   Gif_Stream *gfs;
00795   
00796   if (infoing == 2)
00797     f = stderr;
00798   else if (outfile == 0)
00799     f = stdout;
00800   else {
00801     f = fopen(outfile, "w");
00802     if (!f) {
00803       error("%s: %s", outfile, strerror(errno));
00804       return;
00805     }
00806   }
00807   
00808   for (i = 0; i < frames->count; i++)
00809     FRAME(frames, i).stream->userflags = 97;
00810 
00811   for (i = 0; i < frames->count; i++)
00812     if (FRAME(frames, i).stream->userflags == 97) {
00813       fr = &FRAME(frames, i);
00814       gfs = fr->stream;
00815       gfs->userflags = 0;
00816       stream_info(f, gfs, fr->input_filename, fr->colormap_info,
00817                   fr->extensions_info);
00818       for (j = i; j < frames->count; j++)
00819         if (FRAME(frames, j).stream == gfs) {
00820           fr = &FRAME(frames, j);
00821           image_info(f, gfs, fr->image, fr->colormap_info);
00822         }
00823     }
00824   
00825   if (f != stderr && f != stdout)
00826     fclose(f);
00827 }
00828 
00829 void
00830 output_frames(void)
00831 {
00832   
00833 
00834 
00835 
00836   int i;
00837   char *outfile = active_output_data.output_name;
00838   active_output_data.output_name = 0;
00839   
00840   
00841   if (infoing)
00842     output_information(outfile);
00843   
00844   if (infoing != 1 && frames->count > 0)
00845     switch (mode) {
00846       
00847      case MERGING:
00848      case BATCHING:
00849       merge_and_write_frames(outfile, 0, -1);
00850       break;
00851       
00852      case EXPLODING: {
00853        
00854 
00855 
00856        int max_nimages = 0;
00857        for (i = 0; i < frames->count; i++) {
00858          Gt_Frame *fr = &FRAME(frames, i);
00859          if (fr->stream->nimages > max_nimages)
00860            max_nimages = fr->stream->nimages;
00861        }
00862        
00863        if (!outfile) 
00864          outfile = "-";
00865        
00866        for (i = 0; i < frames->count; i++) {
00867          Gt_Frame *fr = &FRAME(frames, i);
00868          int imagenumber = Gif_ImageNumber(fr->stream, fr->image);
00869          char *explodename;
00870          
00871          char *imagename = 0;
00872          if (fr->explode_by_name)
00873            imagename = fr->name ? fr->name : fr->image->identifier;
00874          
00875          explodename = explode_filename(outfile, imagenumber, imagename,
00876                                         max_nimages);
00877          merge_and_write_frames(explodename, i, i);
00878        }
00879        break;
00880      }
00881      
00882      case INSERTING:
00883       
00884       break;
00885       
00886     }
00887   
00888   active_next_output = 0;
00889   clear_frameset(frames, 0);
00890   
00891   
00892 
00893   if (def_frame.crop)
00894     def_frame.crop->ready = 0;
00895 }
00896 
00897 
00898 
00899 
00900 
00901 
00902 int
00903 frame_argument(Clp_Parser *clp, char *arg)
00904 {
00905   
00906   int val = parse_frame_spec(clp, arg, -1, 0);
00907   if (val == -97)
00908     return 0;
00909   else if (val > 0) {
00910     int i;
00911     for (i = frame_spec_1; i <= frame_spec_2; i++)
00912       show_frame(i, frame_spec_name != 0);
00913     if (next_output) combine_output_options();
00914     return 1;
00915   } else
00916     return 1;
00917 }
00918 
00919 static int
00920 handle_extension(Clp_Parser *clp, int is_app)
00921 {
00922   Gif_Extension *gfex;
00923   char *extension_type = clp->arg;
00924   char *extension_body = Clp_Shift(clp, 1);
00925   if (!extension_body) {
00926     Clp_OptionError(clp, "%O requires two arguments");
00927     return 0;
00928   }
00929 
00930   UNCHECKED_MARK_CH(frame, CH_EXTENSION);
00931   if (is_app)
00932     gfex = Gif_NewExtension(255, extension_type);
00933   else if (!isdigit(extension_type[0]) && extension_type[1] == 0)
00934     gfex = Gif_NewExtension(extension_type[0], 0);
00935   else {
00936     long l = strtol(extension_type, &extension_type, 0);
00937     if (*extension_type != 0 || l < 0 || l >= 256)
00938       fatal_error("bad extension type: must be a number between 0 and 255");
00939     gfex = Gif_NewExtension(l, 0);
00940   }
00941   
00942   gfex->data = (byte *)extension_body;
00943   gfex->length = strlen(extension_body);
00944   gfex->next = def_frame.extensions;
00945   def_frame.extensions = gfex;
00946   
00947   return 1;
00948 }
00949 
00950 
00951 
00952 
00953 
00954 
00955 static void
00956 initialize_def_frame(void)
00957 {
00958   
00959   def_frame.stream = 0;
00960   def_frame.image = 0;
00961   def_frame.use = 1;
00962   
00963   def_frame.name = 0;
00964   def_frame.no_name = 0;
00965   def_frame.comment = 0;
00966   def_frame.no_comments = 0;
00967   
00968   def_frame.interlacing = -1;
00969   def_frame.transparent.haspixel = 0;
00970   def_frame.left = -1;
00971   def_frame.top = -1;
00972   def_frame.position_is_offset = 0;
00973   
00974   def_frame.crop = 0;
00975   
00976   def_frame.delay = -1;
00977   def_frame.disposal = -1;
00978   
00979   def_frame.nest = 0;
00980   def_frame.explode_by_name = 0;
00981   
00982   def_frame.no_extensions = 0;
00983   def_frame.extensions = 0;
00984   
00985   def_frame.flip_horizontal = 0;
00986   def_frame.flip_vertical = 0;
00987   
00988   
00989   def_output_data.output_name = 0;
00990   
00991   def_output_data.screen_width = -1;
00992   def_output_data.screen_height = -1;
00993   def_output_data.background.haspixel = 0;
00994   def_output_data.loopcount = -2;
00995   
00996   def_output_data.colormap_size = 0;
00997   def_output_data.colormap_fixed = 0;
00998   def_output_data.colormap_algorithm = COLORMAP_DIVERSITY;
00999   def_output_data.colormap_dither = 0;
01000   
01001   def_output_data.optimizing = 0;
01002   def_output_data.scaling = 0;
01003   
01004   active_output_data = def_output_data;
01005 }
01006 
01007 static void
01008 combine_output_options(void)
01009 {
01010   int recent = next_output;
01011   next_output = active_next_output;
01012 #define COMBINE_ONE_OUTPUT_OPTION(value, field)         \
01013   if (CHANGED(recent, value)) {                         \
01014     MARK_CH(output, value);                             \
01015     active_output_data.field = def_output_data.field;   \
01016   }
01017   
01018   COMBINE_ONE_OUTPUT_OPTION(CH_OUTPUT, output_name);
01019   
01020   if (CHANGED(recent, CH_LOGICAL_SCREEN)) {
01021     MARK_CH(output, CH_LOGICAL_SCREEN);
01022     active_output_data.screen_width = def_output_data.screen_width;
01023     active_output_data.screen_height = def_output_data.screen_height;
01024   }
01025   COMBINE_ONE_OUTPUT_OPTION(CH_BACKGROUND, background);
01026   COMBINE_ONE_OUTPUT_OPTION(CH_LOOPCOUNT, loopcount);
01027   
01028   COMBINE_ONE_OUTPUT_OPTION(CH_OPTIMIZE, optimizing);
01029   COMBINE_ONE_OUTPUT_OPTION(CH_COLORMAP, colormap_size);
01030   COMBINE_ONE_OUTPUT_OPTION(CH_COLORMAP_METHOD, colormap_algorithm);
01031   if (CHANGED(recent, CH_USE_COLORMAP)) {
01032     MARK_CH(output, CH_USE_COLORMAP);
01033     if (def_output_data.colormap_fixed)
01034       def_output_data.colormap_fixed->refcount++;
01035     Gif_DeleteColormap(active_output_data.colormap_fixed);
01036     active_output_data.colormap_fixed = def_output_data.colormap_fixed;
01037   }
01038   COMBINE_ONE_OUTPUT_OPTION(CH_DITHER, colormap_dither);
01039   
01040   if (CHANGED(recent, CH_RESIZE)) {
01041     MARK_CH(output, CH_RESIZE);
01042     active_output_data.scaling = def_output_data.scaling;
01043     active_output_data.resize_width = def_output_data.resize_width;
01044     active_output_data.resize_height = def_output_data.resize_height;
01045     active_output_data.scale_x = def_output_data.scale_x;
01046     active_output_data.scale_y = def_output_data.scale_y;
01047   }
01048   
01049   def_output_data.colormap_fixed = 0;
01050   def_output_data.output_name = 0;
01051   
01052   active_next_output |= next_output;
01053   next_output = 0;
01054 }
01055 
01056 static void
01057 redundant_option_warning(const char *option_type)
01058 {
01059   static int context = 0;
01060   warning("redundant %s option", option_type);
01061   if (!context) {
01062     warncontext("(The %s option was overridden by another %s option",
01063                 option_type, option_type);
01064     warncontext("before it had any effect.)");
01065   }
01066   context = 1;
01067 }
01068 
01069 static void
01070 print_useless_options(const char *type_name, int value, const char *names[])
01071 {
01072   int explanation_printed = 0;
01073   int i;
01074   if (!value) return;
01075   for (i = 0; i < 32; i++)
01076     if (CHANGED(value, i)) {
01077       warning("useless %s-related %s option", names[i], type_name);
01078       if (!explanation_printed)
01079         warncontext("(It didn't affect any %s.)", type_name);
01080       explanation_printed = 1;
01081     }
01082 }
01083 
01084 
01085 
01086 
01087 
01088 
01089 int
01090 main(int argc, char **argv)
01091 {
01092   Clp_Parser *clp =
01093     Clp_NewParser(argc, argv, sizeof(options) / sizeof(options[0]), options);
01094   
01095   Clp_AddStringListType
01096     (clp, LOOP_TYPE, Clp_AllowNumbers,
01097      "infinite", 0, "forever", 0,
01098      0);
01099   Clp_AddStringListType
01100     (clp, DISPOSAL_TYPE, Clp_AllowNumbers,
01101      "none", GIF_DISPOSAL_NONE,
01102      "asis", GIF_DISPOSAL_ASIS,
01103      "background", GIF_DISPOSAL_BACKGROUND,
01104      "bg", GIF_DISPOSAL_BACKGROUND,
01105      "previous", GIF_DISPOSAL_ASIS,
01106      0);
01107   Clp_AddStringListType
01108     (clp, COLORMAP_ALG_TYPE, 0,
01109      "diversity", COLORMAP_DIVERSITY,
01110      "blend-diversity", COLORMAP_BLEND_DIVERSITY,
01111      "median-cut", COLORMAP_MEDIAN_CUT,
01112      0);
01113   Clp_AddType(clp, DIMENSIONS_TYPE, 0, parse_dimensions, 0);
01114   Clp_AddType(clp, POSITION_TYPE, 0, parse_position, 0);
01115   Clp_AddType(clp, SCALE_FACTOR_TYPE, 0, parse_scale_factor, 0);
01116   Clp_AddType(clp, FRAME_SPEC_TYPE, 0, parse_frame_spec, 0);
01117   Clp_AddType(clp, COLOR_TYPE, Clp_DisallowOptions, parse_color, 0);
01118   Clp_AddType(clp, RECTANGLE_TYPE, 0, parse_rectangle, 0);
01119   Clp_AddType(clp, TWO_COLORS_TYPE, Clp_DisallowOptions, parse_two_colors, 0);
01120   Clp_SetOptionChar(clp, '+', Clp_ShortNegated);
01121   Clp_SetErrorHandler(clp, clp_error_handler);
01122   
01123   program_name = Clp_ProgramName(clp);
01124   
01125   frames = new_frameset(16);
01126   initialize_def_frame();
01127   
01128 #ifdef DMALLOC
01129   dmalloc_verbose("fudge");
01130 #endif
01131   
01132   
01133 
01134 
01135 
01136 
01137   {
01138     u_int16_t m = 0xFFFFU;
01139     int i = m;
01140     assert(i > 0 && "configuration/lameness failure! bug the author!");
01141   }
01142   
01143   while (1) {
01144     int opt = Clp_Next(clp);
01145     switch (opt) {
01146       
01147       
01148       
01149      case 'b':
01150       set_mode(BATCHING);
01151       break;
01152       
01153      case 'm':
01154       set_mode(MERGING);
01155       break;
01156       
01157      case 'e':
01158       set_mode(EXPLODING);
01159       def_frame.explode_by_name = 0;
01160       break;
01161       
01162      case 'E':
01163       set_mode(EXPLODING);
01164       def_frame.explode_by_name = 1;
01165       break;
01166       
01167       
01168       
01169      case INFO_OPT:
01170       if (clp->negated)
01171         infoing = 0;
01172       else
01173         
01174 
01175         infoing = (infoing == 1 ? 2 : 1);
01176       break;
01177       
01178      case COLOR_INFO_OPT:
01179       if (clp->negated)
01180         def_frame.colormap_info = 0;
01181       else {
01182         def_frame.colormap_info = 1;
01183         if (!infoing) infoing = 1;
01184       }
01185       break;
01186       
01187      case EXTENSION_INFO_OPT:
01188       if (clp->negated)
01189         def_frame.extensions_info = 0;
01190       else {
01191         def_frame.extensions_info = 1;
01192         if (!infoing) infoing = 1;
01193       }
01194       break;
01195       
01196      case VERBOSE_OPT:
01197       verbosing = clp->negated ? 0 : 1;
01198       break;
01199       
01200       
01201       
01202      case DELETE_OPT:
01203      case REPLACE_OPT:
01204      case INSERT_OPT:
01205      case APPEND_OPT:
01206       frame_change_done();
01207       set_frame_change(opt);
01208       break;
01209       
01210      case ALTER_DONE_OPT:
01211       frame_change_done();
01212       break;
01213       
01214       
01215       
01216      case NAME_OPT:
01217       if (clp->negated) goto no_names;
01218       MARK_CH(frame, CH_NAME);
01219       def_frame.name = clp->arg;
01220       break;
01221       
01222      no_names:
01223      case NO_NAME_OPT:
01224       MARK_CH(frame, CH_NAME);
01225       def_frame.no_name = 1;
01226       def_frame.name = 0;
01227       break;
01228       
01229      case SAME_NAME_OPT:
01230       def_frame.no_name = 0;
01231       def_frame.name = 0;
01232       break;
01233       
01234      case COMMENT_OPT:
01235       if (clp->negated) goto no_comments;
01236       MARK_CH(frame, CH_COMMENT);
01237       if (!def_frame.comment) def_frame.comment = Gif_NewComment();
01238       Gif_AddComment(def_frame.comment, clp->arg, -1);
01239       break;
01240       
01241      no_comments:
01242      case NO_COMMENTS_OPT:
01243       Gif_DeleteComment(def_frame.comment);
01244       def_frame.comment = 0;
01245       def_frame.no_comments = 1;
01246       break;
01247       
01248      case SAME_COMMENTS_OPT:
01249       def_frame.no_comments = 0;
01250       break;
01251       
01252      case 'i':
01253       MARK_CH(frame, CH_INTERLACE);
01254       def_frame.interlacing = clp->negated ? 0 : 1;
01255       break;
01256       
01257      case SAME_INTERLACE_OPT:
01258       def_frame.interlacing = -1;
01259       break;
01260       
01261      case POSITION_OPT:
01262       MARK_CH(frame, CH_POSITION);
01263       def_frame.left = clp->negated ? 0 : position_x;
01264       def_frame.top = clp->negated ? 0 : position_y;
01265       break;
01266       
01267      case SAME_POSITION_OPT:
01268       def_frame.left = -1;
01269       def_frame.top = -1;
01270       break;
01271       
01272      case 't':
01273       MARK_CH(frame, CH_TRANSPARENT);
01274       if (clp->negated)
01275         def_frame.transparent.haspixel = 255;
01276       else {
01277         def_frame.transparent = parsed_color;
01278         def_frame.transparent.haspixel = parsed_color.haspixel ? 2 : 1;
01279       }
01280       break;
01281       
01282      case SAME_TRANSPARENT_OPT:
01283       def_frame.transparent.haspixel = 0;
01284       break;
01285       
01286      case BACKGROUND_OPT:
01287       MARK_CH(output, CH_BACKGROUND);
01288       if (clp->negated) {
01289         def_output_data.background.haspixel = 2;
01290         def_output_data.background.pixel = 0;
01291       } else {
01292         def_output_data.background = parsed_color;
01293         def_output_data.background.haspixel = parsed_color.haspixel ? 2 : 1;
01294       }
01295       break;
01296       
01297      case SAME_BACKGROUND_OPT:
01298       MARK_CH(output, CH_BACKGROUND);
01299       def_output_data.background.haspixel = 0;
01300       break;
01301       
01302      case LOGICAL_SCREEN_OPT:
01303       MARK_CH(output, CH_LOGICAL_SCREEN);
01304       if (clp->negated)
01305         def_output_data.screen_width = def_output_data.screen_height = 0;
01306       else {
01307         def_output_data.screen_width = dimensions_x;
01308         def_output_data.screen_height = dimensions_y;
01309       }
01310       break;
01311       
01312      case SAME_LOGICAL_SCREEN_OPT:
01313       MARK_CH(output, CH_LOGICAL_SCREEN);
01314       def_output_data.screen_width = def_output_data.screen_height = -1;
01315       break;
01316       
01317      case CROP_OPT:
01318       if (clp->negated) goto no_crop;
01319       MARK_CH(frame, CH_CROP);
01320       {
01321         Gt_Crop *crop = Gif_New(Gt_Crop);
01322         
01323         crop->ready = 0;
01324         crop->whole_stream = 0;
01325         crop->spec_x = position_x;
01326         crop->spec_y = position_y;
01327         crop->spec_w = dimensions_x;
01328         crop->spec_h = dimensions_y;
01329         def_frame.crop = crop;
01330       }
01331       break;
01332       
01333      no_crop:
01334      case SAME_CROP_OPT:
01335       def_frame.crop = 0;
01336       break;
01337       
01338       
01339       
01340      case NO_EXTENSIONS_OPT:
01341       def_frame.no_extensions = 1;
01342       break;
01343       
01344      case SAME_EXTENSIONS_OPT:
01345       def_frame.no_extensions = 0;
01346       break;
01347       
01348      case EXTENSION_OPT:
01349       if (!handle_extension(clp, 0))
01350         goto bad_option;
01351       break;
01352       
01353      case APP_EXTENSION_OPT:
01354       if (!handle_extension(clp, 1))
01355         goto bad_option;
01356       break;
01357       
01358       
01359       
01360      case FLIP_HORIZ_OPT:
01361       MARK_CH(frame, CH_FLIP);
01362       def_frame.flip_horizontal = !clp->negated;
01363       break;
01364       
01365      case FLIP_VERT_OPT:
01366       MARK_CH(frame, CH_FLIP);
01367       def_frame.flip_vertical = !clp->negated;
01368       break;
01369        
01370      case NO_FLIP_OPT:
01371       def_frame.flip_horizontal = def_frame.flip_vertical = 0;
01372       break;
01373       
01374      case NO_ROTATE_OPT:
01375       def_frame.rotation = 0;
01376       break;
01377        
01378      case ROTATE_90_OPT:
01379       MARK_CH(frame, CH_ROTATE);
01380       def_frame.rotation = 1;
01381       break;
01382        
01383      case ROTATE_180_OPT:
01384       MARK_CH(frame, CH_ROTATE);
01385       def_frame.rotation = 2;
01386       break;
01387       
01388      case ROTATE_270_OPT:
01389       MARK_CH(frame, CH_ROTATE);
01390       def_frame.rotation = 3;
01391       break;
01392        
01393       
01394       
01395      case 'd':
01396       MARK_CH(frame, CH_DELAY);
01397       def_frame.delay = clp->negated ? 0 : clp->val.i;
01398       break;
01399       
01400      case SAME_DELAY_OPT:
01401       def_frame.delay = -1;
01402       break;
01403       
01404      case DISPOSAL_OPT:
01405       MARK_CH(frame, CH_DISPOSAL);
01406       if (clp->negated)
01407         def_frame.disposal = GIF_DISPOSAL_NONE;
01408       else if (clp->val.i < 0 || clp->val.i > 7)
01409         error("disposal must be between 0 and 7");
01410       else
01411         def_frame.disposal = clp->val.i;
01412       break;
01413       
01414      case SAME_DISPOSAL_OPT:
01415       def_frame.disposal = -1;
01416       break;
01417       
01418      case 'l':
01419       MARK_CH(output, CH_LOOPCOUNT);
01420       if (clp->negated)
01421         def_output_data.loopcount = -1;
01422       else
01423         def_output_data.loopcount = (clp->have_arg ? clp->val.i : 0);
01424       break;
01425       
01426      case SAME_LOOPCOUNT_OPT:
01427       MARK_CH(output, CH_LOOPCOUNT);
01428       def_output_data.loopcount = -2;
01429       break;
01430       
01431      case OPTIMIZE_OPT:
01432       MARK_CH(output, CH_OPTIMIZE);
01433       if (clp->negated)
01434         def_output_data.optimizing = 0;
01435       else
01436         def_output_data.optimizing = (clp->have_arg ? clp->val.i : 1);
01437       break;
01438       
01439      case UNOPTIMIZE_OPT:
01440       UNCHECKED_MARK_CH(input, CH_UNOPTIMIZE);
01441       unoptimizing = clp->negated ? 0 : 1;
01442       break;
01443       
01444       
01445 
01446      case CAREFUL_OPT: {
01447        if (clp->negated)
01448          gif_read_flags = gif_write_flags = 0;
01449        else {
01450          gif_read_flags = 0;
01451          gif_write_flags = GIF_WRITE_CAREFUL_MIN_CODE_SIZE;
01452        }
01453        break;
01454      }
01455       
01456      case CHANGE_COLOR_OPT: {
01457        next_input |= CH_CHANGE_COLOR;
01458        if (clp->negated)
01459          input_transforms = delete_color_transforms
01460            (input_transforms, &color_change_transformer);
01461        else if (parsed_color2.haspixel)
01462          error("COLOR2 must be in RGB format in `--change-color COLOR1 COLOR2'");
01463        else
01464          input_transforms = append_color_change
01465            (input_transforms, parsed_color, parsed_color2);
01466        break;
01467      }
01468      
01469      case COLOR_TRANSFORM_OPT:
01470       next_output |= CH_COLOR_TRANSFORM;
01471       if (clp->negated)
01472         output_transforms = delete_color_transforms
01473           (output_transforms, &pipe_color_transformer);
01474       else
01475         output_transforms = append_color_transform
01476           (output_transforms, &pipe_color_transformer, clp->arg);
01477       break;
01478       
01479      case COLORMAP_OPT:
01480       MARK_CH(output, CH_COLORMAP);
01481       if (clp->negated)
01482         def_output_data.colormap_size = 0;
01483       else {
01484         def_output_data.colormap_size = clp->val.i;
01485         if (def_output_data.colormap_size < 2
01486             || def_output_data.colormap_size > 256) {
01487           Clp_OptionError(clp, "argument to `%O' must be between 2 and 256");
01488           def_output_data.colormap_size = 0;
01489         }
01490       }
01491       break;
01492       
01493      case USE_COLORMAP_OPT:
01494       MARK_CH(output, CH_USE_COLORMAP);
01495       Gif_DeleteColormap(def_output_data.colormap_fixed);
01496       if (clp->negated)
01497         def_output_data.colormap_fixed = 0;
01498       else
01499         set_new_fixed_colormap(clp->arg);
01500       break;
01501       
01502      case COLORMAP_ALGORITHM_OPT:
01503       MARK_CH(output, CH_COLORMAP_METHOD);
01504       def_output_data.colormap_algorithm = clp->val.i;
01505       break;
01506       
01507      case DITHER_OPT:
01508       MARK_CH(output, CH_DITHER);
01509       def_output_data.colormap_dither = !clp->negated;
01510       break;
01511       
01512      case RESIZE_OPT:
01513       MARK_CH(output, CH_RESIZE);
01514       if (clp->negated)
01515         def_output_data.scaling = 0;
01516       else if (dimensions_x <= 0 && dimensions_y <= 0) {
01517         error("one of W and H must be positive in `--resize WxH'");
01518         def_output_data.scaling = 0;
01519       } else {
01520         def_output_data.scaling = 1; 
01521         def_output_data.resize_width = dimensions_x;
01522         def_output_data.resize_height = dimensions_y;
01523       }
01524       break;
01525       
01526      case RESIZE_WIDTH_OPT:
01527       MARK_CH(output, CH_RESIZE);
01528       if (clp->negated)
01529         def_output_data.scaling = 0;
01530       else if (clp->val.u == 0) {
01531         error("`--resize-width' argument must be positive");
01532         def_output_data.scaling = 0;
01533       } else {
01534         def_output_data.scaling = 1; 
01535         def_output_data.resize_width = clp->val.u;
01536         def_output_data.resize_height = 0;
01537       }
01538       break;
01539       
01540      case RESIZE_HEIGHT_OPT:
01541       MARK_CH(output, CH_RESIZE);
01542       if (clp->negated)
01543         def_output_data.scaling = 0;
01544       else if (clp->val.u == 0) {
01545         error("`--resize-height' argument must be positive");
01546         def_output_data.scaling = 0;
01547       } else {
01548         def_output_data.scaling = 1; 
01549         def_output_data.resize_width = 0;
01550         def_output_data.resize_height = clp->val.u;
01551       }
01552       break;
01553       
01554      case SCALE_OPT:
01555       MARK_CH(output, CH_RESIZE);
01556       if (clp->negated)
01557         def_output_data.scaling = 0;
01558       else if (parsed_scale_factor_x <= 0 || parsed_scale_factor_y <= 0) {
01559         error("`--scale' X and Y factors must be positive");
01560         def_output_data.scaling = 0;
01561       } else {
01562         def_output_data.scaling = 2; 
01563         def_output_data.scale_x = parsed_scale_factor_x;
01564         def_output_data.scale_y = parsed_scale_factor_y;
01565       }
01566       break;
01567       
01568       
01569       
01570      case NO_WARNINGS_OPT:
01571       no_warnings = !clp->negated;
01572       break;
01573       
01574      case WARNINGS_OPT:
01575       no_warnings = clp->negated;
01576       break;
01577       
01578      case VERSION_OPT:
01579 #ifdef GIF_UNGIF
01580       printf("LCDF Gifsicle %s (ungif)\n", VERSION);
01581 #else
01582       printf("LCDF Gifsicle %s\n", VERSION);
01583 #endif
01584       printf("Copyright (C) 1997-2001 Eddie Kohler\n\
01585 This is free software; see the source for copying conditions.\n\
01586 There is NO warranty, not even for merchantability or fitness for a\n\
01587 particular purpose.\n");
01588       exit(EXIT_OK);
01589       break;
01590       
01591      case HELP_OPT:
01592       usage();
01593       exit(EXIT_OK);
01594       break;
01595       
01596      case OUTPUT_OPT:
01597       MARK_CH(output, CH_OUTPUT);
01598       if (strcmp(clp->arg, "-") == 0)
01599         def_output_data.output_name = 0;
01600       else
01601         def_output_data.output_name = clp->arg;
01602       break;
01603       
01604       
01605       
01606      case Clp_NotOption:
01607       if (clp->arg[0] != '#' || !frame_argument(clp, clp->arg)) {
01608         input_done();
01609         input_stream(clp->arg);
01610       }
01611       break;
01612       
01613      case Clp_Done:
01614       goto done;
01615       
01616      bad_option:
01617      case Clp_BadOption:
01618       short_usage();
01619       exit(EXIT_USER_ERR);
01620       break;
01621       
01622      default:
01623       break;
01624       
01625     }
01626   }
01627   
01628  done:
01629   
01630   if (next_output)
01631     combine_output_options();
01632   if (!files_given)
01633     input_stream(0);
01634   
01635   frame_change_done();
01636   input_done();
01637   if (mode == MERGING)
01638     output_frames();
01639   
01640   verbose_endline();
01641   print_useless_options("frame", next_frame, frame_option_types);
01642   print_useless_options("input", next_input, input_option_types);
01643   if (any_output_successful)
01644     print_useless_options("output", active_next_output, output_option_types);
01645   blank_frameset(frames, 0, 0, 1);
01646 #ifdef DMALLOC
01647   dmalloc_report();
01648 #endif
01649   return (error_count ? EXIT_ERR : EXIT_OK);
01650 }