home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / animoptimize.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-31  |  25.7 KB  |  1,099 lines

  1. /*
  2.  * Animation Optimizer plug-in version 1.0.4
  3.  *
  4.  * (c) Adam D. Moss, 1997-2000
  5.  *     adam@gimp.org
  6.  *     adam@foxbox.org
  7.  *
  8.  * This is part of the GIMP package and falls under the GPL.
  9.  */
  10.  
  11. /*
  12.  * REVISION HISTORY:
  13.  *
  14.  * 2000-08-30 : version 1.0.4
  15.  *              Change default frame duration from 125ms to 100ms for
  16.  *              consistancy.
  17.  *
  18.  * 2000-06-05 : version 1.0.3
  19.  *              Fix old bug which could cause errors in evaluating the
  20.  *              final pixel of each composed layer.
  21.  *
  22.  * 2000-01-13 : version 1.0.2
  23.  *              Collapse timing of completely optimized-away frames
  24.  *              onto previous surviving frame.  Also be looser with
  25.  *              (XXXXX) tag parsing.
  26.  *
  27.  * 2000-01-07 : version 1.0.1
  28.  *              PDB interface submitted by Andreas Jaekel
  29.  *              <jaekel@cablecats.de>
  30.  *
  31.  * 98.05.17 : version 1.0.0
  32.  *            Finally preserves frame timings / layer names.  Has
  33.  *            a progress bar now.  No longer beta, I suppose.
  34.  *
  35.  * 98.04.19 : version 0.70.0
  36.  *            Plug-in doubles up as Animation UnOptimize too!  (This
  37.  *            is somewhat more useful than it sounds.)
  38.  *
  39.  * 98.03.16 : version 0.61.0
  40.  *            Support more rare opaque/transparent combinations.
  41.  *
  42.  * 97.12.09 : version 0.60.0
  43.  *            Added support for INDEXED* and GRAY* images.
  44.  *
  45.  * 97.12.09 : version 0.52.0
  46.  *            Fixed some bugs.
  47.  *
  48.  * 97.12.08 : version 0.51.0
  49.  *            Relaxed bounding box on optimized layers marked
  50.  *            'replace'.
  51.  *
  52.  * 97.12.07 : version 0.50.0
  53.  *            Initial release.
  54.  */
  55.  
  56. /*
  57.  * BUGS:
  58.  *  ?
  59.  */
  60.  
  61. /*
  62.  * TODO:
  63.  *   User interface
  64.  */
  65.  
  66. #include "config.h"
  67.  
  68. #include <stdlib.h>
  69. #include <stdio.h>
  70. #include <string.h>
  71. #include <ctype.h>
  72.  
  73. #include <libgimp/gimp.h>
  74.  
  75. #include "libgimp/stdplugins-intl.h"
  76.  
  77.  
  78. typedef enum
  79. {
  80.   DISPOSE_UNDEFINED = 0x00,
  81.   DISPOSE_COMBINE   = 0x01,
  82.   DISPOSE_REPLACE   = 0x02
  83. } DisposeType;
  84.  
  85.  
  86.  
  87. /* Declare local functions. */
  88. static void query (void);
  89. static void run   (gchar   *name,
  90.            gint     nparams,
  91.            GimpParam  *param,
  92.            gint    *nreturn_vals,
  93.            GimpParam **return_vals);
  94.  
  95. static      gint32 do_optimizations   (GimpRunModeType run_mode);
  96.  
  97.  
  98. /* tag util functions*/
  99. static         int parse_ms_tag        (const char *str);
  100. static DisposeType parse_disposal_tag  (const char *str);
  101. static DisposeType get_frame_disposal  (const guint whichframe);
  102. static     guint32 get_frame_duration  (const guint whichframe);
  103. static        void remove_disposal_tag (char* dest, char *src);
  104. static        void remove_ms_tag       (char* dest, char *src);
  105. static int is_disposal_tag (const char *str,
  106.                 DisposeType *disposal,
  107.                 int *taglength);
  108. static int is_ms_tag (const char *str,
  109.               int *duration,
  110.               int *taglength);
  111.  
  112.  
  113. GimpPlugInInfo PLUG_IN_INFO =
  114. {
  115.   NULL,  /* init_proc  */
  116.   NULL,  /* quit_proc  */
  117.   query, /* query_proc */
  118.   run,   /* run_proc   */
  119. };
  120.  
  121.  
  122. /* Global widgets'n'stuff */
  123. static guint          width,height;
  124. static gint32         image_id;
  125. static gint32         new_image_id;
  126. static gint32         total_frames;
  127. static gint32        *layers;
  128. static GimpDrawable     *drawable;
  129. static GimpImageBaseType     imagetype;
  130. static GimpImageType  drawabletype_alpha;
  131. static guchar         pixelstep;
  132. static guchar        *palette;
  133. static gint           ncolours;
  134. static gboolean       optimize;
  135.  
  136.  
  137. MAIN ()
  138.  
  139. static void
  140. query (void)
  141. {
  142.   static GimpParamDef args[] =
  143.   {
  144.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  145.     { GIMP_PDB_IMAGE, "image", "Input image" },
  146.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
  147.   };
  148.   static GimpParamDef return_args[] =
  149.   {
  150.     { GIMP_PDB_IMAGE, "result", "Resulting image" }
  151.   };
  152.   static gint nargs = sizeof (args) / sizeof (args[0]);
  153.   static gint nreturn_args = sizeof (return_args) / sizeof (return_args[0]);
  154.  
  155.   gimp_install_procedure ("plug_in_animationoptimize",
  156.               "This plugin applies various optimizations to"
  157.               " a GIMP layer-based animation.",
  158.               "",
  159.               "Adam D. Moss <adam@gimp.org>",
  160.               "Adam D. Moss <adam@gimp.org>",
  161.               "1997-98",
  162.               N_("<Image>/Filters/Animation/Animation Optimize"),
  163.               "RGB*, INDEXED*, GRAY*",
  164.               GIMP_PLUGIN,
  165.               nargs, nreturn_args,
  166.               args, return_args);
  167.  
  168.   gimp_install_procedure ("plug_in_animationunoptimize",
  169.               "This plugin 'simplifies' a GIMP layer-based"
  170.               " animation that has been AnimationOptimized.  This"
  171.               " makes the animation much easier to work with if,"
  172.               " for example, the optimized version is all you"
  173.               " have.",
  174.               "",
  175.               "Adam D. Moss <adam@gimp.org>",
  176.               "Adam D. Moss <adam@gimp.org>",
  177.               "1997-98",
  178.               N_("<Image>/Filters/Animation/Animation UnOptimize"),
  179.               "RGB*, INDEXED*, GRAY*",
  180.               GIMP_PLUGIN,
  181.               nargs, nreturn_args,
  182.               args, return_args);
  183. }
  184.  
  185. static void
  186. run (gchar   *name,
  187.      gint     n_params,
  188.      GimpParam  *param,
  189.      gint    *nreturn_vals,
  190.      GimpParam **return_vals)
  191. {
  192.   static GimpParam values[2];
  193.   GimpRunModeType run_mode;
  194.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  195.  
  196.   *nreturn_vals = 2;
  197.   *return_vals = values;
  198.  
  199.   run_mode = param[0].data.d_int32;
  200.   
  201.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  202.     {
  203.       if (n_params != 3)
  204.     {
  205.       status = GIMP_PDB_CALLING_ERROR;
  206.     }
  207.     }
  208.     INIT_I18N();
  209.  
  210.   /* Check the procedure name we were called with, to decide
  211.      what needs to be done. */
  212.   if (strcmp(name,"plug_in_animationoptimize")==0)
  213.     optimize = TRUE;
  214.   else
  215.     optimize = FALSE; /* UnOptimize */
  216.  
  217.   if (status == GIMP_PDB_SUCCESS)
  218.     {
  219.       image_id = param[1].data.d_image;
  220.       
  221.       new_image_id = do_optimizations(run_mode);
  222.       
  223.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  224.     gimp_displays_flush();
  225.     }
  226.   
  227.   values[0].type = GIMP_PDB_STATUS;
  228.   values[0].data.d_status = status;
  229.  
  230.   values[1].type = GIMP_PDB_IMAGE;
  231.   values[1].data.d_image = new_image_id;
  232. }
  233.  
  234.  
  235.  
  236. /* Rendering Functions */
  237.  
  238. static void
  239. total_alpha(guchar* imdata, guint32 numpix, guchar bytespp)
  240. {
  241.   /* Set image to total-transparency w/black
  242.    */
  243.  
  244.   memset(imdata, 0, numpix*bytespp);
  245. }
  246.  
  247. static gint32
  248. do_optimizations(GimpRunModeType run_mode)
  249. {
  250.   GimpPixelRgn pixel_rgn;
  251.   static guchar* rawframe = NULL;
  252.   static gint rawwidth=0, rawheight=0, rawbpp=0;
  253.   gint rawx=0, rawy=0;
  254.   guchar* srcptr;
  255.   guchar* destptr;
  256.   gint i,j,this_frame_num;
  257.   guint32 frame_sizebytes;
  258.   gint32 new_layer_id;
  259.   DisposeType dispose;
  260.   guchar* this_frame = NULL;
  261.   guchar* last_frame = NULL;
  262.   guchar* opti_frame = NULL;
  263.  
  264.   int this_delay;
  265.   int cumulated_delay = 0;
  266.   int last_true_frame = -1;
  267.   int buflen;
  268.  
  269.   gchar* oldlayer_name;
  270.   gchar* newlayer_name;
  271.   
  272.   gboolean can_combine;
  273.  
  274.   gint32 bbox_top, bbox_bottom, bbox_left, bbox_right;
  275.   gint32 rbox_top, rbox_bottom, rbox_left, rbox_right;
  276.  
  277.   if (optimize)
  278.     gimp_progress_init (_("Optimizing Animation..."));
  279.   else
  280.     gimp_progress_init (_("UnOptimizing Animation..."));
  281.  
  282.   width     = gimp_image_width(image_id);
  283.   height    = gimp_image_height(image_id);
  284.   layers    = gimp_image_get_layers (image_id, &total_frames);
  285.   imagetype = gimp_image_base_type(image_id);
  286.   pixelstep = (imagetype == GIMP_RGB) ? 4 : 2;
  287.  
  288.   drawabletype_alpha = (imagetype == GIMP_RGB) ? GIMP_RGBA_IMAGE :
  289.     ((imagetype == GIMP_INDEXED) ? GIMP_INDEXEDA_IMAGE : GIMP_GRAYA_IMAGE);
  290.  
  291.   frame_sizebytes = width * height * pixelstep;
  292.  
  293.   this_frame = g_malloc (frame_sizebytes);
  294.   last_frame = g_malloc (frame_sizebytes);
  295.   opti_frame = g_malloc (frame_sizebytes);
  296.  
  297.   total_alpha (this_frame, width*height, pixelstep);
  298.   total_alpha (last_frame, width*height, pixelstep);
  299.  
  300.   new_image_id = gimp_image_new(width, height, imagetype);
  301.  
  302.   if (imagetype == GIMP_INDEXED)
  303.     {
  304.       palette = gimp_image_get_cmap (image_id, &ncolours);
  305.       gimp_image_set_cmap (new_image_id, palette, ncolours);
  306.     }
  307.  
  308.   if ((this_frame == NULL) || (last_frame == NULL) || (opti_frame == NULL))
  309.     g_error(_("Not enough memory to allocate buffers for optimization.\n"));
  310.  
  311.   for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
  312.     {
  313.       /*
  314.        * We actually unoptimize the animation before re-optimizing it,
  315.        *  otherwise the logic can get a bit too hard... for me.
  316.        */
  317.  
  318.       /*
  319.        *
  320.        * BUILD THIS FRAME
  321.        *
  322.        */
  323.  
  324.       drawable = gimp_drawable_get (layers[total_frames-(this_frame_num+1)]);
  325.  
  326.       /* Image has been closed/etc since we got the layer list? */
  327.       /* FIXME - How do we tell if a gimp_drawable_get() fails? */
  328.       if (gimp_drawable_width (drawable->id) == 0)
  329.     {
  330.       gimp_quit ();
  331.     }
  332.  
  333.       this_delay = get_frame_duration (this_frame_num);
  334.       dispose    = get_frame_disposal (this_frame_num);
  335.  
  336.       if (dispose == DISPOSE_REPLACE)
  337.     {
  338.       total_alpha (this_frame, width * height, pixelstep);
  339.     }
  340.  
  341.       /* only get a new 'raw' drawable-data buffer if this and
  342.      the previous raw buffer were different sizes*/
  343.  
  344.       if ((rawwidth*rawheight*rawbpp)
  345.       !=
  346.       ((gimp_drawable_width (drawable->id)*
  347.         gimp_drawable_height (drawable->id)*
  348.         gimp_drawable_bpp (drawable->id))))
  349.     {
  350.       if (rawframe != NULL) g_free (rawframe);
  351.       rawframe = g_malloc ((gimp_drawable_width (drawable->id)) *
  352.                    (gimp_drawable_height (drawable->id)) *
  353.                    (gimp_drawable_bpp (drawable->id)));
  354.     }
  355.  
  356.       rawwidth = gimp_drawable_width (drawable->id);
  357.       rawheight = gimp_drawable_height (drawable->id);
  358.       rawbpp = gimp_drawable_bpp (drawable->id);
  359.  
  360.       /* Initialise and fetch the whole raw new frame */
  361.  
  362.       gimp_pixel_rgn_init (&pixel_rgn,
  363.                drawable,
  364.                0, 0,
  365.                drawable->width, drawable->height,
  366.                FALSE,
  367.                FALSE);
  368.       gimp_pixel_rgn_get_rect (&pixel_rgn,
  369.                    rawframe,
  370.                    0, 0,
  371.                    drawable->width, drawable->height);
  372.       /*  gimp_pixel_rgns_register (1, &pixel_rgn);*/
  373.  
  374.       gimp_drawable_offsets (drawable->id,
  375.                  &rawx,
  376.                  &rawy);
  377.  
  378.  
  379.       /* render... */
  380.  
  381.       switch (imagetype)
  382.     {
  383.     case GIMP_RGB:
  384.       if ((rawwidth==width) &&
  385.           (rawheight==height) &&
  386.           (rawx==0) &&
  387.           (rawy==0))
  388.         {
  389.           /* --- These cases are for the best cases,  in        --- */
  390.           /* --- which this frame is the same size and position --- */
  391.           /* --- as the preview buffer itself                   --- */
  392.       
  393.           if (gimp_drawable_has_alpha (drawable->id))
  394.         { /* alpha RGB, same size */
  395.           destptr = this_frame;
  396.           srcptr  = rawframe;
  397.           
  398.           i = rawwidth*rawheight;
  399.           while (i--)
  400.             {
  401.               if (!((*(srcptr+3))&128))
  402.             {
  403.               srcptr  += 4;
  404.               destptr += 4;
  405.               continue;
  406.             }
  407.               *(destptr++) = *(srcptr++);
  408.               *(destptr++) = *(srcptr++);
  409.               *(destptr++) = *(srcptr++);
  410.               *(destptr++) = 255;
  411.               srcptr++;
  412.             }
  413.         }
  414.           else /* RGB no alpha, same size */
  415.         {
  416.           destptr = this_frame;
  417.           srcptr  = rawframe;
  418.           
  419.           i = rawwidth*rawheight;
  420.           while (i--)
  421.             {
  422.               *(destptr++) = *(srcptr++);
  423.               *(destptr++) = *(srcptr++);
  424.               *(destptr++) = *(srcptr++);
  425.               *(destptr++) = 255;
  426.             }          
  427.         }
  428.         }
  429.       else
  430.         {
  431.           /* --- These are suboptimal catch-all cases for when  --- */
  432.           /* --- this frame is bigger/smaller than the preview  --- */
  433.           /* --- buffer, and/or offset within it.               --- */
  434.       
  435.           /* FIXME: FINISH ME! [done?] */
  436.       
  437.           if (gimp_drawable_has_alpha (drawable->id))
  438.         { /* RGB alpha, diff size */
  439.           
  440.           destptr = this_frame;
  441.           srcptr = rawframe;
  442.           
  443.           for (j=rawy; j<rawheight+rawy; j++)
  444.             {
  445.               for (i=rawx; i<rawwidth+rawx; i++)
  446.             {
  447.               if ((i>=0 && i<width) &&
  448.                   (j>=0 && j<height))
  449.                 {
  450.                   if ((*(srcptr+3))&128)
  451.                 {
  452.                   this_frame[(j*width+i)*4   ] = *(srcptr);
  453.                   this_frame[(j*width+i)*4 +1] = *(srcptr+1);
  454.                   this_frame[(j*width+i)*4 +2] = *(srcptr+2);
  455.                   this_frame[(j*width+i)*4 +3] = 255;
  456.                 }
  457.                 }
  458.               
  459.               srcptr += 4;
  460.             }
  461.             }
  462.         }
  463.           else
  464.         {
  465.           /* RGB no alpha, diff size */
  466.           
  467.           destptr = this_frame;
  468.           srcptr = rawframe;
  469.           
  470.           for (j=rawy; j<rawheight+rawy; j++)
  471.             {
  472.               for (i=rawx; i<rawwidth+rawx; i++)
  473.             {
  474.               if ((i>=0 && i<width) &&
  475.                   (j>=0 && j<height))
  476.                 {
  477.                   this_frame[(j*width+i)*4   ] = *(srcptr);
  478.                   this_frame[(j*width+i)*4 +1] = *(srcptr+1);
  479.                   this_frame[(j*width+i)*4 +2] = *(srcptr+2);
  480.                   this_frame[(j*width+i)*4 +3] = 255;
  481.                 }
  482.               
  483.               srcptr += 3;
  484.             }
  485.             }
  486.         }
  487.         }
  488.       break; /* case RGB */
  489.  
  490.     case GIMP_GRAY:
  491.     case GIMP_INDEXED:
  492.       if ((rawwidth==width) &&
  493.           (rawheight==height) &&
  494.           (rawx==0) &&
  495.           (rawy==0))
  496.         {
  497.           /* --- These cases are for the best cases,  in        --- */
  498.           /* --- which this frame is the same size and position --- */
  499.           /* --- as the preview buffer itself                   --- */
  500.       
  501.           if (gimp_drawable_has_alpha (drawable->id))
  502.         { /* I alpha, same size */
  503.           destptr = this_frame;
  504.           srcptr  = rawframe;
  505.           
  506.           i = rawwidth*rawheight;
  507.           while (i--)
  508.             {
  509.               if (!(*(srcptr+1)))
  510.             {
  511.               srcptr  += 2;
  512.               destptr += 2;
  513.               continue;
  514.             }
  515.               *(destptr++) = *(srcptr);
  516.               *(destptr++) = 255;
  517.               srcptr+=2;
  518.             }
  519.         }
  520.           else /* I, no alpha, same size */
  521.         {
  522.           destptr = this_frame;
  523.           srcptr  = rawframe;
  524.           
  525.           i = rawwidth*rawheight;
  526.           while (i--)
  527.             {
  528.               *(destptr++) = *(srcptr);
  529.               *(destptr++) = 255;
  530.               srcptr++;
  531.             }
  532.         }
  533.         }
  534.       else
  535.         {
  536.           /* --- These are suboptimal catch-all cases for when  --- */
  537.           /* --- this frame is bigger/smaller than the preview  --- */
  538.           /* --- buffer, and/or offset within it.               --- */
  539.       
  540.           /* FIXME: FINISH ME! */
  541.       
  542.           if (gimp_drawable_has_alpha (drawable->id))
  543.         { /* I alpha, diff size */
  544.           
  545.           srcptr = rawframe;
  546.           
  547.           for (j=rawy; j<rawheight+rawy; j++)
  548.             {
  549.               for (i=rawx; i<rawwidth+rawx; i++)
  550.             {
  551.               if ((i>=0 && i<width) &&
  552.                   (j>=0 && j<height))
  553.                 {
  554.                   if (*(srcptr+1))
  555.                 {
  556.                   this_frame[(j*width+i)*pixelstep] =
  557.                     *srcptr;
  558.                   this_frame[(j*width+i)*pixelstep
  559.                         + pixelstep - 1] =
  560.                     255;
  561.                 }
  562.                 }
  563.               
  564.               srcptr += 2;
  565.             }
  566.             }
  567.         }
  568.           else
  569.         {
  570.           /* I, no alpha, diff size */
  571.           
  572.           srcptr = rawframe;
  573.           
  574.           for (j=rawy; j<rawheight+rawy; j++)
  575.             {
  576.               for (i=rawx; i<rawwidth+rawx; i++)
  577.             {
  578.               if ((i>=0 && i<width) &&
  579.                   (j>=0 && j<height))
  580.                 {
  581.                   this_frame[(j*width+i)*pixelstep]
  582.                 = *srcptr;
  583.                   this_frame[(j*width+i)*pixelstep
  584.                     + pixelstep - 1] = 255;
  585.                 }
  586.               
  587.               srcptr ++;
  588.             }
  589.             }
  590.         }
  591.         }
  592.       break; /* case INDEXED/GRAY */
  593.  
  594.     }
  595.   
  596.       /* clean up */
  597.       gimp_drawable_detach(drawable);
  598.  
  599.  
  600.       can_combine = FALSE;
  601.       bbox_left = 0;
  602.       bbox_top = 0;
  603.       bbox_right = width;
  604.       bbox_bottom = height;
  605.       rbox_left = 0;
  606.       rbox_top = 0;
  607.       rbox_right = width;
  608.       rbox_bottom = height;
  609.       /* copy 'this' frame into a buffer which we can safely molest */
  610.       memcpy(opti_frame, this_frame, frame_sizebytes);
  611.       /*
  612.        *
  613.        * OPTIMIZE HERE!
  614.        *
  615.        */
  616.       if (
  617.       (this_frame_num != 0) /* Can't delta bottom frame! */
  618.       && (optimize)
  619.       )
  620.     {
  621.       int xit, yit, byteit;
  622.  
  623.       can_combine = TRUE;
  624.  
  625.       /*
  626.        * SEARCH FOR BOUNDING BOX
  627.        */
  628.       bbox_left = width;
  629.       bbox_top = height;
  630.       bbox_right = 0;
  631.       bbox_bottom = 0;
  632.       rbox_left = width;
  633.       rbox_top = height;
  634.       rbox_right = 0;
  635.       rbox_bottom = 0;
  636.       
  637.       for (yit=0; yit<height; yit++)
  638.         {
  639.           for (xit=0; xit<width; xit++)
  640.         {
  641.           gboolean keep_pix;
  642.           gboolean opaq_pix;
  643.           /* Check if 'this' and 'last' are transparent */
  644.           if (!(this_frame[yit*width*pixelstep + xit*pixelstep
  645.                   + pixelstep-1]&128)
  646.               &&
  647.               !(last_frame[yit*width*pixelstep + xit*pixelstep
  648.                   + pixelstep-1]&128))
  649.             {
  650.               keep_pix = FALSE;
  651.               opaq_pix = FALSE;
  652.               goto decided;
  653.             }
  654.           /* Check if just 'this' is transparent */
  655.           if ((last_frame[yit*width*pixelstep + xit*pixelstep
  656.                  + pixelstep-1]&128)
  657.               &&
  658.               !(this_frame[yit*width*pixelstep + xit*pixelstep
  659.                   + pixelstep-1]&128))
  660.             {
  661.               keep_pix = TRUE;
  662.               opaq_pix = FALSE;
  663.               can_combine = FALSE;
  664.               goto decided;
  665.             }
  666.           /* Check if just 'last' is transparent */
  667.           if (!(last_frame[yit*width*pixelstep + xit*pixelstep
  668.                   + pixelstep-1]&128)
  669.               &&
  670.               (this_frame[yit*width*pixelstep + xit*pixelstep
  671.                  + pixelstep-1]&128))
  672.             {
  673.               keep_pix = TRUE;
  674.               opaq_pix = TRUE;
  675.               goto decided;
  676.             }
  677.           /* If 'last' and 'this' are opaque, we have
  678.            *  to check if they're the same colour - we
  679.            *  only have to keep the pixel if 'last' or
  680.            *  'this' are opaque and different.
  681.            */
  682.           keep_pix = FALSE;
  683.           opaq_pix = TRUE;
  684.           for (byteit=0; byteit<pixelstep-1; byteit++)
  685.             {
  686.               if (last_frame[yit*width*pixelstep + xit*pixelstep
  687.                     + byteit]
  688.               !=
  689.               this_frame[yit*width*pixelstep + xit*pixelstep
  690.                     + byteit])
  691.             {
  692.               keep_pix = TRUE;
  693.               goto decided;
  694.             }                
  695.             }
  696.         decided:
  697.           if (opaq_pix)
  698.             {
  699.               if (xit<rbox_left) rbox_left=xit;
  700.               if (xit>rbox_right) rbox_right=xit;
  701.               if (yit<rbox_top) rbox_top=yit;
  702.               if (yit>rbox_bottom) rbox_bottom=yit;
  703.             }
  704.           if (keep_pix)
  705.             {
  706.               if (xit<bbox_left) bbox_left=xit;
  707.               if (xit>bbox_right) bbox_right=xit;
  708.               if (yit<bbox_top) bbox_top=yit;
  709.               if (yit>bbox_bottom) bbox_bottom=yit;
  710.             }
  711.           else
  712.             {
  713.               /* pixel didn't change this frame - make
  714.                *  it transparent in our optimized buffer!
  715.                */
  716.               opti_frame[yit*width*pixelstep + xit*pixelstep
  717.                 + pixelstep-1] = 0;
  718.             }
  719.         } /* xit */
  720.         } /* yit */
  721.  
  722.       if (!can_combine)
  723.         {
  724.           bbox_left = rbox_left;
  725.           bbox_top = rbox_top;
  726.           bbox_right = rbox_right;
  727.           bbox_bottom = rbox_bottom;
  728.         }
  729.  
  730.       bbox_right++;
  731.       bbox_bottom++;
  732.  
  733.       /*
  734.        * Collapse opti_frame data down such that the data
  735.        *  which occupies the bounding box sits at the start
  736.        *  of the data (for convenience with ..set_rect()).
  737.        */
  738.       destptr = opti_frame;
  739.       /*
  740.        * If can_combine, then it's safe to use our optimized
  741.        *  alpha information.  Otherwise, an opaque pixel became
  742.        *  transparent this frame, and we'll have to use the
  743.        *  actual true frame's alpha.
  744.        */
  745.       if (can_combine)
  746.         srcptr = opti_frame;
  747.       else
  748.         srcptr = this_frame;
  749.       for (yit=bbox_top; yit<bbox_bottom; yit++)
  750.         {
  751.           for (xit=bbox_left; xit<bbox_right; xit++)
  752.         {
  753.           for (byteit=0; byteit<pixelstep; byteit++)
  754.             {
  755.               *(destptr++) = srcptr[yit*pixelstep*width +
  756.                        pixelstep*xit + byteit];
  757.             }
  758.         }
  759.         }
  760.     } /* !bot frame? */
  761.       else
  762.     {
  763.       memcpy(opti_frame, this_frame, frame_sizebytes);
  764.     }
  765.  
  766.       /*
  767.        *
  768.        * REMEMBER THE ANIMATION STATUS TO DELTA AGAINST NEXT TIME
  769.        *
  770.        */
  771.       memcpy(last_frame, this_frame, frame_sizebytes);
  772.  
  773.       
  774.       /*
  775.        *
  776.        * PUT THIS FRAME INTO A NEW LAYER IN THE NEW IMAGE
  777.        *
  778.        */
  779.  
  780.       oldlayer_name =
  781.     gimp_layer_get_name(layers[total_frames-(this_frame_num+1)]);
  782.  
  783.       buflen = strlen(oldlayer_name) + 40;
  784.  
  785.       newlayer_name = g_malloc(buflen);
  786.  
  787.       remove_disposal_tag(newlayer_name, oldlayer_name);
  788.       g_free(oldlayer_name);
  789.  
  790.       oldlayer_name = g_malloc(buflen);
  791.  
  792.       remove_ms_tag(oldlayer_name, newlayer_name);
  793.  
  794.       g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
  795.          oldlayer_name, this_delay,
  796.          (this_frame_num ==  0) ? "" :
  797.          can_combine ? "(combine)" : "(replace)");
  798.  
  799.       g_free(oldlayer_name);
  800.  
  801.       /* Empty frame! */
  802.       if (bbox_right <= bbox_left ||
  803.       bbox_bottom <= bbox_top)
  804.     {
  805.       cumulated_delay += this_delay;
  806.  
  807.       g_free (newlayer_name);
  808.  
  809.       oldlayer_name =
  810.         gimp_layer_get_name(last_true_frame);
  811.       
  812.       buflen = strlen(oldlayer_name) + 40;
  813.       
  814.       newlayer_name = g_malloc(buflen);
  815.       
  816.       remove_disposal_tag(newlayer_name, oldlayer_name);
  817.       g_free(oldlayer_name);
  818.       
  819.       oldlayer_name = g_malloc(buflen);
  820.       
  821.       remove_ms_tag(oldlayer_name, newlayer_name);
  822.       
  823.       g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
  824.              oldlayer_name, cumulated_delay,
  825.              (this_frame_num ==  0) ? "" :
  826.              can_combine ? "(combine)" : "(replace)");
  827.  
  828.       gimp_layer_set_name(last_true_frame, newlayer_name);
  829.  
  830.       g_free (newlayer_name);
  831.     }
  832.       else
  833.     {
  834.       cumulated_delay = this_delay;
  835.  
  836.       last_true_frame =
  837.         new_layer_id = gimp_layer_new(new_image_id,
  838.                       newlayer_name,
  839.                       bbox_right-bbox_left,
  840.                       bbox_bottom-bbox_top,
  841.                       drawabletype_alpha,
  842.                       100.0,
  843.                       GIMP_NORMAL_MODE);
  844.       g_free(newlayer_name);
  845.       
  846.       gimp_image_add_layer (new_image_id, new_layer_id, 0);
  847.       
  848.       drawable = gimp_drawable_get (new_layer_id);
  849.       
  850.       gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
  851.                    bbox_right-bbox_left,
  852.                    bbox_bottom-bbox_top,
  853.                    TRUE, FALSE);
  854.       gimp_pixel_rgn_set_rect (&pixel_rgn, opti_frame, 0, 0,
  855.                    bbox_right-bbox_left,
  856.                    bbox_bottom-bbox_top);
  857.       gimp_drawable_flush (drawable);
  858.       gimp_drawable_detach (drawable);     
  859.       gimp_layer_translate (new_layer_id, (gint)bbox_left, (gint)bbox_top);
  860.     }
  861.  
  862.       gimp_progress_update (((double)this_frame_num+1.0) /
  863.                 ((double)total_frames));
  864.     }
  865.  
  866.     if (run_mode != GIMP_RUN_NONINTERACTIVE)
  867.        gimp_display_new (new_image_id);
  868.  
  869.   g_free(rawframe);
  870.   rawframe = NULL;
  871.   g_free(last_frame);
  872.   last_frame = NULL;
  873.   g_free(this_frame);
  874.   this_frame = NULL;
  875.   g_free(opti_frame);
  876.   opti_frame = NULL;
  877.  
  878.   return new_image_id;
  879. }
  880.  
  881.  
  882.  
  883.  
  884. /* Util. */
  885.  
  886. static DisposeType
  887. get_frame_disposal (const guint whichframe)
  888. {
  889.   gchar* layer_name;
  890.   DisposeType disposal;
  891.   
  892.   layer_name = gimp_layer_get_name(layers[total_frames-(whichframe+1)]);
  893.   disposal = parse_disposal_tag(layer_name);
  894.   g_free(layer_name);
  895.  
  896.   return(disposal);
  897. }
  898.  
  899.  
  900.  
  901. static guint32
  902. get_frame_duration (const guint whichframe)
  903. {
  904.   gchar* layer_name;
  905.   gint   duration = 0;
  906.  
  907.   layer_name = gimp_layer_get_name(layers[total_frames-(whichframe+1)]);
  908.   if (layer_name != NULL)
  909.     {
  910.       duration = parse_ms_tag(layer_name);
  911.       g_free(layer_name);
  912.     }
  913.   
  914.   if (duration < 0) duration = 100;  /* FIXME for default-if-not-said  */
  915.   if (duration == 0) duration = 100; /* FIXME - 0-wait is nasty */
  916.  
  917.   return ((guint32) duration);
  918. }
  919.  
  920.  
  921. static int
  922. is_ms_tag (const char *str, int *duration, int *taglength)
  923. {
  924.   gint sum = 0;
  925.   gint offset;
  926.   gint length;
  927.  
  928.   length = strlen(str);
  929.  
  930.   if (str[0] != '(')
  931.     return 0;
  932.  
  933.   offset = 1;
  934.  
  935.   /* eat any spaces between open-parenthesis and number */
  936.   while ((offset<length) && (str[offset] == ' '))
  937.     offset++;
  938.   
  939.   if ((offset>=length) || (!isdigit(str[offset])))
  940.     return 0;
  941.  
  942.   do
  943.     {
  944.       sum *= 10;
  945.       sum += str[offset] - '0';
  946.       offset++;
  947.     }
  948.   while ((offset<length) && (isdigit(str[offset])));  
  949.  
  950.   if (length-offset <= 2)
  951.     return 0;
  952.  
  953.   /* eat any spaces between number and 'ms' */
  954.   while ((offset<length) && (str[offset] == ' '))
  955.     offset++;
  956.  
  957.   if ((length-offset <= 2) ||
  958.       (toupper(str[offset]) != 'M') || (toupper(str[offset+1]) != 'S'))
  959.     return 0;
  960.  
  961.   offset += 2;
  962.  
  963.   /* eat any spaces between 'ms' and close-parenthesis */
  964.   while ((offset<length) && (str[offset] == ' '))
  965.     offset++;
  966.  
  967.   if ((length-offset < 1) || (str[offset] != ')'))
  968.     return 0;
  969.  
  970.   offset++;
  971.   
  972.   *duration = sum;
  973.   *taglength = offset;
  974.  
  975.   return 1;
  976. }
  977.  
  978.  
  979. static int
  980. parse_ms_tag (const char *str)
  981. {
  982.   int i;
  983.   int rtn;
  984.   int dummy;
  985.   int length;
  986.  
  987.   length = strlen(str);
  988.  
  989.   for (i=0; i<length; i++)
  990.     {
  991.       if (is_ms_tag(&str[i], &rtn, &dummy))
  992.     return rtn;
  993.     }
  994.   
  995.   return -1;
  996. }
  997.  
  998.  
  999. static int
  1000. is_disposal_tag (const char *str, DisposeType *disposal, int *taglength)
  1001. {
  1002.   if (strlen(str) != 9)
  1003.     return 0;
  1004.   
  1005.   if (strncmp(str, "(combine)", 9) == 0)
  1006.     {
  1007.       *taglength = 9;
  1008.       *disposal = DISPOSE_COMBINE;
  1009.       return 1;
  1010.     }
  1011.   else if (strncmp(str, "(replace)", 9) == 0)
  1012.     {
  1013.       *taglength = 9;
  1014.       *disposal = DISPOSE_REPLACE;
  1015.       return 1;
  1016.     }
  1017.  
  1018.   return 0;
  1019. }
  1020.  
  1021.  
  1022. static DisposeType
  1023. parse_disposal_tag (const char *str)
  1024. {
  1025.   DisposeType rtn;
  1026.   int i, dummy;
  1027.   gint length;
  1028.  
  1029.   length = strlen(str);
  1030.  
  1031.   for (i=0; i<length; i++)
  1032.     {
  1033.       if (is_disposal_tag(&str[i], &rtn, &dummy))
  1034.     {
  1035.       return rtn;
  1036.     }
  1037.     }
  1038.  
  1039.   return (DISPOSE_UNDEFINED); /* FIXME */
  1040. }
  1041.  
  1042.  
  1043.  
  1044. static void
  1045. remove_disposal_tag (char *dest, char *src)
  1046. {
  1047.   gint offset = 0;
  1048.   gint destoffset = 0;
  1049.   gint length;
  1050.   int taglength;
  1051.   DisposeType dummy;
  1052.  
  1053.   length = strlen(src);
  1054.  
  1055.   strcpy(dest, src);
  1056.  
  1057.   while (offset<=length)
  1058.     {
  1059.       if (is_disposal_tag(&src[offset], &dummy, &taglength))
  1060.     {
  1061.       offset += taglength;
  1062.     }
  1063.       dest[destoffset] = src[offset];
  1064.       destoffset++;
  1065.       offset++;
  1066.     }
  1067.  
  1068.   dest[offset] = '\0';
  1069. }
  1070.  
  1071.  
  1072.  
  1073. static void
  1074. remove_ms_tag (char *dest, char *src)
  1075. {
  1076.   gint offset = 0;
  1077.   gint destoffset = 0;
  1078.   gint length;
  1079.   int taglength;
  1080.   int dummy;
  1081.  
  1082.   length = strlen(src);
  1083.  
  1084.   strcpy(dest, src);
  1085.  
  1086.   while (offset<=length)
  1087.     {
  1088.       if (is_ms_tag(&src[offset], &dummy, &taglength))
  1089.     {
  1090.       offset += taglength;
  1091.     }
  1092.       dest[destoffset] = src[offset];
  1093.       destoffset++;
  1094.       offset++;
  1095.     }
  1096.  
  1097.   dest[offset] = '\0';
  1098. }
  1099.