1 /*
2  * Animation Optimizer plug-in version 1.1.2
3  *
4  * (c) Adam D. Moss, 1997-2003
5  *     adam@gimp.org
6  *     adam@foxbox.org
7  *
8  * GIMP - The GNU Image Manipulation Program
9  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
23  */
24 
25 /*
26 #define EXPERIMENTAL_BACKDROP_CODE
27 */
28 
29 
30 #include "config.h"
31 
32 #include <string.h>
33 
34 #include <libgimp/gimp.h>
35 
36 #include "libgimp/stdplugins-intl.h"
37 
38 
39 #define OPTIMIZE_PROC        "plug-in-animationoptimize"
40 #define OPTIMIZE_DIFF_PROC   "plug-in-animationoptimize-diff"
41 #define UNOPTIMIZE_PROC      "plug-in-animationunoptimize"
42 #define REMOVE_BACKDROP_PROC "plug-in-animation-remove-backdrop"
43 #define FIND_BACKDROP_PROC   "plug-in-animation-find-backdrop"
44 
45 
46 typedef enum
47 {
48   DISPOSE_UNDEFINED = 0x00,
49   DISPOSE_COMBINE   = 0x01,
50   DISPOSE_REPLACE   = 0x02
51 } DisposeType;
52 
53 
54 typedef enum
55 {
56   OPOPTIMIZE   = 0L,
57   OPUNOPTIMIZE = 1L,
58   OPFOREGROUND = 2L,
59   OPBACKGROUND = 3L
60 } operatingMode;
61 
62 
63 /* Declare local functions. */
64 static  void query (void);
65 static  void run   (const gchar      *name,
66                     gint              nparams,
67                     const GimpParam  *param,
68                     gint             *nreturn_vals,
69                     GimpParam       **return_vals);
70 
71 static  gint32      do_optimizations    (GimpRunMode  run_mode,
72                                          gboolean     diff_only);
73 
74 /* tag util functions*/
75 static  gint        parse_ms_tag        (const gchar *str);
76 static  DisposeType parse_disposal_tag  (const gchar *str);
77 static  DisposeType get_frame_disposal  (guint        whichframe);
78 static  guint32     get_frame_duration  (guint        whichframe);
79 static  void        remove_disposal_tag (gchar       *dest,
80                                          gchar       *src);
81 static  void        remove_ms_tag       (gchar       *dest,
82                                          gchar       *src);
83 static  gboolean    is_disposal_tag     (const gchar *str,
84                                          DisposeType *disposal,
85                                          gint        *taglength);
86 static  gboolean    is_ms_tag           (const gchar *str,
87                                          gint        *duration,
88                                          gint        *taglength);
89 
90 
91 const GimpPlugInInfo PLUG_IN_INFO =
92 {
93   NULL,  /* init_proc  */
94   NULL,  /* quit_proc  */
95   query, /* query_proc */
96   run,   /* run_proc   */
97 };
98 
99 
100 /* Global widgets'n'stuff */
101 static  guint             width, height;
102 static  gint32            image_id;
103 static  gint32            new_image_id;
104 static  gint32            total_frames;
105 static  gint32           *layers;
106 static  GimpImageBaseType imagetype;
107 static  GimpImageType     drawabletype_alpha;
108 static  guchar            pixelstep;
109 static  guchar           *palette;
110 static  gint              ncolors;
111 static  operatingMode     opmode;
112 
113 
MAIN()114 MAIN ()
115 
116 static void
117 query (void)
118 {
119   static const GimpParamDef args[] =
120   {
121     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
122     { GIMP_PDB_IMAGE,    "image",    "Input image"                  },
123     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)"      }
124   };
125   static const GimpParamDef return_args[] =
126   {
127     { GIMP_PDB_IMAGE, "result", "Resulting image" }
128   };
129 
130   gimp_install_procedure (OPTIMIZE_PROC,
131                           N_("Modify image to reduce size when saved as GIF animation"),
132                           "This procedure applies various optimizations to"
133                           " a GIMP layer-based animation in an attempt to"
134                           " reduce the final file size.  If a frame of the"
135                           " animation can use the 'combine' mode, this"
136                           " procedure attempts to maximize the number of"
137                           " ajdacent pixels having the same color, which"
138                           " improves the compression for some image formats"
139                           " such as GIF or MNG.",
140                           "Adam D. Moss <adam@gimp.org>",
141                           "Adam D. Moss <adam@gimp.org>",
142                           "1997-2003",
143                           N_("Optimize (for _GIF)"),
144                           "RGB*, INDEXED*, GRAY*",
145                           GIMP_PLUGIN,
146                           G_N_ELEMENTS (args),
147                           G_N_ELEMENTS (return_args),
148                           args, return_args);
149 
150   gimp_install_procedure (OPTIMIZE_DIFF_PROC,
151                           N_("Reduce file size where combining layers is possible"),
152                           "This procedure applies various optimizations to"
153                           " a GIMP layer-based animation in an attempt to"
154                           " reduce the final file size.  If a frame of the"
155                           " animation can use the 'combine' mode, this"
156                           " procedure uses a simple difference between the"
157                           " frames.",
158                           "Adam D. Moss <adam@gimp.org>",
159                           "Adam D. Moss <adam@gimp.org>",
160                           "1997-2001",
161                           N_("_Optimize (Difference)"),
162                           "RGB*, INDEXED*, GRAY*",
163                           GIMP_PLUGIN,
164                           G_N_ELEMENTS (args),
165                           G_N_ELEMENTS (return_args),
166                           args, return_args);
167 
168   gimp_install_procedure (UNOPTIMIZE_PROC,
169                           N_("Remove optimization to make editing easier"),
170                           "This procedure 'simplifies' a GIMP layer-based"
171                           " animation that has been optimized for animation. "
172                           "This makes editing the animation much easier.",
173                           "Adam D. Moss <adam@gimp.org>",
174                           "Adam D. Moss <adam@gimp.org>",
175                           "1997-2001",
176                           N_("_Unoptimize"),
177                           "RGB*, INDEXED*, GRAY*",
178                           GIMP_PLUGIN,
179                           G_N_ELEMENTS (args),
180                           G_N_ELEMENTS (return_args),
181                           args, return_args);
182 
183   gimp_plugin_menu_register (OPTIMIZE_PROC,      "<Image>/Filters/Animation");
184   gimp_plugin_menu_register (OPTIMIZE_DIFF_PROC, "<Image>/Filters/Animation");
185   gimp_plugin_menu_register (UNOPTIMIZE_PROC,    "<Image>/Filters/Animation");
186 
187 #ifdef EXPERIMENTAL_BACKDROP_CODE
188   gimp_install_procedure (REMOVE_BACKDROP_PROC,
189                           "This procedure attempts to remove the backdrop"
190                           " from a GIMP layer-based animation, leaving"
191                           " the foreground animation over transparency.",
192                           "",
193                           "Adam D. Moss <adam@gimp.org>",
194                           "Adam D. Moss <adam@gimp.org>",
195                           "2001",
196                           N_("_Remove Backdrop"),
197                           "RGB*, INDEXED*, GRAY*",
198                           GIMP_PLUGIN,
199                           G_N_ELEMENTS (args),
200                           G_N_ELEMENTS (return_args),
201                           args, return_args);
202 
203   gimp_install_procedure (FIND_BACKDROP_PROC,
204                           "This procedure attempts to remove the foreground"
205                           " from a GIMP layer-based animation, leaving"
206                           " a one-layered image containing only the"
207                           " constant backdrop image.",
208                           "",
209                           "Adam D. Moss <adam@gimp.org>",
210                           "Adam D. Moss <adam@gimp.org>",
211                           "2001",
212                           N_("_Find Backdrop"),
213                           "RGB*, INDEXED*, GRAY*",
214                           GIMP_PLUGIN,
215                           G_N_ELEMENTS (args),
216                           G_N_ELEMENTS (return_args),
217                           args, return_args);
218 
219   gimp_plugin_menu_register (REMOVE_BACKDROP_PROC, "<Image>/Filters/Animation");
220   gimp_plugin_menu_register (FIND_BACKDROP_PROC,   "<Image>/Filters/Animation");
221 #endif
222 }
223 
224 static void
run(const gchar * name,gint n_params,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)225 run (const gchar      *name,
226      gint              n_params,
227      const GimpParam  *param,
228      gint             *nreturn_vals,
229      GimpParam       **return_vals)
230 {
231   static GimpParam  values[2];
232   GimpRunMode       run_mode;
233   GimpPDBStatusType status    = GIMP_PDB_SUCCESS;
234   gboolean          diff_only = FALSE;
235 
236   *nreturn_vals = 2;
237   *return_vals  = values;
238 
239   run_mode = param[0].data.d_int32;
240 
241   INIT_I18N ();
242   gegl_init (NULL, NULL);
243 
244   if (run_mode == GIMP_RUN_NONINTERACTIVE && n_params != 3)
245     {
246       status = GIMP_PDB_CALLING_ERROR;
247     }
248 
249   /* Check the procedure name we were called with, to decide
250      what needs to be done. */
251   if (strcmp (name, OPTIMIZE_PROC) == 0)
252     opmode = OPOPTIMIZE;
253   else if (strcmp (name, OPTIMIZE_DIFF_PROC) == 0)
254     {
255       opmode = OPOPTIMIZE;
256       diff_only = TRUE;
257     }
258   else if (strcmp (name, UNOPTIMIZE_PROC) == 0)
259     opmode = OPUNOPTIMIZE;
260   else if (strcmp (name, FIND_BACKDROP_PROC) == 0)
261     opmode = OPBACKGROUND;
262   else if (strcmp (name, REMOVE_BACKDROP_PROC) == 0)
263     opmode = OPFOREGROUND;
264   else
265     g_error("GAH!!!");
266 
267   if (status == GIMP_PDB_SUCCESS)
268     {
269       image_id = param[1].data.d_image;
270 
271       new_image_id = do_optimizations (run_mode, diff_only);
272 
273       if (run_mode != GIMP_RUN_NONINTERACTIVE)
274         gimp_displays_flush();
275     }
276 
277   values[0].type          = GIMP_PDB_STATUS;
278   values[0].data.d_status = status;
279 
280   values[1].type         = GIMP_PDB_IMAGE;
281   values[1].data.d_image = new_image_id;
282 }
283 
284 
285 
286 /* Rendering Functions */
287 
288 static void
total_alpha(guchar * imdata,guint32 numpix,guchar bytespp)289 total_alpha (guchar  *imdata,
290              guint32  numpix,
291              guchar   bytespp)
292 {
293   /* Set image to total-transparency w/black
294    */
295 
296   memset (imdata, 0, numpix * bytespp);
297 }
298 
299 static const Babl *
get_format(gint32 drawable_ID)300 get_format (gint32 drawable_ID)
301 {
302   if (gimp_drawable_is_rgb (drawable_ID))
303     {
304       if (gimp_drawable_has_alpha (drawable_ID))
305         return babl_format ("R'G'B'A u8");
306       else
307         return babl_format ("R'G'B' u8");
308     }
309   else if (gimp_drawable_is_gray (drawable_ID))
310     {
311       if (gimp_drawable_has_alpha (drawable_ID))
312         return babl_format ("Y'A u8");
313       else
314         return babl_format ("Y' u8");
315     }
316 
317   return gimp_drawable_get_format (drawable_ID);
318 }
319 
320 static void
compose_row(gint frame_num,DisposeType dispose,gint row_num,guchar * dest,gint dest_width,gint32 drawable_ID,gboolean cleanup)321 compose_row (gint          frame_num,
322              DisposeType   dispose,
323              gint          row_num,
324              guchar       *dest,
325              gint          dest_width,
326              gint32        drawable_ID,
327              gboolean      cleanup)
328 {
329   static guchar *line_buf = NULL;
330   GeglBuffer    *src_buffer;
331   const Babl    *format;
332   guchar        *srcptr;
333   gint           rawx, rawy, rawbpp, rawwidth, rawheight;
334   gint           i;
335   gboolean       has_alpha;
336 
337   if (cleanup)
338     {
339       if (line_buf)
340         {
341           g_free (line_buf);
342           line_buf = NULL;
343         }
344 
345       return;
346     }
347 
348   if (dispose == DISPOSE_REPLACE)
349     {
350       total_alpha (dest, dest_width, pixelstep);
351     }
352 
353   gimp_drawable_offsets (drawable_ID, &rawx, &rawy);
354 
355   rawwidth  = gimp_drawable_width (drawable_ID);
356   rawheight = gimp_drawable_height (drawable_ID);
357 
358   /* this frame has nothing to give us for this row; return */
359   if (row_num >= rawheight + rawy ||
360       row_num < rawy)
361     return;
362 
363   format = get_format (drawable_ID);
364 
365   has_alpha = gimp_drawable_has_alpha (drawable_ID);
366   rawbpp    = babl_format_get_bytes_per_pixel (format);
367 
368   if (line_buf)
369     {
370       g_free (line_buf);
371       line_buf = NULL;
372     }
373   line_buf = g_malloc (rawwidth * rawbpp);
374 
375   /* Initialise and fetch the raw new frame row */
376 
377   src_buffer = gimp_drawable_get_buffer (drawable_ID);
378 
379   gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row_num - rawy,
380                                                rawwidth, 1), 1.0,
381                    format, line_buf,
382                    GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
383 
384   g_object_unref (src_buffer);
385 
386   /* render... */
387 
388   srcptr = line_buf;
389 
390   for (i=rawx; i<rawwidth+rawx; i++)
391     {
392       if (i>=0 && i<dest_width)
393         {
394           if ((!has_alpha) || ((*(srcptr+rawbpp-1))&128))
395             {
396               gint pi;
397 
398               for (pi = 0; pi < pixelstep-1; pi++)
399                 {
400                   dest[i*pixelstep +pi] = *(srcptr + pi);
401                 }
402 
403               dest[i*pixelstep + pixelstep - 1] = 255;
404             }
405         }
406 
407       srcptr += rawbpp;
408     }
409 }
410 
411 
412 static gint32
do_optimizations(GimpRunMode run_mode,gboolean diff_only)413 do_optimizations (GimpRunMode run_mode,
414                   gboolean    diff_only)
415 {
416   static guchar *rawframe = NULL;
417   guchar        *srcptr;
418   guchar        *destptr;
419   gint           row, this_frame_num;
420   guint32        frame_sizebytes;
421   gint32         new_layer_id;
422   DisposeType    dispose;
423   guchar        *this_frame = NULL;
424   guchar        *last_frame = NULL;
425   guchar        *opti_frame = NULL;
426   guchar        *back_frame = NULL;
427 
428   gint           this_delay;
429   gint           cumulated_delay = 0;
430   gint           last_true_frame = -1;
431   gint           buflen;
432 
433   gchar         *oldlayer_name;
434   gchar         *newlayer_name;
435 
436   gboolean       can_combine;
437 
438   gint32         bbox_top, bbox_bottom, bbox_left, bbox_right;
439   gint32         rbox_top, rbox_bottom, rbox_left, rbox_right;
440 
441   switch (opmode)
442     {
443     case OPUNOPTIMIZE:
444       gimp_progress_init (_("Unoptimizing animation"));
445       break;
446     case OPFOREGROUND:
447       gimp_progress_init (_("Removing animation background"));
448       break;
449     case OPBACKGROUND:
450       gimp_progress_init (_("Finding animation background"));
451       break;
452     case OPOPTIMIZE:
453     default:
454       gimp_progress_init (_("Optimizing animation"));
455       break;
456     }
457 
458   width     = gimp_image_width (image_id);
459   height    = gimp_image_height (image_id);
460   layers    = gimp_image_get_layers (image_id, &total_frames);
461   imagetype = gimp_image_base_type (image_id);
462   pixelstep = (imagetype == GIMP_RGB) ? 4 : 2;
463 
464   drawabletype_alpha = (imagetype == GIMP_RGB) ? GIMP_RGBA_IMAGE :
465     ((imagetype == GIMP_INDEXED) ? GIMP_INDEXEDA_IMAGE : GIMP_GRAYA_IMAGE);
466 
467   frame_sizebytes = width * height * pixelstep;
468 
469   this_frame = g_malloc (frame_sizebytes);
470   last_frame = g_malloc (frame_sizebytes);
471   opti_frame = g_malloc (frame_sizebytes);
472 
473   if (opmode == OPBACKGROUND ||
474       opmode == OPFOREGROUND)
475     back_frame = g_malloc (frame_sizebytes);
476 
477   total_alpha (this_frame, width*height, pixelstep);
478   total_alpha (last_frame, width*height, pixelstep);
479 
480   new_image_id = gimp_image_new(width, height, imagetype);
481   gimp_image_undo_disable (new_image_id);
482 
483   if (imagetype == GIMP_INDEXED)
484     {
485       palette = gimp_image_get_colormap (image_id, &ncolors);
486       gimp_image_set_colormap (new_image_id, palette, ncolors);
487     }
488 
489 #if 1
490   if (opmode == OPBACKGROUND ||
491       opmode == OPFOREGROUND)
492     {
493       /* iterate through all rows of all frames, find statistical
494          mode for each pixel position. */
495       gint     i,j;
496       guchar **these_rows;
497       guchar **red;
498       guchar **green;
499       guchar **blue;
500       guint  **count;
501       guint   *num_colors;
502 
503       these_rows = g_new (guchar *, total_frames);
504       red =        g_new (guchar *, total_frames);
505       green =      g_new (guchar *, total_frames);
506       blue =       g_new (guchar *, total_frames);
507       count =      g_new (guint *, total_frames);
508 
509       num_colors = g_new (guint, width);
510 
511       for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
512         {
513           these_rows[this_frame_num] = g_malloc(width * pixelstep);
514 
515           red[this_frame_num]   = g_new (guchar, width);
516           green[this_frame_num] = g_new (guchar, width);
517           blue[this_frame_num]  = g_new (guchar, width);
518 
519           count[this_frame_num] = g_new0(guint, width);
520         }
521 
522       for (row = 0; row < height; row++)
523         {
524           memset(num_colors, 0, width * sizeof(guint));
525 
526           for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
527             {
528               gint32 drawable_ID = layers[total_frames-(this_frame_num+1)];
529 
530               dispose = get_frame_disposal (this_frame_num);
531 
532               compose_row (this_frame_num,
533                            dispose,
534                            row,
535                            these_rows[this_frame_num],
536                            width,
537                            drawable_ID,
538                            FALSE);
539             }
540 
541           for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
542             {
543               for (i=0; i<width; i++)
544                 {
545                   if (these_rows[this_frame_num][i * pixelstep + pixelstep -1]
546                       >= 128)
547                     {
548                       for (j=0; j<num_colors[i]; j++)
549                         {
550 
551                           switch (pixelstep)
552                             {
553                             case 4:
554                               if (these_rows[this_frame_num][i * 4 +0] ==
555                                   red[j][i] &&
556                                   these_rows[this_frame_num][i * 4 +1] ==
557                                   green[j][i] &&
558                                   these_rows[this_frame_num][i * 4 +2] ==
559                                   blue[j][i])
560                                 {
561                                   (count[j][i])++;
562                                   goto same;
563                                 }
564                               break;
565                             case 2:
566                               if (these_rows[this_frame_num][i * 2 +0] ==
567                                   red[j][i])
568                                 {
569                                   (count[j][i])++;
570                                   goto same;
571                                 }
572                               break;
573                             default:
574                               g_error ("Eeep!");
575                               break;
576                             }
577                         }
578 
579                       count[num_colors[i]][i] = 1;
580                       red[num_colors[i]][i] =
581                         these_rows[this_frame_num][i * pixelstep];
582                       if (pixelstep == 4)
583                         {
584                           green[num_colors[i]][i] =
585                             these_rows[this_frame_num][i * 4 +1];
586                           blue[num_colors[i]][i] =
587                             these_rows[this_frame_num][i * 4 +2];
588                         }
589                       num_colors[i]++;
590                     }
591                 same:
592                   /* nop */;
593                 }
594             }
595 
596           for (i=0; i<width; i++)
597             {
598               guint  best_count = 0;
599               guchar best_r = 255, best_g = 0, best_b = 255;
600 
601               for (j=0; j<num_colors[i]; j++)
602                 {
603                   if (count[j][i] > best_count)
604                     {
605                       best_count = count[j][i];
606                       best_r = red[j][i];
607                       best_g = green[j][i];
608                       best_b = blue[j][i];
609                     }
610                 }
611 
612               back_frame[width * pixelstep * row +i*pixelstep + 0] = best_r;
613               if (pixelstep == 4)
614                 {
615                   back_frame[width * pixelstep * row +i*pixelstep + 1] =
616                     best_g;
617                   back_frame[width * pixelstep * row +i*pixelstep + 2] =
618                     best_b;
619                 }
620               back_frame[width * pixelstep * row +i*pixelstep +pixelstep-1] =
621                 (best_count == 0) ? 0 : 255;
622 
623               if (best_count == 0)
624                 g_warning("yayyyy!");
625             }
626           /*      memcpy(&back_frame[width * pixelstep * row],
627                   these_rows[0],
628                   width * pixelstep);*/
629         }
630 
631       for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
632         {
633           g_free (these_rows[this_frame_num]);
634           g_free (red[this_frame_num]);
635           g_free (green[this_frame_num]);
636           g_free (blue[this_frame_num]);
637           g_free (count[this_frame_num]);
638         }
639 
640       g_free (these_rows);
641       g_free (red);
642       g_free (green);
643       g_free (blue);
644       g_free (count);
645       g_free (num_colors);
646     }
647 #endif
648 
649   if (opmode == OPBACKGROUND)
650     {
651       GeglBuffer *buffer;
652       const Babl *format;
653 
654       new_layer_id = gimp_layer_new (new_image_id,
655                                      "Backgroundx",
656                                      width, height,
657                                      drawabletype_alpha,
658                                      100.0,
659                                      gimp_image_get_default_new_layer_mode (new_image_id));
660 
661       gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
662 
663       buffer = gimp_drawable_get_buffer (new_layer_id);
664 
665       format = get_format (new_layer_id);
666 
667       gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
668                        format, back_frame,
669                        GEGL_ABYSS_NONE);
670 
671       g_object_unref (buffer);
672     }
673   else
674     {
675       for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
676         {
677           /*
678            * BUILD THIS FRAME into our 'this_frame' buffer.
679            */
680 
681           gint32 drawable_ID = layers[total_frames-(this_frame_num+1)];
682 
683           /* Image has been closed/etc since we got the layer list? */
684           /* FIXME - How do we tell if a gimp_drawable_get() fails? */
685           if (gimp_drawable_width (drawable_ID) == 0)
686             {
687               gimp_quit ();
688             }
689 
690           this_delay = get_frame_duration (this_frame_num);
691           dispose    = get_frame_disposal (this_frame_num);
692 
693           for (row = 0; row < height; row++)
694             {
695               compose_row (this_frame_num,
696                            dispose,
697                            row,
698                            &this_frame[pixelstep*width * row],
699                            width,
700                            drawable_ID,
701                            FALSE
702                            );
703             }
704 
705           if (opmode == OPFOREGROUND)
706             {
707               gint xit, yit, byteit;
708 
709               for (yit=0; yit<height; yit++)
710                 {
711                   for (xit=0; xit<width; xit++)
712                     {
713                       for (byteit=0; byteit<pixelstep-1; byteit++)
714                         {
715                           if (back_frame[yit*width*pixelstep + xit*pixelstep
716                                         + byteit]
717                               !=
718                               this_frame[yit*width*pixelstep + xit*pixelstep
719                                         + byteit])
720                             {
721                               goto enough;
722                             }
723                         }
724                       this_frame[yit*width*pixelstep + xit*pixelstep
725                                 + pixelstep - 1] = 0;
726                     enough:
727                       /* nop */;
728                     }
729                 }
730             }
731 
732           can_combine = FALSE;
733           bbox_left   = 0;
734           bbox_top    = 0;
735           bbox_right  = width;
736           bbox_bottom = height;
737           rbox_left   = 0;
738           rbox_top    = 0;
739           rbox_right  = width;
740           rbox_bottom = height;
741 
742           /* copy 'this' frame into a buffer which we can safely molest */
743           memcpy (opti_frame, this_frame, frame_sizebytes);
744           /*
745            *
746            * OPTIMIZE HERE!
747            *
748            */
749           if (
750               (this_frame_num != 0) /* Can't delta bottom frame! */
751               && (opmode == OPOPTIMIZE)
752               )
753             {
754               gint xit, yit, byteit;
755 
756               can_combine = TRUE;
757 
758               /*
759                * SEARCH FOR BOUNDING BOX
760                */
761               bbox_left   = width;
762               bbox_top    = height;
763               bbox_right  = 0;
764               bbox_bottom = 0;
765               rbox_left   = width;
766               rbox_top    = height;
767               rbox_right  = 0;
768               rbox_bottom = 0;
769 
770               for (yit=0; yit<height; yit++)
771                 {
772                   for (xit=0; xit<width; xit++)
773                     {
774                       gboolean keep_pix;
775                       gboolean opaq_pix;
776 
777                       /* Check if 'this' and 'last' are transparent */
778                       if (!(this_frame[yit*width*pixelstep + xit*pixelstep
779                                       + pixelstep-1]&128)
780                           &&
781                           !(last_frame[yit*width*pixelstep + xit*pixelstep
782                                       + pixelstep-1]&128))
783                         {
784                           keep_pix = FALSE;
785                           opaq_pix = FALSE;
786                           goto decided;
787                         }
788                       /* Check if just 'this' is transparent */
789                       if ((last_frame[yit*width*pixelstep + xit*pixelstep
790                                      + pixelstep-1]&128)
791                           &&
792                           !(this_frame[yit*width*pixelstep + xit*pixelstep
793                                       + pixelstep-1]&128))
794                         {
795                           keep_pix = TRUE;
796                           opaq_pix = FALSE;
797                           can_combine = FALSE;
798                           goto decided;
799                         }
800                       /* Check if just 'last' is transparent */
801                       if (!(last_frame[yit*width*pixelstep + xit*pixelstep
802                                       + pixelstep-1]&128)
803                           &&
804                           (this_frame[yit*width*pixelstep + xit*pixelstep
805                                      + pixelstep-1]&128))
806                         {
807                           keep_pix = TRUE;
808                           opaq_pix = TRUE;
809                           goto decided;
810                         }
811                       /* If 'last' and 'this' are opaque, we have
812                        *  to check if they're the same color - we
813                        *  only have to keep the pixel if 'last' or
814                        *  'this' are opaque and different.
815                        */
816                       keep_pix = FALSE;
817                       opaq_pix = TRUE;
818                       for (byteit=0; byteit<pixelstep-1; byteit++)
819                         {
820                           if ((last_frame[yit*width*pixelstep + xit*pixelstep
821                                          + byteit]
822                                !=
823                                this_frame[yit*width*pixelstep + xit*pixelstep
824                                          + byteit])
825                               )
826                             {
827                               keep_pix = TRUE;
828                               goto decided;
829                             }
830                         }
831                     decided:
832                       if (opaq_pix)
833                         {
834                           if (xit<rbox_left) rbox_left=xit;
835                           if (xit>rbox_right) rbox_right=xit;
836                           if (yit<rbox_top) rbox_top=yit;
837                           if (yit>rbox_bottom) rbox_bottom=yit;
838                         }
839                       if (keep_pix)
840                         {
841                           if (xit<bbox_left) bbox_left=xit;
842                           if (xit>bbox_right) bbox_right=xit;
843                           if (yit<bbox_top) bbox_top=yit;
844                           if (yit>bbox_bottom) bbox_bottom=yit;
845                         }
846                       else
847                         {
848                           /* pixel didn't change this frame - make
849                            *  it transparent in our optimized buffer!
850                            */
851                           opti_frame[yit*width*pixelstep + xit*pixelstep
852                                     + pixelstep-1] = 0;
853                         }
854                     } /* xit */
855                 } /* yit */
856 
857               if (!can_combine)
858                 {
859                   bbox_left = rbox_left;
860                   bbox_top = rbox_top;
861                   bbox_right = rbox_right;
862                   bbox_bottom = rbox_bottom;
863                 }
864 
865               bbox_right++;
866               bbox_bottom++;
867 
868               if (can_combine && !diff_only)
869                 {
870                   /* Try to optimize the pixel data for RLE or LZW compression
871                    * by making some transparent pixels non-transparent if they
872                    * would have the same color as the adjacent pixels.  This
873                    * gives a better compression if the algorithm compresses
874                    * the image line by line.
875                    * See: http://bugzilla.gnome.org/show_bug.cgi?id=66367
876                    * It may not be very efficient to add two additional passes
877                    * over the pixels, but this hopefully makes the code easier
878                    * to maintain and less error-prone.
879                    */
880                   for (yit = bbox_top; yit < bbox_bottom; yit++)
881                     {
882                       /* Compare with previous pixels from left to right */
883                       for (xit = bbox_left + 1; xit < bbox_right; xit++)
884                         {
885                           if (!(opti_frame[yit*width*pixelstep
886                                            + xit*pixelstep
887                                            + pixelstep-1]&128)
888                               && (opti_frame[yit*width*pixelstep
889                                              + (xit-1)*pixelstep
890                                              + pixelstep-1]&128)
891                               && (last_frame[yit*width*pixelstep
892                                              + xit*pixelstep
893                                              + pixelstep-1]&128))
894                             {
895                               for (byteit=0; byteit<pixelstep-1; byteit++)
896                                 {
897                                   if (opti_frame[yit*width*pixelstep
898                                                  + (xit-1)*pixelstep
899                                                  + byteit]
900                                       !=
901                                       last_frame[yit*width*pixelstep
902                                                  + xit*pixelstep
903                                                  + byteit])
904                                     {
905                                       goto skip_right;
906                                     }
907                                 }
908                               /* copy the color and alpha */
909                               for (byteit=0; byteit<pixelstep; byteit++)
910                                 {
911                                   opti_frame[yit*width*pixelstep
912                                              + xit*pixelstep
913                                              + byteit]
914                                     = last_frame[yit*width*pixelstep
915                                                  + xit*pixelstep
916                                                  + byteit];
917                                 }
918                             }
919                         skip_right:
920                           /* nop */;
921                         } /* xit */
922 
923                       /* Compare with next pixels from right to left */
924                       for (xit = bbox_right - 2; xit >= bbox_left; xit--)
925                         {
926                           if (!(opti_frame[yit*width*pixelstep
927                                            + xit*pixelstep
928                                            + pixelstep-1]&128)
929                               && (opti_frame[yit*width*pixelstep
930                                              + (xit+1)*pixelstep
931                                              + pixelstep-1]&128)
932                               && (last_frame[yit*width*pixelstep
933                                              + xit*pixelstep
934                                              + pixelstep-1]&128))
935                             {
936                               for (byteit=0; byteit<pixelstep-1; byteit++)
937                                 {
938                                   if (opti_frame[yit*width*pixelstep
939                                                  + (xit+1)*pixelstep
940                                                  + byteit]
941                                       !=
942                                       last_frame[yit*width*pixelstep
943                                                  + xit*pixelstep
944                                                  + byteit])
945                                     {
946                                       goto skip_left;
947                                     }
948                                 }
949                               /* copy the color and alpha */
950                               for (byteit=0; byteit<pixelstep; byteit++)
951                                 {
952                                   opti_frame[yit*width*pixelstep
953                                              + xit*pixelstep
954                                              + byteit]
955                                     = last_frame[yit*width*pixelstep
956                                                  + xit*pixelstep
957                                                  + byteit];
958                                 }
959                             }
960                         skip_left:
961                           /* nop */;
962                         } /* xit */
963                     } /* yit */
964                 }
965 
966               /*
967                * Collapse opti_frame data down such that the data
968                *  which occupies the bounding box sits at the start
969                *  of the data (for convenience with ..set_rect()).
970                */
971               destptr = opti_frame;
972               /*
973                * If can_combine, then it's safe to use our optimized
974                *  alpha information.  Otherwise, an opaque pixel became
975                *  transparent this frame, and we'll have to use the
976                *  actual true frame's alpha.
977                */
978               if (can_combine)
979                 srcptr = opti_frame;
980               else
981                 srcptr = this_frame;
982               for (yit=bbox_top; yit<bbox_bottom; yit++)
983                 {
984                   for (xit=bbox_left; xit<bbox_right; xit++)
985                     {
986                       for (byteit=0; byteit<pixelstep; byteit++)
987                         {
988                           *(destptr++) = srcptr[yit*pixelstep*width +
989                                                pixelstep*xit + byteit];
990                         }
991                     }
992                 }
993             } /* !bot frame? */
994           else
995             {
996               memcpy (opti_frame, this_frame, frame_sizebytes);
997             }
998 
999           /*
1000            *
1001            * REMEMBER THE ANIMATION STATUS TO DELTA AGAINST NEXT TIME
1002            *
1003            */
1004           memcpy (last_frame, this_frame, frame_sizebytes);
1005 
1006 
1007           /*
1008            *
1009            * PUT THIS FRAME INTO A NEW LAYER IN THE NEW IMAGE
1010            *
1011            */
1012 
1013           oldlayer_name =
1014             gimp_item_get_name(layers[total_frames-(this_frame_num+1)]);
1015 
1016           buflen = strlen(oldlayer_name) + 40;
1017 
1018           newlayer_name = g_malloc(buflen);
1019 
1020           remove_disposal_tag(newlayer_name, oldlayer_name);
1021           g_free(oldlayer_name);
1022 
1023           oldlayer_name = g_malloc(buflen);
1024 
1025           remove_ms_tag(oldlayer_name, newlayer_name);
1026 
1027           g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
1028                      oldlayer_name, this_delay,
1029                      (this_frame_num ==  0) ? "" :
1030                      can_combine ? "(combine)" : "(replace)");
1031 
1032           g_free(oldlayer_name);
1033 
1034           /* Empty frame! */
1035           if (bbox_right <= bbox_left ||
1036               bbox_bottom <= bbox_top)
1037             {
1038               cumulated_delay += this_delay;
1039 
1040               g_free (newlayer_name);
1041 
1042               oldlayer_name = gimp_item_get_name (last_true_frame);
1043 
1044               buflen = strlen (oldlayer_name) + 40;
1045 
1046               newlayer_name = g_malloc (buflen);
1047 
1048               remove_disposal_tag (newlayer_name, oldlayer_name);
1049               g_free (oldlayer_name);
1050 
1051               oldlayer_name = g_malloc (buflen);
1052 
1053               remove_ms_tag (oldlayer_name, newlayer_name);
1054 
1055               g_snprintf (newlayer_name, buflen, "%s(%dms)%s",
1056                           oldlayer_name, cumulated_delay,
1057                           (this_frame_num ==  0) ? "" :
1058                           can_combine ? "(combine)" : "(replace)");
1059 
1060               gimp_item_set_name (last_true_frame, newlayer_name);
1061 
1062               g_free (newlayer_name);
1063             }
1064           else
1065             {
1066               GeglBuffer *buffer;
1067               const Babl *format;
1068 
1069               cumulated_delay = this_delay;
1070 
1071               last_true_frame =
1072                 new_layer_id = gimp_layer_new (new_image_id,
1073                                                newlayer_name,
1074                                                bbox_right-bbox_left,
1075                                                bbox_bottom-bbox_top,
1076                                                drawabletype_alpha,
1077                                                100.0,
1078                                                gimp_image_get_default_new_layer_mode (new_image_id));
1079               g_free (newlayer_name);
1080 
1081               gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
1082 
1083               buffer = gimp_drawable_get_buffer (new_layer_id);
1084 
1085               format = get_format (new_layer_id);
1086 
1087               gegl_buffer_set (buffer,
1088                                GEGL_RECTANGLE (0, 0,
1089                                                bbox_right-bbox_left,
1090                                                bbox_bottom-bbox_top), 0,
1091                                format, opti_frame,
1092                                GEGL_AUTO_ROWSTRIDE);
1093 
1094               g_object_unref (buffer);
1095               gimp_item_transform_translate (new_layer_id, bbox_left, bbox_top);
1096             }
1097 
1098           gimp_progress_update (((gdouble) this_frame_num + 1.0) /
1099                                 ((gdouble) total_frames));
1100         }
1101 
1102       gimp_progress_update (1.0);
1103     }
1104 
1105   gimp_image_undo_enable (new_image_id);
1106 
1107   if (run_mode != GIMP_RUN_NONINTERACTIVE)
1108     gimp_display_new (new_image_id);
1109 
1110   g_free (rawframe);
1111   rawframe = NULL;
1112 
1113   g_free (last_frame);
1114   last_frame = NULL;
1115 
1116   g_free (this_frame);
1117   this_frame = NULL;
1118 
1119   g_free (opti_frame);
1120   opti_frame = NULL;
1121 
1122   g_free (back_frame);
1123   back_frame = NULL;
1124 
1125   return new_image_id;
1126 }
1127 
1128 /* Util. */
1129 
1130 static DisposeType
get_frame_disposal(guint whichframe)1131 get_frame_disposal (guint whichframe)
1132 {
1133   gchar       *layer_name;
1134   DisposeType  disposal;
1135 
1136   layer_name = gimp_item_get_name(layers[total_frames-(whichframe+1)]);
1137   disposal = parse_disposal_tag(layer_name);
1138   g_free(layer_name);
1139 
1140   return disposal;
1141 }
1142 
1143 static guint32
get_frame_duration(guint whichframe)1144 get_frame_duration (guint whichframe)
1145 {
1146   gchar* layer_name;
1147   gint   duration = 0;
1148 
1149   layer_name = gimp_item_get_name(layers[total_frames-(whichframe+1)]);
1150   if (layer_name)
1151     {
1152       duration = parse_ms_tag(layer_name);
1153       g_free(layer_name);
1154     }
1155 
1156   if (duration < 0) duration = 100;  /* FIXME for default-if-not-said  */
1157   if (duration == 0) duration = 100; /* FIXME - 0-wait is nasty */
1158 
1159   return (guint32) duration;
1160 }
1161 
1162 static gboolean
is_ms_tag(const gchar * str,gint * duration,gint * taglength)1163 is_ms_tag (const gchar *str,
1164            gint        *duration,
1165            gint        *taglength)
1166 {
1167   gint sum = 0;
1168   gint offset;
1169   gint length;
1170 
1171   length = strlen(str);
1172 
1173   if (str[0] != '(')
1174     return FALSE;
1175 
1176   offset = 1;
1177 
1178   /* eat any spaces between open-parenthesis and number */
1179   while ((offset<length) && (str[offset] == ' '))
1180     offset++;
1181 
1182   if ((offset>=length) || (!g_ascii_isdigit (str[offset])))
1183     return 0;
1184 
1185   do
1186     {
1187       sum *= 10;
1188       sum += str[offset] - '0';
1189       offset++;
1190     }
1191   while ((offset<length) && (g_ascii_isdigit (str[offset])));
1192 
1193   if (length-offset <= 2)
1194     return FALSE;
1195 
1196   /* eat any spaces between number and 'ms' */
1197   while ((offset<length) && (str[offset] == ' '))
1198     offset++;
1199 
1200   if ((length-offset <= 2) ||
1201       (g_ascii_toupper (str[offset]) != 'M') ||
1202       (g_ascii_toupper (str[offset+1]) != 'S'))
1203     return FALSE;
1204 
1205   offset += 2;
1206 
1207   /* eat any spaces between 'ms' and close-parenthesis */
1208   while ((offset<length) && (str[offset] == ' '))
1209     offset++;
1210 
1211   if ((length-offset < 1) || (str[offset] != ')'))
1212     return FALSE;
1213 
1214   offset++;
1215 
1216   *duration  = sum;
1217   *taglength = offset;
1218 
1219   return TRUE;
1220 }
1221 
1222 static int
parse_ms_tag(const char * str)1223 parse_ms_tag (const char *str)
1224 {
1225   gint i;
1226   gint rtn;
1227   gint dummy;
1228   gint length;
1229 
1230   length = strlen (str);
1231 
1232   for (i = 0; i < length; i++)
1233     {
1234       if (is_ms_tag (&str[i], &rtn, &dummy))
1235         return rtn;
1236     }
1237 
1238   return -1;
1239 }
1240 
1241 static gboolean
is_disposal_tag(const gchar * str,DisposeType * disposal,gint * taglength)1242 is_disposal_tag (const gchar *str,
1243                  DisposeType *disposal,
1244                  gint        *taglength)
1245 {
1246   if (strlen (str) != 9)
1247     return FALSE;
1248 
1249   if (strncmp (str, "(combine)", 9) == 0)
1250     {
1251       *taglength = 9;
1252       *disposal = DISPOSE_COMBINE;
1253       return TRUE;
1254     }
1255   else if (strncmp (str, "(replace)", 9) == 0)
1256     {
1257       *taglength = 9;
1258       *disposal = DISPOSE_REPLACE;
1259       return TRUE;
1260     }
1261 
1262   return FALSE;
1263 }
1264 
1265 
1266 static DisposeType
parse_disposal_tag(const gchar * str)1267 parse_disposal_tag (const gchar *str)
1268 {
1269   DisposeType rtn;
1270   gint        i, dummy;
1271   gint        length;
1272 
1273   length = strlen(str);
1274 
1275   for (i=0; i<length; i++)
1276     {
1277       if (is_disposal_tag (&str[i], &rtn, &dummy))
1278         {
1279           return rtn;
1280         }
1281     }
1282 
1283   return DISPOSE_UNDEFINED; /* FIXME */
1284 }
1285 
1286 static void
remove_disposal_tag(gchar * dest,gchar * src)1287 remove_disposal_tag (gchar *dest,
1288                      gchar *src)
1289 {
1290   gint        offset = 0;
1291   gint        destoffset = 0;
1292   gint        length;
1293   int         taglength;
1294   DisposeType dummy;
1295 
1296   length = strlen(src);
1297 
1298   strcpy(dest, src);
1299 
1300   while (offset<=length)
1301     {
1302       if (is_disposal_tag(&src[offset], &dummy, &taglength))
1303         {
1304           offset += taglength;
1305         }
1306       dest[destoffset] = src[offset];
1307       destoffset++;
1308       offset++;
1309     }
1310 
1311   dest[offset] = '\0';
1312 }
1313 
1314 static void
remove_ms_tag(gchar * dest,gchar * src)1315 remove_ms_tag (gchar *dest,
1316                gchar *src)
1317 {
1318   gint offset = 0;
1319   gint destoffset = 0;
1320   gint length;
1321   gint taglength;
1322   gint dummy;
1323 
1324   length = strlen(src);
1325 
1326   strcpy(dest, src);
1327 
1328   while (offset<=length)
1329     {
1330       if (is_ms_tag(&src[offset], &dummy, &taglength))
1331         {
1332           offset += taglength;
1333         }
1334       dest[destoffset] = src[offset];
1335       destoffset++;
1336       offset++;
1337     }
1338 
1339   dest[offset] = '\0';
1340 }
1341