1 /* GIF exporting file filter for GIMP
2  *
3  *    Copyright
4  *    - Adam D. Moss
5  *    - Peter Mattis
6  *    - Spencer Kimball
7  *
8  *      Based around original GIF code by David Koblas.
9  *
10  *
11  * Version 4.1.0 - 2003-06-16
12  *                        Adam D. Moss - <adam@gimp.org> <adam@foxbox.org>
13  */
14 /*
15  * This filter uses code taken from the "giftopnm" and "ppmtogif" programs
16  *    which are part of the "netpbm" package.
17  */
18 /*
19  *  "The Graphics Interchange Format(c) is the Copyright property of
20  *  CompuServe Incorporated.  GIF(sm) is a Service Mark property of
21  *  CompuServe Incorporated."
22  */
23 /* Copyright notice for GIF code from which this plugin was long ago     */
24 /* derived (David Koblas has granted permission to relicense):           */
25 /* +-------------------------------------------------------------------+ */
26 /* | Copyright 1990, 1991, 1993, David Koblas.  (koblas@extra.com)     | */
27 /* +-------------------------------------------------------------------+ */
28 
29 #include "config.h"
30 
31 #include <string.h>
32 
33 #include <libgimp/gimp.h>
34 #include <libgimp/gimpui.h>
35 
36 #include "libgimp/stdplugins-intl.h"
37 
38 
39 #define SAVE_PROC      "file-gif-save"
40 #define SAVE2_PROC     "file-gif-save2"
41 #define PLUG_IN_BINARY "file-gif-save"
42 #define PLUG_IN_ROLE   "gimp-file-gif-save"
43 
44 
45 /* uncomment the line below for a little debugging info */
46 /* #define GIFDEBUG yesplease */
47 
48 
49 enum
50 {
51   DISPOSE_STORE_VALUE_COLUMN,
52   DISPOSE_STORE_LABEL_COLUMN
53 };
54 
55 enum
56 {
57   DISPOSE_UNSPECIFIED,
58   DISPOSE_COMBINE,
59   DISPOSE_REPLACE
60 };
61 
62 typedef struct
63 {
64   gint     interlace;
65   gint     save_comment;
66   gint     loop;
67   gint     default_delay;
68   gint     default_dispose;
69   gboolean always_use_default_delay;
70   gboolean always_use_default_dispose;
71   gboolean as_animation;
72 } GIFSaveVals;
73 
74 
75 /* Declare some local functions.
76  */
77 static void     query                  (void);
78 static void     run                    (const gchar      *name,
79                                         gint              nparams,
80                                         const GimpParam  *param,
81                                         gint             *nreturn_vals,
82                                         GimpParam       **return_vals);
83 
84 static gboolean  save_image            (GFile            *file,
85                                         gint32            image_ID,
86                                         gint32            drawable_ID,
87                                         gint32            orig_image_ID,
88                                         GError          **error);
89 
90 static GimpPDBStatusType sanity_check  (GFile            *file,
91                                         gint32           *image_ID,
92                                         GimpRunMode       run_mode,
93                                         GError          **error);
94 static gboolean bad_bounds_dialog      (void);
95 
96 static gboolean save_dialog            (gint32            image_ID);
97 static void     comment_entry_callback (GtkTextBuffer    *buffer);
98 
99 
100 static gboolean  comment_was_edited = FALSE;
101 static gchar    *globalcomment      = NULL;
102 static gint      Interlace; /* For compression code */
103 
104 
105 const GimpPlugInInfo PLUG_IN_INFO =
106 {
107   NULL,  /* init_proc  */
108   NULL,  /* quit_proc  */
109   query, /* query_proc */
110   run,   /* run_proc   */
111 };
112 
113 static GIFSaveVals gsvals =
114 {
115   FALSE,   /* interlace                            */
116   TRUE,    /* save comment                         */
117   TRUE,    /* loop infinitely                      */
118   100,     /* default_delay between frames (100ms) */
119   0,       /* default_dispose = "don't care"       */
120   FALSE,   /* don't always use default_delay       */
121   FALSE,   /* don't always use default_dispose     */
122   FALSE    /* as_animation                         */
123 };
124 
125 
MAIN()126 MAIN ()
127 
128 #define COMMON_SAVE_ARGS \
129     { GIMP_PDB_INT32,    "run-mode",        "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, \
130     { GIMP_PDB_IMAGE,    "image",           "Image to export" }, \
131     { GIMP_PDB_DRAWABLE, "drawable",        "Drawable to export" }, \
132     { GIMP_PDB_STRING,   "uri",             "The name of the URI to export the image in" }, \
133     { GIMP_PDB_STRING,   "raw-uri",         "The name of the URI to export the image in" }, \
134     { GIMP_PDB_INT32,    "interlace",       "Try to export as interlaced" }, \
135     { GIMP_PDB_INT32,    "loop",            "(animated gif) loop infinitely" }, \
136     { GIMP_PDB_INT32,    "default-delay",   "(animated gif) Default delay between frames in milliseconds" }, \
137     { GIMP_PDB_INT32,    "default-dispose", "(animated gif) Default disposal type (0=`don't care`, 1=combine, 2=replace)" }
138 
139 static void
140 query (void)
141 {
142   static const GimpParamDef save_args[] =
143   {
144     COMMON_SAVE_ARGS
145   };
146 
147   static const GimpParamDef save2_args[] =
148   {
149     COMMON_SAVE_ARGS,
150     { GIMP_PDB_INT32,    "as-animation", "Export GIF as animation?" },
151     { GIMP_PDB_INT32,    "force-delay", "(animated gif) Use specified delay for all frames?" },
152     { GIMP_PDB_INT32,    "force-dispose", "(animated gif) Use specified disposal for all frames?" }
153   };
154 
155   gimp_install_procedure (SAVE_PROC,
156                           "exports files in Compuserve GIF file format",
157                           "Export a file in Compuserve GIF format, with "
158                           "possible animation, transparency, and comment.  "
159                           "To export an animation, operate on a multi-layer "
160                           "file.  The plug-in will interpret <50% alpha as "
161                           "transparent.  When run non-interactively, the "
162                           "value for the comment is taken from the "
163                           "'gimp-comment' parasite.  ",
164                           "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
165                           "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
166                           "1995-1997",
167                           N_("GIF image"),
168                           "INDEXED*, GRAY*",
169                           GIMP_PLUGIN,
170                           G_N_ELEMENTS (save_args), 0,
171                           save_args, NULL);
172 
173   gimp_install_procedure (SAVE2_PROC,
174                           "exports files in Compuserve GIF file format",
175                           "Export a file in Compuserve GIF format, with "
176                           "possible animation, transparency, and comment.  "
177                           "To export an animation, operate on a multi-layer "
178                           "file and give the 'as-animation' parameter "
179                           "as TRUE.  The plug-in will interpret <50% "
180                           "alpha as transparent.  When run "
181                           "non-interactively, the value for the comment "
182                           "is taken from the 'gimp-comment' parasite.  ",
183                           "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
184                           "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
185                           "1995-1997",
186                           N_("GIF image"),
187                           "INDEXED*, GRAY*",
188                           GIMP_PLUGIN,
189                           G_N_ELEMENTS (save2_args), 0,
190                           save2_args, NULL);
191 
192   gimp_register_file_handler_mime (SAVE_PROC, "image/gif");
193   gimp_register_save_handler (SAVE_PROC, "gif", "");
194   gimp_register_file_handler_uri (SAVE_PROC);
195 
196   gimp_register_file_handler_uri (SAVE2_PROC);
197 }
198 
199 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)200 run (const gchar      *name,
201      gint              nparams,
202      const GimpParam  *param,
203      gint             *nreturn_vals,
204      GimpParam       **return_vals)
205 {
206   static GimpParam   values[2];
207   GimpRunMode        run_mode;
208   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
209   GimpExportReturn   export = GIMP_EXPORT_CANCEL;
210   GError            *error  = NULL;
211 
212   INIT_I18N ();
213   gegl_init (NULL, NULL);
214 
215   run_mode = param[0].data.d_int32;
216 
217   *nreturn_vals = 1;
218   *return_vals  = values;
219 
220   values[0].type          = GIMP_PDB_STATUS;
221   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
222 
223   if (strcmp (name, SAVE_PROC)  == 0 ||
224       strcmp (name, SAVE2_PROC) == 0)
225     {
226       GFile  *file;
227       gint32  image_ID;
228       gint32  orig_image_ID;
229       gint32  sanitized_image_ID = 0;
230       gint32  drawable_ID;
231 
232       image_ID    = orig_image_ID = param[1].data.d_int32;
233       drawable_ID = param[2].data.d_int32;
234       file        = g_file_new_for_uri (param[3].data.d_string);
235 
236       if (run_mode == GIMP_RUN_INTERACTIVE ||
237           run_mode == GIMP_RUN_WITH_LAST_VALS)
238         gimp_ui_init (PLUG_IN_BINARY, FALSE);
239 
240       status = sanity_check (file, &image_ID, run_mode, &error);
241 
242       /* Get the export options */
243       if (status == GIMP_PDB_SUCCESS)
244         {
245           /* If the sanity check succeeded, the image_ID will point to
246            * a duplicate image to delete later.
247            */
248           sanitized_image_ID = image_ID;
249 
250           switch (run_mode)
251             {
252             case GIMP_RUN_INTERACTIVE:
253               /*  Possibly retrieve data  */
254               gimp_get_data (SAVE_PROC, &gsvals);
255 
256               /*  First acquire information with a dialog  */
257               if (! save_dialog (image_ID))
258                 {
259                   gimp_image_delete (sanitized_image_ID);
260                   status = GIMP_PDB_CANCEL;
261                 }
262               break;
263 
264             case GIMP_RUN_NONINTERACTIVE:
265               /*  Make sure all the arguments are there!  */
266               if (nparams != 9 && nparams != 12)
267                 {
268                   status = GIMP_PDB_CALLING_ERROR;
269                 }
270               else
271                 {
272                   gsvals.interlace       = (param[5].data.d_int32) ? TRUE : FALSE;
273                   gsvals.save_comment    = TRUE;  /*  no way to to specify that through the PDB  */
274                   gsvals.loop            = (param[6].data.d_int32) ? TRUE : FALSE;
275                   gsvals.default_delay   = param[7].data.d_int32;
276                   gsvals.default_dispose = param[8].data.d_int32;
277                   if (nparams == 12)
278                     {
279                       gsvals.as_animation               = (param[9].data.d_int32) ? TRUE : FALSE;
280                       gsvals.always_use_default_delay   = (param[10].data.d_int32) ? TRUE : FALSE;
281                       gsvals.always_use_default_dispose = (param[11].data.d_int32) ? TRUE : FALSE;
282                     }
283                 }
284               break;
285 
286             case GIMP_RUN_WITH_LAST_VALS:
287               /*  Possibly retrieve data  */
288               gimp_get_data (SAVE_PROC, &gsvals);
289               break;
290 
291             default:
292               break;
293             }
294         }
295 
296       if (status == GIMP_PDB_SUCCESS)
297         {
298           /* Create an exportable image based on the export options */
299           switch (run_mode)
300             {
301             case GIMP_RUN_INTERACTIVE:
302             case GIMP_RUN_WITH_LAST_VALS:
303                 {
304                   GimpExportCapabilities capabilities =
305                     GIMP_EXPORT_CAN_HANDLE_INDEXED |
306                     GIMP_EXPORT_CAN_HANDLE_GRAY    |
307                     GIMP_EXPORT_CAN_HANDLE_ALPHA;
308 
309                   if (gsvals.as_animation)
310                     capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
311 
312                   export = gimp_export_image (&image_ID, &drawable_ID, "GIF",
313                                               capabilities);
314 
315                   if (export == GIMP_EXPORT_CANCEL)
316                     {
317                       values[0].data.d_status = GIMP_PDB_CANCEL;
318                       if (sanitized_image_ID)
319                         gimp_image_delete (sanitized_image_ID);
320                       return;
321                     }
322                 }
323               break;
324             default:
325               break;
326             }
327 
328           /* Write the image to file */
329           if (save_image (file,
330                           image_ID, drawable_ID, orig_image_ID,
331                           &error))
332             {
333               /*  Store psvals data  */
334               gimp_set_data (SAVE_PROC, &gsvals, sizeof (GIFSaveVals));
335             }
336           else
337             {
338               status = GIMP_PDB_EXECUTION_ERROR;
339             }
340 
341           gimp_image_delete (sanitized_image_ID);
342         }
343 
344       if (export == GIMP_EXPORT_EXPORT)
345         gimp_image_delete (image_ID);
346 
347       g_object_unref (file);
348     }
349 
350   if (status != GIMP_PDB_SUCCESS && error)
351     {
352       *nreturn_vals = 2;
353       values[1].type          = GIMP_PDB_STRING;
354       values[1].data.d_string = error->message;
355     }
356 
357   values[0].data.d_status = status;
358 }
359 
360 
361 /* ppmtogif.c - read a portable pixmap and produce a GIF file
362 **
363 ** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
364 ** Lempel-Ziv compression based on "compress".
365 **
366 ** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
367 **
368 **
369 ** Copyright (C) 1989 by Jef Poskanzer.
370 **
371 ** Permission to use, copy, modify, and distribute this software and its
372 ** documentation for any purpose and without fee is hereby granted, provided
373 ** that the above copyright notice appear in all copies and that both that
374 ** copyright notice and this permission notice appear in supporting
375 ** documentation.  This software is provided "as is" without express or
376 ** implied warranty.
377 **
378 ** The Graphics Interchange Format(c) is the Copyright property of
379 ** CompuServe Incorporated.  GIF(sm) is a Service Mark property of
380 ** CompuServe Incorporated.
381 */
382 
383 #define MAXCOLORS 256
384 
385 /*
386  * Pointer to function returning an int
387  */
388 typedef gint (* ifunptr) (gint x,
389                           gint y);
390 
391 
392 static gint find_unused_ia_color           (const guchar  *pixels,
393                                             gint           numpixels,
394                                             gint           num_indices,
395                                             gint          *colors);
396 
397 static void special_flatten_indexed_alpha  (guchar        *pixels,
398                                             gint           transparent,
399                                             gint           numpixels);
400 
401 static gint colors_to_bpp                  (gint           colors);
402 static gint bpp_to_colors                  (gint           bpp);
403 static gint get_pixel                      (gint           x,
404                                             gint           y);
405 static gint gif_next_pixel                 (ifunptr        getpixel);
406 static void bump_pixel                     (void);
407 
408 static gboolean gif_encode_header              (GOutputStream  *output,
409                                                 gboolean        gif89,
410                                                 gint            width,
411                                                 gint            height,
412                                                 gint            background,
413                                                 gint            bpp,
414                                                 gint           *red,
415                                                 gint           *green,
416                                                 gint           *blue,
417                                                 ifunptr         get_pixel,
418                                                 GError        **error);
419 static gboolean gif_encode_graphic_control_ext (GOutputStream  *output,
420                                                 gint            disposal,
421                                                 gint            delay89,
422                                                 gint            n_frames,
423                                                 gint            width,
424                                                 gint            height,
425                                                 gint            transparent,
426                                                 gint            bpp,
427                                                 ifunptr         get_pixel,
428                                                 GError        **error);
429 static gboolean gif_encode_image_data          (GOutputStream  *output,
430                                                 gint            width,
431                                                 gint            height,
432                                                 gint            interlace,
433                                                 gint            bpp,
434                                                 ifunptr         get_pixel,
435                                                 gint            offset_x,
436                                                 gint            offset_y,
437                                                 GError        **error);
438 static gboolean gif_encode_close               (GOutputStream  *output,
439                                                 GError        **error);
440 static gboolean gif_encode_loop_ext            (GOutputStream  *output,
441                                                 guint           n_loops,
442                                                 GError        **error);
443 static gboolean gif_encode_comment_ext         (GOutputStream  *output,
444                                                 const gchar    *comment,
445                                                 GError        **error);
446 
447 static gint     rowstride;
448 static guchar  *pixels;
449 static gint     cur_progress;
450 static gint     max_progress;
451 
452 static gboolean compress        (GOutputStream *output,
453                                  gint           init_bits,
454                                  ifunptr        ReadValue,
455                                  GError        **error);
456 static gboolean no_compress     (GOutputStream *output,
457                                  gint           init_bits,
458                                  ifunptr        ReadValue,
459                                  GError        **error);
460 static gboolean rle_compress    (GOutputStream *output,
461                                  gint           init_bits,
462                                  ifunptr        ReadValue,
463                                  GError        **error);
464 static gboolean normal_compress (GOutputStream *output,
465                                  gint           init_bits,
466                                  ifunptr        ReadValue,
467                                  GError        **error);
468 
469 static gboolean put_byte        (GOutputStream  *output,
470                                  guchar          b,
471                                  GError        **error);
472 static gboolean put_word        (GOutputStream  *output,
473                                  gint            w,
474                                  GError        **error);
475 static gboolean put_string      (GOutputStream  *output,
476                                  const gchar    *s,
477                                  GError        **error);
478 static gboolean output_code     (GOutputStream  *output,
479                                  gint            code,
480                                  GError        **error);
481 static gboolean cl_block        (GOutputStream  *output,
482                                  GError        **error);
483 static void     cl_hash         (glong           hsize);
484 
485 static void     char_init       (void);
486 static gboolean char_out        (GOutputStream  *output,
487                                  gint            c,
488                                  GError        **error);
489 static gboolean char_flush      (GOutputStream  *output,
490                                  GError        **error);
491 
492 
493 static gint
find_unused_ia_color(const guchar * pixels,gint numpixels,gint num_indices,gint * colors)494 find_unused_ia_color (const guchar *pixels,
495                       gint          numpixels,
496                       gint          num_indices,
497                       gint         *colors)
498 {
499   gboolean ix_used[256];
500   gint     i;
501 
502 #ifdef GIFDEBUG
503   g_printerr ("GIF: fuiac: Image claims to use %d/%d indices - finding free "
504               "index...\n", *colors, num_indices);
505 #endif
506 
507   for (i = 0; i < 256; i++)
508     ix_used[i] = FALSE;
509 
510   for (i = 0; i < numpixels; i++)
511     {
512       if (pixels[i * 2 + 1])
513         ix_used[pixels[i * 2]] = TRUE;
514     }
515 
516   for (i = num_indices - 1; i >= 0; i--)
517     {
518       if (! ix_used[i])
519         {
520 #ifdef GIFDEBUG
521           g_printerr ("GIF: Found unused color index %d.\n", (int) i);
522 #endif
523           return i;
524         }
525     }
526 
527   /* Couldn't find an unused color index within the number of bits per
528    * pixel we wanted.  Will have to increment the number of colors in
529    * the image and assign a transparent pixel there.
530    */
531   if (*colors < 256)
532     {
533       (*colors)++;
534 
535       g_printerr ("GIF: 2nd pass "
536                   "- Increasing bounds and using color index %d.\n",
537                   *colors - 1);
538       return ((*colors) - 1);
539     }
540 
541   g_message (_("Couldn't simply reduce colors further. Exporting as opaque."));
542 
543   return -1;
544 }
545 
546 
547 static void
special_flatten_indexed_alpha(guchar * pixels,gint transparent,gint numpixels)548 special_flatten_indexed_alpha (guchar *pixels,
549                                gint    transparent,
550                                gint    numpixels)
551 {
552   guint32 i;
553 
554   /* Each transparent pixel in the image is mapped to a uniform value
555    * for encoding, if image already has <=255 colors
556    */
557 
558   if (transparent == -1) /* tough, no indices left for the trans. index */
559     {
560       for (i = 0; i < numpixels; i++)
561         pixels[i] = pixels[i * 2];
562     }
563   else  /* make transparent */
564     {
565       for (i = 0; i < numpixels; i++)
566         {
567           if (! (pixels[i * 2 + 1] & 128))
568             {
569               pixels[i] = (guchar) transparent;
570             }
571           else
572             {
573               pixels[i] = pixels[i * 2];
574             }
575         }
576     }
577 }
578 
579 
580 static gint
parse_ms_tag(const gchar * str)581 parse_ms_tag (const gchar *str)
582 {
583   gint sum    = 0;
584   gint offset = 0;
585   gint length;
586 
587   length = strlen (str);
588 
589  find_another_bra:
590 
591   while ((offset < length) && (str[offset] != '('))
592     offset++;
593 
594   if (offset >= length)
595     return(-1);
596 
597   if (! g_ascii_isdigit (str[++offset]))
598     goto find_another_bra;
599 
600   do
601     {
602       sum *= 10;
603       sum += str[offset] - '0';
604       offset++;
605     }
606   while ((offset < length) && (g_ascii_isdigit (str[offset])));
607 
608   if (length - offset <= 2)
609     return(-3);
610 
611   if ((g_ascii_toupper (str[offset])     != 'M') ||
612       (g_ascii_toupper (str[offset + 1]) != 'S'))
613     return -4;
614 
615   return sum;
616 }
617 
618 
619 static gint
parse_disposal_tag(const gchar * str)620 parse_disposal_tag (const gchar *str)
621 {
622   gint offset = 0;
623   gint length;
624 
625   length = strlen (str);
626 
627   while ((offset + 9) <= length)
628     {
629       if (strncmp (&str[offset], "(combine)", 9) == 0)
630         return 0x01;
631 
632       if (strncmp (&str[offset], "(replace)", 9) == 0)
633         return 0x02 ;
634 
635       offset++;
636     }
637 
638   return gsvals.default_dispose;
639 }
640 
641 
642 static GimpPDBStatusType
sanity_check(GFile * file,gint32 * image_ID,GimpRunMode run_mode,GError ** error)643 sanity_check (GFile        *file,
644               gint32       *image_ID,
645               GimpRunMode   run_mode,
646               GError      **error)
647 {
648   gint32 *layers;
649   gint    nlayers;
650   gint    image_width;
651   gint    image_height;
652   gint    i;
653 
654   image_width  = gimp_image_width (*image_ID);
655   image_height = gimp_image_height (*image_ID);
656 
657   if (image_width > G_MAXUSHORT || image_height > G_MAXUSHORT)
658     {
659       g_set_error (error, 0, 0,
660                    _("Unable to export '%s'.  "
661                    "The GIF file format does not support images that are "
662                    "more than %d pixels wide or tall."),
663                    gimp_file_get_utf8_name (file), G_MAXUSHORT);
664 
665       return GIMP_PDB_EXECUTION_ERROR;
666     }
667 
668   /*** Iterate through the layers to make sure they're all ***/
669   /*** within the bounds of the image                      ***/
670 
671   *image_ID = gimp_image_duplicate (*image_ID);
672   layers = gimp_image_get_layers (*image_ID, &nlayers);
673 
674   for (i = 0; i < nlayers; i++)
675     {
676       gint offset_x;
677       gint offset_y;
678 
679       gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
680 
681       if (offset_x < 0 ||
682           offset_y < 0 ||
683           offset_x + gimp_drawable_width (layers[i]) > image_width ||
684           offset_y + gimp_drawable_height (layers[i]) > image_height)
685         {
686           g_free (layers);
687 
688           /* Image has illegal bounds - ask the user what it wants to do */
689 
690           /* Do the crop if we can't talk to the user, or if we asked
691            * the user and they said yes.
692            */
693           if ((run_mode == GIMP_RUN_NONINTERACTIVE) || bad_bounds_dialog ())
694             {
695               gimp_image_crop (*image_ID, image_width, image_height, 0, 0);
696               return GIMP_PDB_SUCCESS;
697             }
698           else
699             {
700               gimp_image_delete (*image_ID);
701               return GIMP_PDB_CANCEL;
702             }
703         }
704     }
705 
706   g_free (layers);
707 
708   return GIMP_PDB_SUCCESS;
709 }
710 
711 
712 static gboolean
save_image(GFile * file,gint32 image_ID,gint32 drawable_ID,gint32 orig_image_ID,GError ** error)713 save_image (GFile   *file,
714             gint32   image_ID,
715             gint32   drawable_ID,
716             gint32   orig_image_ID,
717             GError **error)
718 {
719   GeglBuffer    *buffer;
720   GimpImageType  drawable_type;
721   const Babl    *format = NULL;
722   GOutputStream *output;
723   gint           Red[MAXCOLORS];
724   gint           Green[MAXCOLORS];
725   gint           Blue[MAXCOLORS];
726   guchar        *cmap;
727   guint          rows, cols;
728   gint           BitsPerPixel;
729   gint           liberalBPP = 0;
730   gint           useBPP     = 0;
731   gint           colors;
732   gint           i;
733   gint           transparent;
734   gint           offset_x, offset_y;
735 
736   gint32        *layers;
737   gint           nlayers;
738 
739   gboolean       is_gif89 = FALSE;
740 
741   gint           Delay89;
742   gint           Disposal;
743   gchar         *layer_name;
744 
745   GimpRGB        background;
746   guchar         bgred, bggreen, bgblue;
747   guchar         bgindex = 0;
748   guint          best_error = 0xFFFFFFFF;
749 
750   /* Save the comment back to the ImageID, if appropriate */
751   if (globalcomment != NULL && comment_was_edited)
752     {
753       GimpParasite *parasite;
754 
755       parasite = gimp_parasite_new ("gimp-comment",
756                                     GIMP_PARASITE_PERSISTENT,
757                                     strlen (globalcomment) + 1,
758                                     (gpointer) globalcomment);
759       gimp_image_attach_parasite (orig_image_ID, parasite);
760       gimp_parasite_free (parasite);
761     }
762 
763   /* The GIF spec says 7bit ASCII for the comment block. */
764   if (gsvals.save_comment && globalcomment)
765     {
766       const gchar *c = globalcomment;
767       gint         len;
768 
769       for (len = strlen (c); len; c++, len--)
770         {
771           if ((guchar) *c > 127)
772             {
773               g_message (_("The GIF format only supports comments in "
774                            "7bit ASCII encoding. No comment is saved."));
775 
776               g_free (globalcomment);
777               globalcomment = NULL;
778 
779               break;
780             }
781         }
782     }
783 
784   /* get a list of layers for this image_ID */
785   layers = gimp_image_get_layers (image_ID, &nlayers);
786 
787   drawable_type = gimp_drawable_type (layers[0]);
788 
789   /* If the image has multiple layers (i.e. will be animated), a
790    * comment, or transparency, then it must be encoded as a GIF89a
791    * file, not a vanilla GIF87a.
792    */
793   if (nlayers > 1)
794     {
795       is_gif89 = TRUE;
796 
797       /* Layers can be with or without alpha channel. Make sure we set
798        * alpha if there is at least one layer with alpha channel. */
799       if (drawable_type == GIMP_GRAY_IMAGE ||
800           drawable_type == GIMP_INDEXED_IMAGE)
801         {
802           for (i = nlayers - 1; i >= 0; i--)
803             {
804               GimpImageType dr_type = gimp_drawable_type (layers[i]);
805 
806               if (dr_type == GIMP_GRAYA_IMAGE ||
807                   dr_type == GIMP_INDEXEDA_IMAGE)
808                 {
809                   drawable_type = dr_type;
810                   break;
811                 }
812             }
813         }
814     }
815 
816   if (gsvals.save_comment)
817     is_gif89 = TRUE;
818 
819   switch (drawable_type)
820     {
821     case GIMP_INDEXEDA_IMAGE:
822       is_gif89 = TRUE;
823     case GIMP_INDEXED_IMAGE:
824       cmap = gimp_image_get_colormap (image_ID, &colors);
825 
826       gimp_context_get_background (&background);
827       gimp_rgb_get_uchar (&background, &bgred, &bggreen, &bgblue);
828 
829       for (i = 0; i < colors; i++)
830         {
831           Red[i]   = *cmap++;
832           Green[i] = *cmap++;
833           Blue[i]  = *cmap++;
834         }
835       for ( ; i < 256; i++)
836         {
837           Red[i]   = bgred;
838           Green[i] = bggreen;
839           Blue[i]  = bgblue;
840         }
841       break;
842     case GIMP_GRAYA_IMAGE:
843       is_gif89 = TRUE;
844     case GIMP_GRAY_IMAGE:
845       colors = 256;                   /* FIXME: Not ideal. */
846       for ( i = 0;  i < 256; i++)
847         {
848           Red[i] = Green[i] = Blue[i] = i;
849         }
850       break;
851 
852     default:
853       g_message (_("Cannot export RGB color images. Convert to "
854                    "indexed color or grayscale first."));
855       return FALSE;
856     }
857 
858 
859   /* find earliest index in palette which is closest to the background
860    * color, and ATTEMPT to use that as the GIF's default background
861    * color.
862    */
863   for (i = 255; i >= 0; --i)
864     {
865       guint local_error = 0;
866 
867       local_error += (Red[i]   - bgred)   * (Red[i]   - bgred);
868       local_error += (Green[i] - bggreen) * (Green[i] - bggreen);
869       local_error += (Blue[i]  - bgblue)  * (Blue[i]  - bgblue);
870 
871       if (local_error <= best_error)
872         {
873           bgindex = i;
874           best_error = local_error;
875         }
876     }
877 
878 
879   /* init the progress meter */
880   gimp_progress_init_printf (_("Exporting '%s'"),
881                              gimp_file_get_utf8_name (file));
882 
883 
884   /* open the destination file for writing */
885   output = G_OUTPUT_STREAM (g_file_replace (file,
886                                             NULL, FALSE, G_FILE_CREATE_NONE,
887                                             NULL, error));
888   if (output)
889     {
890       GDataOutputStream *data_output;
891 
892       data_output = g_data_output_stream_new (output);
893       g_object_unref (output);
894 
895       g_data_output_stream_set_byte_order (data_output,
896                                            G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
897 
898       output = G_OUTPUT_STREAM (data_output);
899     }
900   else
901     {
902       return FALSE;
903     }
904 
905 
906   /* write the GIFheader */
907 
908   if (colors < 256)
909     {
910       /* we keep track of how many bits we promised to have in
911        * liberalBPP, so that we don't accidentally come under this
912        * when doing clever transparency stuff where we can re-use
913        * wasted indices.
914        */
915       liberalBPP = BitsPerPixel =
916         colors_to_bpp (colors + ((drawable_type==GIMP_INDEXEDA_IMAGE) ? 1 : 0));
917     }
918   else
919     {
920       liberalBPP = BitsPerPixel =
921         colors_to_bpp (256);
922 
923       if (drawable_type == GIMP_INDEXEDA_IMAGE)
924         {
925           g_printerr ("GIF: Too many colors?\n");
926         }
927     }
928 
929   cols = gimp_image_width (image_ID);
930   rows = gimp_image_height (image_ID);
931   Interlace = gsvals.interlace;
932   if (! gif_encode_header (output, is_gif89, cols, rows, bgindex,
933                            BitsPerPixel, Red, Green, Blue, get_pixel,
934                            error))
935     return FALSE;
936 
937 
938   /* If the image has multiple layers it'll be made into an animated
939    * GIF, so write out the infinite-looping extension
940    */
941   if ((nlayers > 1) && (gsvals.loop))
942     if (! gif_encode_loop_ext (output, 0, error))
943       return FALSE;
944 
945   /* Write comment extension - mustn't be written before the looping ext. */
946   if (gsvals.save_comment && globalcomment)
947     {
948       if (! gif_encode_comment_ext (output, globalcomment, error))
949         return FALSE;
950     }
951 
952 
953   /*** Now for each layer in the image, save an image in a compound GIF ***/
954   /************************************************************************/
955 
956   cur_progress = 0;
957   max_progress = nlayers * rows;
958 
959   for (i = nlayers - 1; i >= 0; i--, cur_progress = (nlayers - i) * rows)
960     {
961       drawable_type = gimp_drawable_type (layers[i]);
962       if (drawable_type == GIMP_GRAYA_IMAGE)
963         {
964           format = babl_format ("Y'A u8");
965         }
966       else if (drawable_type == GIMP_GRAY_IMAGE)
967         {
968           format = babl_format ("Y' u8");
969         }
970       buffer = gimp_drawable_get_buffer (layers[i]);
971       gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
972       cols = gimp_drawable_width (layers[i]);
973       rows = gimp_drawable_height (layers[i]);
974       rowstride = cols;
975 
976       pixels = g_new (guchar, (cols * rows *
977                                (((drawable_type == GIMP_INDEXEDA_IMAGE) ||
978                                  (drawable_type == GIMP_GRAYA_IMAGE)) ? 2 : 1)));
979 
980       gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, cols, rows), 1.0,
981                        format, pixels,
982                        GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
983 
984       /* sort out whether we need to do transparency jiggery-pokery */
985       if ((drawable_type == GIMP_INDEXEDA_IMAGE) ||
986           (drawable_type == GIMP_GRAYA_IMAGE))
987         {
988           /* Try to find an entry which isn't actually used in the
989            * image, for a transparency index.
990            */
991 
992           transparent =
993             find_unused_ia_color (pixels,
994                                    cols * rows,
995                                    bpp_to_colors (colors_to_bpp (colors)),
996                                    &colors);
997 
998           special_flatten_indexed_alpha (pixels,
999                                          transparent,
1000                                          cols * rows);
1001         }
1002       else
1003         {
1004           transparent = -1;
1005         }
1006 
1007       BitsPerPixel = colors_to_bpp (colors);
1008 
1009       if (BitsPerPixel != liberalBPP)
1010         {
1011           /* We were able to re-use an index within the existing
1012            * bitspace, whereas the estimate in the header was
1013            * pessimistic but still needs to be upheld...
1014            */
1015 #ifdef GIFDEBUG
1016           static gboolean onceonly = FALSE;
1017 
1018           if (! onceonly)
1019             {
1020               g_warning ("Promised %d bpp, pondered writing chunk with %d bpp!",
1021                          liberalBPP, BitsPerPixel);
1022               onceonly = TRUE;
1023             }
1024 #endif
1025         }
1026 
1027       useBPP = (BitsPerPixel > liberalBPP) ? BitsPerPixel : liberalBPP;
1028 
1029       if (is_gif89)
1030         {
1031           if (i > 0 && ! gsvals.always_use_default_dispose)
1032             {
1033               layer_name = gimp_item_get_name (layers[i - 1]);
1034               Disposal = parse_disposal_tag (layer_name);
1035               g_free (layer_name);
1036             }
1037           else
1038             {
1039               Disposal = gsvals.default_dispose;
1040             }
1041 
1042           layer_name = gimp_item_get_name (layers[i]);
1043           Delay89 = parse_ms_tag (layer_name);
1044           g_free (layer_name);
1045 
1046           if (Delay89 < 0 || gsvals.always_use_default_delay)
1047             Delay89 = (gsvals.default_delay + 5) / 10;
1048           else
1049             Delay89 = (Delay89 + 5) / 10;
1050 
1051           /* don't allow a CPU-sucking completely 0-delay looping anim */
1052           if ((nlayers > 1) && gsvals.loop && (Delay89 == 0))
1053             {
1054               static gboolean onceonly = FALSE;
1055 
1056               if (! onceonly)
1057                 {
1058                   g_message (_("Delay inserted to prevent evil "
1059                                "CPU-sucking animation."));
1060                   onceonly = TRUE;
1061                 }
1062 
1063               Delay89 = 1;
1064             }
1065 
1066           if (! gif_encode_graphic_control_ext (output,
1067                                                 Disposal, Delay89, nlayers,
1068                                                 cols, rows,
1069                                                 transparent,
1070                                                 useBPP,
1071                                                 get_pixel,
1072                                                 error))
1073             return FALSE;
1074         }
1075 
1076       if (! gif_encode_image_data (output, cols, rows,
1077                                    (rows > 4) ? gsvals.interlace : 0,
1078                                    useBPP,
1079                                    get_pixel,
1080                                    offset_x, offset_y,
1081                                    error))
1082         return FALSE;
1083 
1084       gimp_progress_update (1.0);
1085 
1086       g_object_unref (buffer);
1087 
1088       g_free (pixels);
1089     }
1090 
1091   g_free (layers);
1092 
1093   if (! gif_encode_close (output, error))
1094     return FALSE;
1095 
1096   return TRUE;
1097 }
1098 
1099 static gboolean
bad_bounds_dialog(void)1100 bad_bounds_dialog (void)
1101 {
1102   GtkWidget *dialog;
1103   gboolean   crop;
1104 
1105   dialog = gtk_message_dialog_new (NULL, 0,
1106                                    GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
1107                                    _("The image you are trying to export as a "
1108                                      "GIF contains layers which extend beyond "
1109                                      "the actual borders of the image."));
1110 
1111   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1112                           _("_Cancel"), GTK_RESPONSE_CANCEL,
1113                           _("Cr_op"),   GTK_RESPONSE_OK,
1114                           NULL);
1115 
1116   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1117                                            GTK_RESPONSE_OK,
1118                                            GTK_RESPONSE_CANCEL,
1119                                            -1);
1120 
1121   gimp_window_set_transient (GTK_WINDOW (dialog));
1122 
1123   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1124                                             _("The GIF file format does not "
1125                                               "allow this.  You may choose "
1126                                               "whether to crop all of the "
1127                                               "layers to the image borders, "
1128                                               "or cancel this export."));
1129 
1130   gtk_widget_show (dialog);
1131 
1132   crop = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
1133 
1134   gtk_widget_destroy (dialog);
1135 
1136   return crop;
1137 }
1138 
1139 static GtkWidget *
file_gif_toggle_button_init(GtkBuilder * builder,const gchar * name,gboolean initial_value,gboolean * value_pointer)1140 file_gif_toggle_button_init (GtkBuilder  *builder,
1141                              const gchar *name,
1142                              gboolean     initial_value,
1143                              gboolean    *value_pointer)
1144 {
1145   GtkWidget *toggle = NULL;
1146 
1147   toggle = GTK_WIDGET (gtk_builder_get_object (builder, name));
1148   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), initial_value);
1149   g_signal_connect (toggle, "toggled",
1150                     G_CALLBACK (gimp_toggle_button_update),
1151                     value_pointer);
1152 
1153   return toggle;
1154 }
1155 
1156 static GtkWidget *
file_gif_spin_button_int_init(GtkBuilder * builder,const gchar * name,int initial_value,int * value_pointer)1157 file_gif_spin_button_int_init (GtkBuilder  *builder,
1158                                const gchar *name,
1159                                int          initial_value,
1160                                int         *value_pointer)
1161 {
1162   GtkWidget     *spin_button = NULL;
1163   GtkAdjustment *adjustment  = NULL;
1164 
1165   spin_button = GTK_WIDGET (gtk_builder_get_object (builder, name));
1166 
1167   adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin_button));
1168   gtk_adjustment_set_value (adjustment, initial_value);
1169   g_signal_connect (adjustment, "value-changed",
1170                     G_CALLBACK (gimp_int_adjustment_update),
1171                     value_pointer);
1172 
1173   return spin_button;
1174 }
1175 
1176 static void
file_gif_combo_box_int_update_value(GtkComboBox * combo,gint * value)1177 file_gif_combo_box_int_update_value (GtkComboBox *combo,
1178                                      gint        *value)
1179 {
1180   GtkTreeIter iter;
1181 
1182   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
1183     {
1184       gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)),
1185                           &iter,
1186                           DISPOSE_STORE_VALUE_COLUMN, value,
1187                           -1);
1188     }
1189 }
1190 
1191 static GtkWidget *
file_gif_combo_box_int_init(GtkBuilder * builder,const gchar * name,int initial_value,int * value_pointer,const gchar * first_label,gint first_value,...)1192 file_gif_combo_box_int_init (GtkBuilder  *builder,
1193                              const gchar *name,
1194                              int          initial_value,
1195                              int         *value_pointer,
1196                              const gchar *first_label,
1197                              gint         first_value,
1198                              ...)
1199 {
1200   GtkWidget    *combo  = NULL;
1201   GtkListStore *store  = NULL;
1202   const gchar  *label  = NULL;
1203   gint          value  = 0;
1204   GtkTreeIter   iter   = { 0, };
1205   va_list       values;
1206 
1207   combo = GTK_WIDGET (gtk_builder_get_object (builder, name));
1208   store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
1209 
1210   /* Populate */
1211   va_start (values, first_value);
1212   for (label = first_label, value = first_value;
1213        label;
1214        label = va_arg (values, const gchar *), value = va_arg (values, gint))
1215     {
1216       gtk_list_store_append (store, &iter);
1217       gtk_list_store_set (store, &iter,
1218                           DISPOSE_STORE_VALUE_COLUMN, value,
1219                           DISPOSE_STORE_LABEL_COLUMN, label,
1220                           -1);
1221     }
1222   va_end (values);
1223 
1224   /* Set initial value */
1225   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
1226                                  &iter,
1227                                  NULL,
1228                                  initial_value);
1229   gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
1230 
1231   /* Arrange update of value */
1232   g_signal_connect (combo, "changed",
1233                     G_CALLBACK (file_gif_combo_box_int_update_value),
1234                     value_pointer);
1235 
1236   return combo;
1237 }
1238 
1239 static gboolean
save_dialog(gint32 image_ID)1240 save_dialog (gint32 image_ID)
1241 {
1242   GtkBuilder    *builder = NULL;
1243   gchar         *ui_file = NULL;
1244   GError        *error   = NULL;
1245   GtkWidget     *dialog;
1246   GtkWidget     *text_view;
1247   GtkTextBuffer *text_buffer;
1248   GtkWidget     *toggle;
1249   GtkWidget     *frame;
1250   GimpParasite  *GIF2_CMNT;
1251   gint32         nlayers;
1252   gboolean       animation_supported = FALSE;
1253   gboolean       run;
1254 
1255   g_free (gimp_image_get_layers (image_ID, &nlayers));
1256   animation_supported = nlayers > 1;
1257 
1258   dialog = gimp_export_dialog_new (_("GIF"), PLUG_IN_BINARY, SAVE_PROC);
1259 
1260   /* GtkBuilder init */
1261   builder = gtk_builder_new ();
1262   ui_file = g_build_filename (gimp_data_directory (),
1263                               "ui/plug-ins/plug-in-file-gif.ui",
1264                               NULL);
1265   if (! gtk_builder_add_from_file (builder, ui_file, &error))
1266     g_printerr (_("Error loading UI file '%s':\n%s"),
1267                 ui_file, error ? error->message : "???");
1268   g_free (ui_file);
1269 
1270   /* Main vbox */
1271   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
1272                       GTK_WIDGET (gtk_builder_get_object (builder, "main-vbox")),
1273                       TRUE, TRUE, 0);
1274 
1275   /*  regular gif parameter settings  */
1276   file_gif_toggle_button_init (builder, "interlace",
1277                                gsvals.interlace, &gsvals.interlace);
1278   file_gif_toggle_button_init (builder, "save-comment",
1279                                gsvals.save_comment, &gsvals.save_comment);
1280   file_gif_toggle_button_init (builder, "as-animation",
1281                                gsvals.as_animation, &gsvals.as_animation);
1282 
1283   text_view   = GTK_WIDGET (gtk_builder_get_object (builder, "comment"));
1284   text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
1285 
1286   if (globalcomment)
1287     g_free (globalcomment);
1288 
1289   GIF2_CMNT = gimp_image_get_parasite (image_ID, "gimp-comment");
1290   if (GIF2_CMNT)
1291     {
1292       globalcomment = g_strndup (gimp_parasite_data (GIF2_CMNT),
1293                                  gimp_parasite_data_size (GIF2_CMNT));
1294       gimp_parasite_free (GIF2_CMNT);
1295     }
1296   else
1297     {
1298       globalcomment = gimp_get_default_comment ();
1299     }
1300 
1301   if (globalcomment)
1302     gtk_text_buffer_set_text (text_buffer, globalcomment, -1);
1303 
1304   g_signal_connect (text_buffer, "changed",
1305                     G_CALLBACK (comment_entry_callback),
1306                     NULL);
1307 
1308   /*  additional animated gif parameter settings  */
1309   file_gif_toggle_button_init (builder, "loop-forever",
1310                                gsvals.loop, &gsvals.loop);
1311 
1312   /* default_delay entry field */
1313   file_gif_spin_button_int_init (builder, "delay-spin",
1314                                  gsvals.default_delay, &gsvals.default_delay);
1315 
1316   /* Disposal selector */
1317   file_gif_combo_box_int_init (builder, "dispose-combo",
1318                                gsvals.default_dispose, &gsvals.default_dispose,
1319                                _("I don't care"),
1320                                DISPOSE_UNSPECIFIED,
1321                                _("Cumulative layers (combine)"),
1322                                DISPOSE_COMBINE,
1323                                _("One frame per layer (replace)"),
1324                                DISPOSE_REPLACE,
1325                                NULL);
1326 
1327   /* The "Always use default values" toggles */
1328   file_gif_toggle_button_init (builder, "use-default-delay",
1329                                gsvals.always_use_default_delay,
1330                                &gsvals.always_use_default_delay);
1331   file_gif_toggle_button_init (builder, "use-default-dispose",
1332                                gsvals.always_use_default_dispose,
1333                                &gsvals.always_use_default_dispose);
1334 
1335   frame  = GTK_WIDGET (gtk_builder_get_object (builder, "animation-frame"));
1336   toggle = GTK_WIDGET (gtk_builder_get_object (builder, "as-animation"));
1337   if (! animation_supported)
1338     {
1339       gimp_help_set_help_data (toggle,
1340                                _("You can only export as animation when the "
1341                                  "image has more than one layer. The image "
1342                                  "you are trying to export only has one "
1343                                  "layer."),
1344                                NULL);
1345       /* Make sure the checkbox is not checked from session data. */
1346       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
1347     }
1348   gtk_widget_set_sensitive (toggle, animation_supported);
1349 
1350   g_object_bind_property (toggle, "active",
1351                           frame,  "sensitive",
1352                           G_BINDING_SYNC_CREATE);
1353 
1354   gtk_widget_show (dialog);
1355 
1356   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1357 
1358   gtk_widget_destroy (dialog);
1359 
1360   return run;
1361 }
1362 
1363 static int
colors_to_bpp(gint colors)1364 colors_to_bpp (gint colors)
1365 {
1366   gint bpp;
1367 
1368   if (colors <= 2)
1369     bpp = 1;
1370   else if (colors <= 4)
1371     bpp = 2;
1372   else if (colors <= 8)
1373     bpp = 3;
1374   else if (colors <= 16)
1375     bpp = 4;
1376   else if (colors <= 32)
1377     bpp = 5;
1378   else if (colors <= 64)
1379     bpp = 6;
1380   else if (colors <= 128)
1381     bpp = 7;
1382   else if (colors <= 256)
1383     bpp = 8;
1384   else
1385     {
1386       g_warning ("GIF: colors_to_bpp - Eep! too many colors: %d\n", colors);
1387       return 8;
1388     }
1389 
1390   return bpp;
1391 }
1392 
1393 static int
bpp_to_colors(gint bpp)1394 bpp_to_colors (gint bpp)
1395 {
1396   gint colors;
1397 
1398   if (bpp > 8)
1399     {
1400       g_warning ("GIF: bpp_to_colors - Eep! bpp==%d !\n", bpp);
1401       return 256;
1402     }
1403 
1404   colors = 1 << bpp;
1405 
1406   return colors;
1407 }
1408 
1409 
1410 
1411 static gint
get_pixel(gint x,gint y)1412 get_pixel (gint x,
1413            gint y)
1414 {
1415   return *(pixels + (rowstride * (long) y) + (long) x);
1416 }
1417 
1418 
1419 /*****************************************************************************
1420  *
1421  * GIFENCODE.C    - GIF Image compression interface
1422  *
1423  * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
1424  *            BitsPerPixel, Red, Green, Blue, get_pixel )
1425  *
1426  *****************************************************************************/
1427 
1428 static gint  Width, Height;
1429 static gint  curx, cury;
1430 static glong CountDown;
1431 static gint  Pass = 0;
1432 
1433 /*
1434  * Bump the 'curx' and 'cury' to point to the next pixel
1435  */
1436 static void
bump_pixel(void)1437 bump_pixel (void)
1438 {
1439   /*
1440    * Bump the current X position
1441    */
1442   curx++;
1443 
1444   /*
1445    * If we are at the end of a scan line, set curx back to the beginning
1446    * If we are interlaced, bump the cury to the appropriate spot,
1447    * otherwise, just increment it.
1448    */
1449   if (curx == Width)
1450     {
1451       cur_progress++;
1452 
1453       if ((cur_progress % 20) == 0)
1454         gimp_progress_update ((gdouble) cur_progress / (gdouble) max_progress);
1455 
1456       curx = 0;
1457 
1458       if (! Interlace)
1459         ++cury;
1460       else
1461         {
1462           switch (Pass)
1463             {
1464 
1465             case 0:
1466               cury += 8;
1467               if (cury >= Height)
1468                 {
1469                   Pass++;
1470                   cury = 4;
1471                 }
1472               break;
1473 
1474             case 1:
1475               cury += 8;
1476               if (cury >= Height)
1477                 {
1478                   Pass++;
1479                   cury = 2;
1480                 }
1481               break;
1482 
1483             case 2:
1484               cury += 4;
1485               if (cury >= Height)
1486                 {
1487                   Pass++;
1488                   cury = 1;
1489                 }
1490               break;
1491 
1492             case 3:
1493               cury += 2;
1494               break;
1495             }
1496         }
1497     }
1498 }
1499 
1500 /*
1501  * Return the next pixel from the image
1502  */
1503 static gint
gif_next_pixel(ifunptr getpixel)1504 gif_next_pixel (ifunptr getpixel)
1505 {
1506   gint r;
1507 
1508   if (CountDown == 0)
1509     return EOF;
1510 
1511   --CountDown;
1512 
1513   r = (*getpixel) (curx, cury);
1514 
1515   bump_pixel ();
1516 
1517   return r;
1518 }
1519 
1520 /* public */
1521 
1522 static gboolean
gif_encode_header(GOutputStream * output,gboolean gif89,gint GWidth,gint GHeight,gint Background,gint BitsPerPixel,gint Red[],gint Green[],gint Blue[],ifunptr get_pixel,GError ** error)1523 gif_encode_header (GOutputStream *output,
1524                    gboolean       gif89,
1525                    gint           GWidth,
1526                    gint           GHeight,
1527                    gint           Background,
1528                    gint           BitsPerPixel,
1529                    gint           Red[],
1530                    gint           Green[],
1531                    gint           Blue[],
1532                    ifunptr        get_pixel,
1533                    GError       **error)
1534 {
1535   gint B;
1536   gint RWidth, RHeight;
1537   gint Resolution;
1538   gint ColorMapSize;
1539   gint i;
1540 
1541   ColorMapSize = 1 << BitsPerPixel;
1542 
1543   RWidth = Width = GWidth;
1544   RHeight = Height = GHeight;
1545 
1546   Resolution = BitsPerPixel;
1547 
1548   /*
1549    * Calculate number of bits we are expecting
1550    */
1551   CountDown = (long) Width *(long) Height;
1552 
1553   /*
1554    * Indicate which pass we are on (if interlace)
1555    */
1556   Pass = 0;
1557 
1558   /*
1559    * Set up the current x and y position
1560    */
1561   curx = cury = 0;
1562 
1563   /*
1564    * Write the Magic header
1565    */
1566   if (! put_string (output, gif89 ? "GIF89a" : "GIF87a", error))
1567     return FALSE;
1568 
1569   /*
1570    * Write out the screen width and height
1571    */
1572   if (! put_word (output, RWidth,  error) ||
1573       ! put_word (output, RHeight, error))
1574     return FALSE;
1575 
1576   /*
1577    * Indicate that there is a global color map
1578    */
1579   B = 0x80;                        /* Yes, there is a color map */
1580 
1581   /*
1582    * OR in the resolution
1583    */
1584   B |= (Resolution - 1) << 5;
1585 
1586   /*
1587    * OR in the Bits per Pixel
1588    */
1589   B |= (BitsPerPixel - 1);
1590 
1591   /*
1592    * Write it out
1593    */
1594   if (! put_byte (output, B, error))
1595     return FALSE;
1596 
1597   /*
1598    * Write out the Background color
1599    */
1600   if (! put_byte (output, Background, error))
1601     return FALSE;
1602 
1603   /*
1604    * Byte of 0's (future expansion)
1605    */
1606   if (! put_byte (output, 0, error))
1607     return FALSE;
1608 
1609   /*
1610    * Write out the Global Color Map
1611    */
1612   for (i = 0; i < ColorMapSize; i++)
1613     {
1614       if (! put_byte (output, Red[i],   error) ||
1615           ! put_byte (output, Green[i], error) ||
1616           ! put_byte (output, Blue[i],  error))
1617         return FALSE;
1618     }
1619 
1620   return TRUE;
1621 }
1622 
1623 
1624 static gboolean
gif_encode_graphic_control_ext(GOutputStream * output,int Disposal,int Delay89,int NumFramesInImage,int GWidth,int GHeight,int Transparent,int BitsPerPixel,ifunptr get_pixel,GError ** error)1625 gif_encode_graphic_control_ext (GOutputStream *output,
1626                                 int            Disposal,
1627                                 int            Delay89,
1628                                 int            NumFramesInImage,
1629                                 int            GWidth,
1630                                 int            GHeight,
1631                                 int            Transparent,
1632                                 int            BitsPerPixel,
1633                                 ifunptr        get_pixel,
1634                                 GError       **error)
1635 {
1636   Width = GWidth;
1637   Height = GHeight;
1638 
1639   /*
1640    * Calculate number of bits we are expecting
1641    */
1642   CountDown = (long) Width *(long) Height;
1643 
1644   /*
1645    * Indicate which pass we are on (if interlace)
1646    */
1647   Pass = 0;
1648 
1649   /*
1650    * Set up the current x and y position
1651    */
1652   curx = cury = 0;
1653 
1654   /*
1655    * Write out extension for transparent color index, if necessary.
1656    */
1657   if ( (Transparent >= 0) || (NumFramesInImage > 1) )
1658     {
1659       /* Extension Introducer - fixed. */
1660       if (! put_byte (output, '!', error))
1661         return FALSE;
1662 
1663       /* Graphic Control Label - fixed. */
1664       if (! put_byte (output, 0xf9, error))
1665         return FALSE;
1666 
1667       /* Block Size - fixed. */
1668       if (! put_byte (output, 4, error))
1669         return FALSE;
1670 
1671       /* Packed Fields - XXXdddut (d=disposal, u=userInput, t=transFlag) */
1672       /*                    s8421                                        */
1673       if (! put_byte (output,
1674                       ((Transparent >= 0) ? 0x01 : 0x00) /* TRANSPARENCY */
1675 
1676                       /* DISPOSAL */
1677                       | ((NumFramesInImage > 1) ? (Disposal << 2) : 0x00 ),
1678                       /* 0x03 or 0x01 build frames cumulatively */
1679                       /* 0x02 clears frame before drawing */
1680                       /* 0x00 'don't care' */
1681 
1682                       error))
1683         return FALSE;
1684 
1685       if (! put_word (output, Delay89, error))
1686         return FALSE;
1687 
1688       if (! put_byte (output, Transparent, error) ||
1689           ! put_byte (output, 0, error))
1690         return FALSE;
1691     }
1692 
1693   return TRUE;
1694 }
1695 
1696 
1697 static gboolean
gif_encode_image_data(GOutputStream * output,int GWidth,int GHeight,int GInterlace,int BitsPerPixel,ifunptr get_pixel,gint offset_x,gint offset_y,GError ** error)1698 gif_encode_image_data (GOutputStream *output,
1699                        int            GWidth,
1700                        int            GHeight,
1701                        int            GInterlace,
1702                        int            BitsPerPixel,
1703                        ifunptr        get_pixel,
1704                        gint           offset_x,
1705                        gint           offset_y,
1706                        GError       **error)
1707 {
1708   gint LeftOfs, TopOfs;
1709   gint InitCodeSize;
1710 
1711   Interlace = GInterlace;
1712 
1713   Width   = GWidth;
1714   Height  = GHeight;
1715   LeftOfs = (gint) offset_x;
1716   TopOfs  = (gint) offset_y;
1717 
1718   /*
1719    * Calculate number of bits we are expecting
1720    */
1721   CountDown = (long) Width * (long) Height;
1722 
1723   /*
1724    * Indicate which pass we are on (if interlace)
1725    */
1726   Pass = 0;
1727 
1728   /*
1729    * The initial code size
1730    */
1731   if (BitsPerPixel <= 1)
1732     InitCodeSize = 2;
1733   else
1734     InitCodeSize = BitsPerPixel;
1735 
1736   /*
1737    * Set up the current x and y position
1738    */
1739   curx = cury = 0;
1740 
1741   /*
1742    * Write an Image separator
1743    */
1744   if (! put_byte (output, ',', error))
1745     return FALSE;
1746 
1747   /*
1748    * Write the Image header
1749    */
1750 
1751   if (! put_word (output, LeftOfs, error) ||
1752       ! put_word (output, TopOfs,  error) ||
1753       ! put_word (output, Width,   error) ||
1754       ! put_word (output, Height,  error))
1755     return FALSE;
1756 
1757   /*
1758    * Write out whether or not the image is interlaced
1759    */
1760   if (Interlace)
1761     {
1762       if (! put_byte (output, 0x40, error))
1763         return FALSE;
1764     }
1765   else
1766     {
1767       if (! put_byte (output, 0x00, error))
1768         return FALSE;
1769     }
1770 
1771   /*
1772    * Write out the initial code size
1773    */
1774   if (! put_byte (output, InitCodeSize, error))
1775     return FALSE;
1776 
1777   /*
1778    * Go and actually compress the data
1779    */
1780   if (! compress (output, InitCodeSize + 1, get_pixel, error))
1781     return FALSE;
1782 
1783   /*
1784    * Write out a Zero-length packet (to end the series)
1785    */
1786   if (! put_byte (output, 0, error))
1787     return FALSE;
1788 
1789 #if 0
1790   /***************************/
1791   Interlace = GInterlace;
1792   Width = GWidth;
1793   Height = GHeight;
1794   LeftOfs = TopOfs = 0;
1795 
1796   CountDown = (long) Width *(long) Height;
1797   Pass = 0;
1798   /*
1799    * The initial code size
1800    */
1801   if (BitsPerPixel <= 1)
1802     InitCodeSize = 2;
1803   else
1804     InitCodeSize = BitsPerPixel;
1805   /*
1806    * Set up the current x and y position
1807    */
1808   curx = cury = 0;
1809 #endif
1810 
1811   return TRUE;
1812 }
1813 
1814 
1815 static gboolean
gif_encode_close(GOutputStream * output,GError ** error)1816 gif_encode_close (GOutputStream  *output,
1817                   GError        **error)
1818 {
1819   /*
1820    * Write the GIF file terminator
1821    */
1822   if (! put_byte (output, ';', error))
1823     return FALSE;
1824 
1825   /*
1826    * And close the file
1827    */
1828   return g_output_stream_close (output, NULL, error);
1829 }
1830 
1831 
1832 static gboolean
gif_encode_loop_ext(GOutputStream * output,guint num_loops,GError ** error)1833 gif_encode_loop_ext (GOutputStream  *output,
1834                      guint           num_loops,
1835                      GError        **error)
1836 {
1837   return (put_byte   (output, 0x21, error) &&
1838           put_byte   (output, 0xff, error) &&
1839           put_byte   (output, 0x0b, error) &&
1840           put_string (output, "NETSCAPE2.0", error) &&
1841           put_byte   (output, 0x03, error) &&
1842           put_byte   (output, 0x01, error) &&
1843           put_word   (output, num_loops, error) &&
1844           put_byte   (output, 0x00, error));
1845 
1846   /* NOTE: num_loops == 0 means 'loop infinitely' */
1847 }
1848 
1849 
1850 static gboolean
gif_encode_comment_ext(GOutputStream * output,const gchar * comment,GError ** error)1851 gif_encode_comment_ext (GOutputStream  *output,
1852                         const gchar    *comment,
1853                         GError        **error)
1854 {
1855   if (!comment || !*comment)
1856     return TRUE;
1857 
1858   if (strlen (comment) > 240)
1859     {
1860       g_printerr ("GIF: warning:"
1861                   "comment too large - comment block not written.\n");
1862       return TRUE;
1863     }
1864 
1865   return (put_byte   (output, 0x21, error) &&
1866           put_byte   (output, 0xfe, error) &&
1867           put_byte   (output, strlen (comment), error) &&
1868           put_string (output, comment, error) &&
1869           put_byte   (output, 0x00, error));
1870 }
1871 
1872 
1873 /*
1874  * Write stuff to the GIF file
1875  */
1876 static gboolean
put_byte(GOutputStream * output,guchar b,GError ** error)1877 put_byte (GOutputStream  *output,
1878           guchar          b,
1879           GError        **error)
1880 {
1881   return g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (output),
1882                                         b, NULL, error);
1883 }
1884 
1885 static gboolean
put_word(GOutputStream * output,gint w,GError ** error)1886 put_word (GOutputStream  *output,
1887           gint            w,
1888           GError        **error)
1889 {
1890   return g_data_output_stream_put_int16 (G_DATA_OUTPUT_STREAM (output),
1891                                          w, NULL, error);
1892 }
1893 
1894 static gboolean
put_string(GOutputStream * output,const gchar * s,GError ** error)1895 put_string (GOutputStream  *output,
1896             const gchar    *s,
1897             GError        **error)
1898 {
1899   return g_data_output_stream_put_string (G_DATA_OUTPUT_STREAM (output),
1900                                           s, NULL, error);
1901 }
1902 
1903 
1904 /***************************************************************************
1905  *
1906  *  GIFCOMPR.C       - GIF Image compression routines
1907  *
1908  *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
1909  *  David Rowley (mgardi@watdcsu.waterloo.edu)
1910  *
1911  ***************************************************************************/
1912 
1913 /*
1914  * General DEFINEs
1915  */
1916 
1917 #define GIF_BITS   12
1918 
1919 #define HSIZE    5003                /* 80% occupancy */
1920 
1921 /*
1922  * GIF Image compression - modified 'compress'
1923  *
1924  * Based on: compress.c - File compression ala IEEE Computer, June 1984.
1925  *
1926  * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
1927  *              Jim McKie               (decvax!mcvax!jim)
1928  *              Steve Davies            (decvax!vax135!petsd!peora!srd)
1929  *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
1930  *              James A. Woods          (decvax!ihnp4!ames!jaw)
1931  *              Joe Orost               (decvax!vax135!petsd!joe)
1932  *
1933  */
1934 
1935 static gint n_bits;                /* number of bits/code */
1936 static gint maxbits = GIF_BITS;    /* user settable max # bits/code */
1937 static gint maxcode;               /* maximum code, given n_bits */
1938 static gint maxmaxcode = (gint) 1 << GIF_BITS;        /* should NEVER generate this code */
1939 #ifdef COMPATIBLE                /* But wrong! */
1940 #define MAXCODE(Mn_bits)        ((gint) 1 << (Mn_bits) - 1)
1941 #else /*COMPATIBLE */
1942 #define MAXCODE(Mn_bits)        (((gint) 1 << (Mn_bits)) - 1)
1943 #endif /*COMPATIBLE */
1944 
1945 static glong   htab[HSIZE];
1946 static gushort codetab[HSIZE];
1947 #define HashTabOf(i)       htab[i]
1948 #define CodeTabOf(i)    codetab[i]
1949 
1950 static const gint hsize = HSIZE; /* the original reason for this being
1951                                     variable was "for dynamic table sizing",
1952                                     but since it was never actually changed
1953                                     I made it const   --Adam. */
1954 
1955 static gint free_ent = 0;        /* first unused entry */
1956 
1957 /*
1958  * block compression parameters -- after all codes are used up,
1959  * and compression rate changes, start over.
1960  */
1961 static gint  clear_flg = 0;
1962 
1963 static gint  offset;
1964 static glong in_count  = 1;        /* length of input */
1965 static glong out_count = 0;        /* # of codes output (for debugging) */
1966 
1967 /*
1968  * compress stdin to stdout
1969  *
1970  * Algorithm:  use open addressing double hashing (no chaining) on the
1971  * prefix code / next character combination.  We do a variant of Knuth's
1972  * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
1973  * secondary probe.  Here, the modular division first probe is gives way
1974  * to a faster exclusive-or manipulation.  Also do block compression with
1975  * an adaptive reset, whereby the code table is cleared when the compression
1976  * ratio decreases, but after the table fills.  The variable-length output
1977  * codes are re-sized at this point, and a special CLEAR code is generated
1978  * for the decompressor.  Late addition:  construct the table according to
1979  * file size for noticeable speed improvement on small files.  Please direct
1980  * questions about this implementation to ames!jaw.
1981  */
1982 
1983 static gint g_init_bits;
1984 
1985 static gint ClearCode;
1986 static gint EOFCode;
1987 
1988 static gulong cur_accum;
1989 static gint   cur_bits;
1990 
1991 static gulong masks[] =
1992 {
1993   0x0000, 0x0001, 0x0003, 0x0007,
1994   0x000F, 0x001F, 0x003F, 0x007F,
1995   0x00FF, 0x01FF, 0x03FF, 0x07FF,
1996   0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
1997   0xFFFF
1998 };
1999 
2000 
2001 static gboolean
compress(GOutputStream * output,gint init_bits,ifunptr ReadValue,GError ** error)2002 compress (GOutputStream  *output,
2003           gint            init_bits,
2004           ifunptr         ReadValue,
2005           GError        **error)
2006 {
2007   if (FALSE)
2008     return no_compress (output, init_bits, ReadValue, error);
2009   else if (FALSE)
2010     return rle_compress (output, init_bits, ReadValue, error);
2011   else
2012     return normal_compress (output, init_bits, ReadValue, error);
2013 }
2014 
2015 static gboolean
no_compress(GOutputStream * output,gint init_bits,ifunptr ReadValue,GError ** error)2016 no_compress (GOutputStream  *output,
2017              gint            init_bits,
2018              ifunptr         ReadValue,
2019              GError        **error)
2020 {
2021   glong fcode;
2022   gint  i /* = 0 */ ;
2023   gint  c;
2024   gint  ent;
2025   gint  hsize_reg;
2026   gint  hshift;
2027 
2028   /*
2029    * Set up the globals:  g_init_bits - initial number of bits
2030    */
2031   g_init_bits = init_bits;
2032 
2033   cur_bits = 0;
2034   cur_accum = 0;
2035 
2036   /*
2037    * Set up the necessary values
2038    */
2039   offset = 0;
2040   out_count = 0;
2041   clear_flg = 0;
2042   in_count = 1;
2043 
2044   ClearCode = (1 << (init_bits - 1));
2045   EOFCode = ClearCode + 1;
2046   free_ent = ClearCode + 2;
2047 
2048 
2049   /* Had some problems here... should be okay now.  --Adam */
2050   n_bits = g_init_bits;
2051   maxcode = MAXCODE (n_bits);
2052 
2053 
2054   char_init();
2055 
2056   ent = gif_next_pixel (ReadValue);
2057 
2058   hshift = 0;
2059   for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
2060     ++hshift;
2061   hshift = 8 - hshift;                /* set hash code range bound */
2062 
2063   hsize_reg = hsize;
2064   cl_hash ((glong) hsize_reg);        /* clear hash table */
2065 
2066   if (! output_code (output, (gint) ClearCode, error))
2067     return FALSE;
2068 
2069   while ((c = gif_next_pixel (ReadValue)) != EOF)
2070     {
2071       ++in_count;
2072 
2073       fcode = (long) (((long) c << maxbits) + ent);
2074       i = (((gint) c << hshift) ^ ent);        /* xor hashing */
2075 
2076       if (! output_code (output, (gint) ent, error))
2077         return FALSE;
2078 
2079       ++out_count;
2080       ent = c;
2081 
2082       if (free_ent < maxmaxcode)
2083         {
2084           CodeTabOf (i) = free_ent++;        /* code -> hashtable */
2085           HashTabOf (i) = fcode;
2086         }
2087       else
2088         {
2089           if (! cl_block (output, error))
2090             return FALSE;
2091         }
2092     }
2093 
2094   /*
2095    * Put out the final code.
2096    */
2097   if (! output_code (output, (gint) ent, error))
2098     return FALSE;
2099 
2100   ++out_count;
2101 
2102   if (! output_code (output, (gint) EOFCode, error))
2103     return FALSE;
2104 
2105   return TRUE;
2106 }
2107 
2108 static gboolean
rle_compress(GOutputStream * output,gint init_bits,ifunptr ReadValue,GError ** error)2109 rle_compress (GOutputStream  *output,
2110               gint            init_bits,
2111               ifunptr         ReadValue,
2112               GError        **error)
2113 {
2114   glong fcode;
2115   gint  i /* = 0 */ ;
2116   gint  c, last;
2117   gint  ent;
2118   gint  disp;
2119   gint  hsize_reg;
2120   gint  hshift;
2121 
2122   /*
2123    * Set up the globals:  g_init_bits - initial number of bits
2124    */
2125   g_init_bits = init_bits;
2126 
2127   cur_bits = 0;
2128   cur_accum = 0;
2129 
2130   /*
2131    * Set up the necessary values
2132    */
2133   offset = 0;
2134   out_count = 0;
2135   clear_flg = 0;
2136   in_count = 1;
2137 
2138   ClearCode = (1 << (init_bits - 1));
2139   EOFCode = ClearCode + 1;
2140   free_ent = ClearCode + 2;
2141 
2142 
2143   /* Had some problems here... should be okay now.  --Adam */
2144   n_bits = g_init_bits;
2145   maxcode = MAXCODE (n_bits);
2146 
2147 
2148   char_init ();
2149 
2150   last = ent = gif_next_pixel (ReadValue);
2151 
2152   hshift = 0;
2153   for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
2154     ++hshift;
2155   hshift = 8 - hshift;                /* set hash code range bound */
2156 
2157   hsize_reg = hsize;
2158   cl_hash ((glong) hsize_reg);        /* clear hash table */
2159 
2160   if (! output_code (output, (gint) ClearCode, error))
2161     return FALSE;
2162 
2163 
2164   while ((c = gif_next_pixel (ReadValue)) != EOF)
2165     {
2166       ++in_count;
2167 
2168       fcode = (long) (((long) c << maxbits) + ent);
2169       i = (((gint) c << hshift) ^ ent);        /* xor hashing */
2170 
2171 
2172       if (last == c) {
2173         if (HashTabOf (i) == fcode)
2174           {
2175             ent = CodeTabOf (i);
2176             continue;
2177           }
2178         else if ((long) HashTabOf (i) < 0)        /* empty slot */
2179           goto nomatch;
2180         disp = hsize_reg - i;        /* secondary hash (after G. Knott) */
2181         if (i == 0)
2182           disp = 1;
2183       probe:
2184         if ((i -= disp) < 0)
2185           i += hsize_reg;
2186 
2187         if (HashTabOf (i) == fcode)
2188           {
2189             ent = CodeTabOf (i);
2190             continue;
2191           }
2192         if ((long) HashTabOf (i) > 0)
2193           goto probe;
2194         }
2195     nomatch:
2196       if (! output_code (output, (gint) ent, error))
2197         return FALSE;
2198 
2199       ++out_count;
2200       last = ent = c;
2201       if (free_ent < maxmaxcode)
2202         {
2203           CodeTabOf (i) = free_ent++;        /* code -> hashtable */
2204           HashTabOf (i) = fcode;
2205         }
2206       else
2207         {
2208           if (! cl_block (output, error))
2209             return FALSE;
2210         }
2211     }
2212 
2213   /*
2214    * Put out the final code.
2215    */
2216   if (! output_code (output, (gint) ent, error))
2217     return FALSE;
2218 
2219   ++out_count;
2220 
2221   if (! output_code (output, (gint) EOFCode, error))
2222     return FALSE;
2223 
2224   return TRUE;
2225 }
2226 
2227 static gboolean
normal_compress(GOutputStream * output,gint init_bits,ifunptr ReadValue,GError ** error)2228 normal_compress (GOutputStream  *output,
2229                  gint            init_bits,
2230                  ifunptr         ReadValue,
2231                  GError        **error)
2232 {
2233   glong fcode;
2234   gint  i /* = 0 */ ;
2235   gint  c;
2236   gint  ent;
2237   gint  disp;
2238   gint  hsize_reg;
2239   gint  hshift;
2240 
2241   /*
2242    * Set up the globals:  g_init_bits - initial number of bits
2243    */
2244   g_init_bits = init_bits;
2245 
2246   cur_bits = 0;
2247   cur_accum = 0;
2248 
2249   /*
2250    * Set up the necessary values
2251    */
2252   offset = 0;
2253   out_count = 0;
2254   clear_flg = 0;
2255   in_count = 1;
2256 
2257   ClearCode = (1 << (init_bits - 1));
2258   EOFCode = ClearCode + 1;
2259   free_ent = ClearCode + 2;
2260 
2261 
2262   /* Had some problems here... should be okay now.  --Adam */
2263   n_bits = g_init_bits;
2264   maxcode = MAXCODE (n_bits);
2265 
2266 
2267   char_init();
2268 
2269   ent = gif_next_pixel (ReadValue);
2270 
2271   hshift = 0;
2272   for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
2273     ++hshift;
2274   hshift = 8 - hshift;                /* set hash code range bound */
2275 
2276   hsize_reg = hsize;
2277   cl_hash ((glong) hsize_reg);        /* clear hash table */
2278 
2279   if (! output_code (output, (gint) ClearCode, error))
2280     return FALSE;
2281 
2282 
2283   while ((c = gif_next_pixel (ReadValue)) != EOF)
2284     {
2285       ++in_count;
2286 
2287       fcode = (long) (((long) c << maxbits) + ent);
2288       i = (((gint) c << hshift) ^ ent);        /* xor hashing */
2289 
2290       if (HashTabOf (i) == fcode)
2291         {
2292           ent = CodeTabOf (i);
2293           continue;
2294         }
2295       else if ((long) HashTabOf (i) < 0)        /* empty slot */
2296         goto nomatch;
2297       disp = hsize_reg - i;        /* secondary hash (after G. Knott) */
2298       if (i == 0)
2299         disp = 1;
2300     probe:
2301       if ((i -= disp) < 0)
2302         i += hsize_reg;
2303 
2304       if (HashTabOf (i) == fcode)
2305         {
2306           ent = CodeTabOf (i);
2307           continue;
2308         }
2309       if ((long) HashTabOf (i) > 0)
2310         goto probe;
2311     nomatch:
2312       if (! output_code (output, (gint) ent, error))
2313         return FALSE;
2314 
2315       ++out_count;
2316       ent = c;
2317       if (free_ent < maxmaxcode)
2318         {
2319           CodeTabOf (i) = free_ent++;        /* code -> hashtable */
2320           HashTabOf (i) = fcode;
2321         }
2322       else
2323         {
2324           if (! cl_block (output, error))
2325             return FALSE;
2326         }
2327     }
2328 
2329   /*
2330    * Put out the final code.
2331    */
2332   if (! output_code (output, (gint) ent, error))
2333     return FALSE;
2334 
2335   ++out_count;
2336 
2337   if (! output_code (output, (gint) EOFCode, error))
2338     return FALSE;
2339 
2340   return TRUE;
2341 }
2342 
2343 
2344 /*****************************************************************
2345  * TAG( output )
2346  *
2347  * Output the given code.
2348  * Inputs:
2349  *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
2350  *              that n_bits =< (long)wordsize - 1.
2351  * Outputs:
2352  *      Outputs code to the file.
2353  * Assumptions:
2354  *      Chars are 8 bits long.
2355  * Algorithm:
2356  *      Maintain a GIF_BITS character long buffer (so that 8 codes will
2357  * fit in it exactly).  Use the VAX insv instruction to insert each
2358  * code in turn.  When the buffer fills up empty it and start over.
2359  */
2360 
2361 static gboolean
output_code(GOutputStream * output,gint code,GError ** error)2362 output_code (GOutputStream  *output,
2363              gint            code,
2364              GError        **error)
2365 {
2366   cur_accum &= masks[cur_bits];
2367 
2368   if (cur_bits > 0)
2369     cur_accum |= ((long) code << cur_bits);
2370   else
2371     cur_accum = code;
2372 
2373   cur_bits += n_bits;
2374 
2375   while (cur_bits >= 8)
2376     {
2377       if (! char_out (output, (guchar) (cur_accum & 0xff), error))
2378         return FALSE;
2379 
2380       cur_accum >>= 8;
2381       cur_bits -= 8;
2382     }
2383 
2384   /*
2385    * If the next entry is going to be too big for the code size,
2386    * then increase it, if possible.
2387    */
2388   if (free_ent > maxcode || clear_flg)
2389     {
2390       if (clear_flg)
2391         {
2392           maxcode = MAXCODE (n_bits = g_init_bits);
2393           clear_flg = 0;
2394         }
2395       else
2396         {
2397           ++n_bits;
2398           if (n_bits == maxbits)
2399             maxcode = maxmaxcode;
2400           else
2401             maxcode = MAXCODE (n_bits);
2402         }
2403     }
2404 
2405   if (code == EOFCode)
2406     {
2407       /*
2408        * At EOF, write the rest of the buffer.
2409        */
2410       while (cur_bits > 0)
2411         {
2412           if (! char_out (output, (guchar) (cur_accum & 0xff), error))
2413             return FALSE;
2414 
2415           cur_accum >>= 8;
2416           cur_bits -= 8;
2417         }
2418 
2419       if (! char_flush (output, error))
2420         return FALSE;
2421     }
2422 
2423   return TRUE;
2424 }
2425 
2426 /*
2427  * Clear out the hash table
2428  */
2429 static gboolean
cl_block(GOutputStream * output,GError ** error)2430 cl_block (GOutputStream  *output,
2431           GError        **error) /* table clear for block compress */
2432 {
2433   cl_hash ((glong) hsize);
2434   free_ent = ClearCode + 2;
2435   clear_flg = 1;
2436 
2437   return output_code (output, (gint) ClearCode, error);
2438 }
2439 
2440 static void
cl_hash(glong hsize)2441 cl_hash (glong hsize)        /* reset code table */
2442 {
2443   glong *htab_p = htab + hsize;
2444 
2445   long i;
2446   long m1 = -1;
2447 
2448   i = hsize - 16;
2449   do
2450     {                                /* might use Sys V memset(3) here */
2451       *(htab_p - 16) = m1;
2452       *(htab_p - 15) = m1;
2453       *(htab_p - 14) = m1;
2454       *(htab_p - 13) = m1;
2455       *(htab_p - 12) = m1;
2456       *(htab_p - 11) = m1;
2457       *(htab_p - 10) = m1;
2458       *(htab_p - 9) = m1;
2459       *(htab_p - 8) = m1;
2460       *(htab_p - 7) = m1;
2461       *(htab_p - 6) = m1;
2462       *(htab_p - 5) = m1;
2463       *(htab_p - 4) = m1;
2464       *(htab_p - 3) = m1;
2465       *(htab_p - 2) = m1;
2466       *(htab_p - 1) = m1;
2467       htab_p -= 16;
2468     }
2469   while ((i -= 16) >= 0);
2470 
2471   for (i += 16; i > 0; --i)
2472     *--htab_p = m1;
2473 }
2474 
2475 
2476 /******************************************************************************
2477  * GIF Specific routines
2478  ******************************************************************************/
2479 
2480 /*
2481  * Number of characters so far in this 'packet'
2482  */
2483 static int a_count;
2484 
2485 /*
2486  * Set up the 'byte output' routine
2487  */
2488 static void
char_init(void)2489 char_init (void)
2490 {
2491   a_count = 0;
2492 }
2493 
2494 /*
2495  * Define the storage for the packet accumulator
2496  */
2497 static char accum[256];
2498 
2499 /*
2500  * Add a character to the end of the current packet, and if it is 254
2501  * characters, flush the packet to disk.
2502  */
2503 static gboolean
char_out(GOutputStream * output,gint c,GError ** error)2504 char_out (GOutputStream  *output,
2505           gint            c,
2506           GError        **error)
2507 {
2508   accum[a_count++] = c;
2509 
2510   if (a_count >= 254)
2511     return char_flush (output, error);
2512 
2513   return TRUE;
2514 }
2515 
2516 /*
2517  * Flush the packet to disk, and reset the accumulator
2518  */
2519 static gboolean
char_flush(GOutputStream * output,GError ** error)2520 char_flush (GOutputStream  *output,
2521             GError        **error)
2522 {
2523   if (a_count > 0)
2524     {
2525       if (! put_byte (output, a_count, error))
2526         return FALSE;
2527 
2528       if (! g_output_stream_write_all (output, accum, a_count,
2529                                        NULL, NULL, error))
2530         return FALSE;
2531 
2532       a_count = 0;
2533     }
2534 
2535   return TRUE;
2536 }
2537 
2538 
2539 /*  Save interface functions  */
2540 
2541 static void
comment_entry_callback(GtkTextBuffer * buffer)2542 comment_entry_callback (GtkTextBuffer *buffer)
2543 {
2544   GtkTextIter   start_iter;
2545   GtkTextIter   end_iter;
2546   gchar        *text;
2547 
2548   gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
2549   text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
2550 
2551 #define MAX_COMMENT 240
2552 
2553   if (strlen (text) > MAX_COMMENT)
2554     {
2555       /* translators: the %d is *always* 240 here */
2556       g_message (_("The default comment is limited to %d characters."),
2557                  MAX_COMMENT);
2558 
2559       gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, MAX_COMMENT - 1);
2560       gtk_text_buffer_get_end_iter (buffer, &end_iter);
2561 
2562       /*  this calls us recursivaly, but in the else branch
2563        */
2564       gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
2565     }
2566   else
2567     {
2568       g_free (globalcomment);
2569       globalcomment = g_strdup (text);
2570       comment_was_edited = TRUE;
2571     }
2572 
2573   g_free (text);
2574 }
2575