gimp/plug-ins/common/animoptimize.c
2000-08-30 17:59:45 +00:00

1099 lines
25 KiB
C

/*
* Animation Optimizer plug-in version 1.0.4
*
* (c) Adam D. Moss, 1997-2000
* adam@gimp.org
* adam@foxbox.org
*
* This is part of the GIMP package and falls under the GPL.
*/
/*
* REVISION HISTORY:
*
* 2000-08-30 : version 1.0.4
* Change default frame duration from 125ms to 100ms for
* consistancy.
*
* 2000-06-05 : version 1.0.3
* Fix old bug which could cause errors in evaluating the
* final pixel of each composed layer.
*
* 2000-01-13 : version 1.0.2
* Collapse timing of completely optimized-away frames
* onto previous surviving frame. Also be looser with
* (XXXXX) tag parsing.
*
* 2000-01-07 : version 1.0.1
* PDB interface submitted by Andreas Jaekel
* <jaekel@cablecats.de>
*
* 98.05.17 : version 1.0.0
* Finally preserves frame timings / layer names. Has
* a progress bar now. No longer beta, I suppose.
*
* 98.04.19 : version 0.70.0
* Plug-in doubles up as Animation UnOptimize too! (This
* is somewhat more useful than it sounds.)
*
* 98.03.16 : version 0.61.0
* Support more rare opaque/transparent combinations.
*
* 97.12.09 : version 0.60.0
* Added support for INDEXED* and GRAY* images.
*
* 97.12.09 : version 0.52.0
* Fixed some bugs.
*
* 97.12.08 : version 0.51.0
* Relaxed bounding box on optimized layers marked
* 'replace'.
*
* 97.12.07 : version 0.50.0
* Initial release.
*/
/*
* BUGS:
* ?
*/
/*
* TODO:
* User interface
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <libgimp/gimp.h>
#include "libgimp/stdplugins-intl.h"
typedef enum
{
DISPOSE_UNDEFINED = 0x00,
DISPOSE_COMBINE = 0x01,
DISPOSE_REPLACE = 0x02
} DisposeType;
/* Declare local functions. */
static void query (void);
static void run (gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static gint32 do_optimizations (GimpRunModeType run_mode);
/* tag util functions*/
static int parse_ms_tag (const char *str);
static DisposeType parse_disposal_tag (const char *str);
static DisposeType get_frame_disposal (const guint whichframe);
static guint32 get_frame_duration (const guint whichframe);
static void remove_disposal_tag (char* dest, char *src);
static void remove_ms_tag (char* dest, char *src);
static int is_disposal_tag (const char *str,
DisposeType *disposal,
int *taglength);
static int is_ms_tag (const char *str,
int *duration,
int *taglength);
GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
/* Global widgets'n'stuff */
static guint width,height;
static gint32 image_id;
static gint32 new_image_id;
static gint32 total_frames;
static gint32 *layers;
static GimpDrawable *drawable;
static GimpImageBaseType imagetype;
static GimpImageType drawabletype_alpha;
static guchar pixelstep;
static guchar *palette;
static gint ncolours;
static gboolean optimize;
MAIN ()
static void
query (void)
{
static GimpParamDef args[] =
{
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
};
static GimpParamDef return_args[] =
{
{ GIMP_PDB_IMAGE, "result", "Resulting image" }
};
static gint nargs = sizeof (args) / sizeof (args[0]);
static gint nreturn_args = sizeof (return_args) / sizeof (return_args[0]);
gimp_install_procedure ("plug_in_animationoptimize",
"This plugin applies various optimizations to"
" a GIMP layer-based animation.",
"",
"Adam D. Moss <adam@gimp.org>",
"Adam D. Moss <adam@gimp.org>",
"1997-98",
N_("<Image>/Filters/Animation/Animation Optimize"),
"RGB*, INDEXED*, GRAY*",
GIMP_PLUGIN,
nargs, nreturn_args,
args, return_args);
gimp_install_procedure ("plug_in_animationunoptimize",
"This plugin 'simplifies' a GIMP layer-based"
" animation that has been AnimationOptimized. This"
" makes the animation much easier to work with if,"
" for example, the optimized version is all you"
" have.",
"",
"Adam D. Moss <adam@gimp.org>",
"Adam D. Moss <adam@gimp.org>",
"1997-98",
N_("<Image>/Filters/Animation/Animation UnOptimize"),
"RGB*, INDEXED*, GRAY*",
GIMP_PLUGIN,
nargs, nreturn_args,
args, return_args);
}
static void
run (gchar *name,
gint n_params,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[2];
GimpRunModeType run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
*nreturn_vals = 2;
*return_vals = values;
run_mode = param[0].data.d_int32;
if (run_mode == GIMP_RUN_NONINTERACTIVE)
{
if (n_params != 3)
{
status = GIMP_PDB_CALLING_ERROR;
}
}
INIT_I18N();
/* Check the procedure name we were called with, to decide
what needs to be done. */
if (strcmp(name,"plug_in_animationoptimize")==0)
optimize = TRUE;
else
optimize = FALSE; /* UnOptimize */
if (status == GIMP_PDB_SUCCESS)
{
image_id = param[1].data.d_image;
new_image_id = do_optimizations(run_mode);
if (run_mode != GIMP_RUN_NONINTERACTIVE)
gimp_displays_flush();
}
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = status;
values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = new_image_id;
}
/* Rendering Functions */
static void
total_alpha(guchar* imdata, guint32 numpix, guchar bytespp)
{
/* Set image to total-transparency w/black
*/
memset(imdata, 0, numpix*bytespp);
}
static gint32
do_optimizations(GimpRunModeType run_mode)
{
GimpPixelRgn pixel_rgn;
static guchar* rawframe = NULL;
static gint rawwidth=0, rawheight=0, rawbpp=0;
gint rawx=0, rawy=0;
guchar* srcptr;
guchar* destptr;
gint i,j,this_frame_num;
guint32 frame_sizebytes;
gint32 new_layer_id;
DisposeType dispose;
guchar* this_frame = NULL;
guchar* last_frame = NULL;
guchar* opti_frame = NULL;
int this_delay;
int cumulated_delay = 0;
int last_true_frame = -1;
int buflen;
gchar* oldlayer_name;
gchar* newlayer_name;
gboolean can_combine;
gint32 bbox_top, bbox_bottom, bbox_left, bbox_right;
gint32 rbox_top, rbox_bottom, rbox_left, rbox_right;
if (optimize)
gimp_progress_init (_("Optimizing Animation..."));
else
gimp_progress_init (_("UnOptimizing Animation..."));
width = gimp_image_width(image_id);
height = gimp_image_height(image_id);
layers = gimp_image_get_layers (image_id, &total_frames);
imagetype = gimp_image_base_type(image_id);
pixelstep = (imagetype == GIMP_RGB) ? 4 : 2;
drawabletype_alpha = (imagetype == GIMP_RGB) ? GIMP_RGBA_IMAGE :
((imagetype == GIMP_INDEXED) ? GIMP_INDEXEDA_IMAGE : GIMP_GRAYA_IMAGE);
frame_sizebytes = width * height * pixelstep;
this_frame = g_malloc (frame_sizebytes);
last_frame = g_malloc (frame_sizebytes);
opti_frame = g_malloc (frame_sizebytes);
total_alpha (this_frame, width*height, pixelstep);
total_alpha (last_frame, width*height, pixelstep);
new_image_id = gimp_image_new(width, height, imagetype);
if (imagetype == GIMP_INDEXED)
{
palette = gimp_image_get_cmap (image_id, &ncolours);
gimp_image_set_cmap (new_image_id, palette, ncolours);
}
if ((this_frame == NULL) || (last_frame == NULL) || (opti_frame == NULL))
g_error(_("Not enough memory to allocate buffers for optimization.\n"));
for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
{
/*
* We actually unoptimize the animation before re-optimizing it,
* otherwise the logic can get a bit too hard... for me.
*/
/*
*
* BUILD THIS FRAME
*
*/
drawable = gimp_drawable_get (layers[total_frames-(this_frame_num+1)]);
/* Image has been closed/etc since we got the layer list? */
/* FIXME - How do we tell if a gimp_drawable_get() fails? */
if (gimp_drawable_width (drawable->id) == 0)
{
gimp_quit ();
}
this_delay = get_frame_duration (this_frame_num);
dispose = get_frame_disposal (this_frame_num);
if (dispose == DISPOSE_REPLACE)
{
total_alpha (this_frame, width * height, pixelstep);
}
/* only get a new 'raw' drawable-data buffer if this and
the previous raw buffer were different sizes*/
if ((rawwidth*rawheight*rawbpp)
!=
((gimp_drawable_width (drawable->id)*
gimp_drawable_height (drawable->id)*
gimp_drawable_bpp (drawable->id))))
{
if (rawframe != NULL) g_free (rawframe);
rawframe = g_malloc ((gimp_drawable_width (drawable->id)) *
(gimp_drawable_height (drawable->id)) *
(gimp_drawable_bpp (drawable->id)));
}
rawwidth = gimp_drawable_width (drawable->id);
rawheight = gimp_drawable_height (drawable->id);
rawbpp = gimp_drawable_bpp (drawable->id);
/* Initialise and fetch the whole raw new frame */
gimp_pixel_rgn_init (&pixel_rgn,
drawable,
0, 0,
drawable->width, drawable->height,
FALSE,
FALSE);
gimp_pixel_rgn_get_rect (&pixel_rgn,
rawframe,
0, 0,
drawable->width, drawable->height);
/* gimp_pixel_rgns_register (1, &pixel_rgn);*/
gimp_drawable_offsets (drawable->id,
&rawx,
&rawy);
/* render... */
switch (imagetype)
{
case GIMP_RGB:
if ((rawwidth==width) &&
(rawheight==height) &&
(rawx==0) &&
(rawy==0))
{
/* --- These cases are for the best cases, in --- */
/* --- which this frame is the same size and position --- */
/* --- as the preview buffer itself --- */
if (gimp_drawable_has_alpha (drawable->id))
{ /* alpha RGB, same size */
destptr = this_frame;
srcptr = rawframe;
i = rawwidth*rawheight;
while (i--)
{
if (!((*(srcptr+3))&128))
{
srcptr += 4;
destptr += 4;
continue;
}
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
*(destptr++) = 255;
srcptr++;
}
}
else /* RGB no alpha, same size */
{
destptr = this_frame;
srcptr = rawframe;
i = rawwidth*rawheight;
while (i--)
{
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
*(destptr++) = 255;
}
}
}
else
{
/* --- These are suboptimal catch-all cases for when --- */
/* --- this frame is bigger/smaller than the preview --- */
/* --- buffer, and/or offset within it. --- */
/* FIXME: FINISH ME! [done?] */
if (gimp_drawable_has_alpha (drawable->id))
{ /* RGB alpha, diff size */
destptr = this_frame;
srcptr = rawframe;
for (j=rawy; j<rawheight+rawy; j++)
{
for (i=rawx; i<rawwidth+rawx; i++)
{
if ((i>=0 && i<width) &&
(j>=0 && j<height))
{
if ((*(srcptr+3))&128)
{
this_frame[(j*width+i)*4 ] = *(srcptr);
this_frame[(j*width+i)*4 +1] = *(srcptr+1);
this_frame[(j*width+i)*4 +2] = *(srcptr+2);
this_frame[(j*width+i)*4 +3] = 255;
}
}
srcptr += 4;
}
}
}
else
{
/* RGB no alpha, diff size */
destptr = this_frame;
srcptr = rawframe;
for (j=rawy; j<rawheight+rawy; j++)
{
for (i=rawx; i<rawwidth+rawx; i++)
{
if ((i>=0 && i<width) &&
(j>=0 && j<height))
{
this_frame[(j*width+i)*4 ] = *(srcptr);
this_frame[(j*width+i)*4 +1] = *(srcptr+1);
this_frame[(j*width+i)*4 +2] = *(srcptr+2);
this_frame[(j*width+i)*4 +3] = 255;
}
srcptr += 3;
}
}
}
}
break; /* case RGB */
case GIMP_GRAY:
case GIMP_INDEXED:
if ((rawwidth==width) &&
(rawheight==height) &&
(rawx==0) &&
(rawy==0))
{
/* --- These cases are for the best cases, in --- */
/* --- which this frame is the same size and position --- */
/* --- as the preview buffer itself --- */
if (gimp_drawable_has_alpha (drawable->id))
{ /* I alpha, same size */
destptr = this_frame;
srcptr = rawframe;
i = rawwidth*rawheight;
while (i--)
{
if (!(*(srcptr+1)))
{
srcptr += 2;
destptr += 2;
continue;
}
*(destptr++) = *(srcptr);
*(destptr++) = 255;
srcptr+=2;
}
}
else /* I, no alpha, same size */
{
destptr = this_frame;
srcptr = rawframe;
i = rawwidth*rawheight;
while (i--)
{
*(destptr++) = *(srcptr);
*(destptr++) = 255;
srcptr++;
}
}
}
else
{
/* --- These are suboptimal catch-all cases for when --- */
/* --- this frame is bigger/smaller than the preview --- */
/* --- buffer, and/or offset within it. --- */
/* FIXME: FINISH ME! */
if (gimp_drawable_has_alpha (drawable->id))
{ /* I alpha, diff size */
srcptr = rawframe;
for (j=rawy; j<rawheight+rawy; j++)
{
for (i=rawx; i<rawwidth+rawx; i++)
{
if ((i>=0 && i<width) &&
(j>=0 && j<height))
{
if (*(srcptr+1))
{
this_frame[(j*width+i)*pixelstep] =
*srcptr;
this_frame[(j*width+i)*pixelstep
+ pixelstep - 1] =
255;
}
}
srcptr += 2;
}
}
}
else
{
/* I, no alpha, diff size */
srcptr = rawframe;
for (j=rawy; j<rawheight+rawy; j++)
{
for (i=rawx; i<rawwidth+rawx; i++)
{
if ((i>=0 && i<width) &&
(j>=0 && j<height))
{
this_frame[(j*width+i)*pixelstep]
= *srcptr;
this_frame[(j*width+i)*pixelstep
+ pixelstep - 1] = 255;
}
srcptr ++;
}
}
}
}
break; /* case INDEXED/GRAY */
}
/* clean up */
gimp_drawable_detach(drawable);
can_combine = FALSE;
bbox_left = 0;
bbox_top = 0;
bbox_right = width;
bbox_bottom = height;
rbox_left = 0;
rbox_top = 0;
rbox_right = width;
rbox_bottom = height;
/* copy 'this' frame into a buffer which we can safely molest */
memcpy(opti_frame, this_frame, frame_sizebytes);
/*
*
* OPTIMIZE HERE!
*
*/
if (
(this_frame_num != 0) /* Can't delta bottom frame! */
&& (optimize)
)
{
int xit, yit, byteit;
can_combine = TRUE;
/*
* SEARCH FOR BOUNDING BOX
*/
bbox_left = width;
bbox_top = height;
bbox_right = 0;
bbox_bottom = 0;
rbox_left = width;
rbox_top = height;
rbox_right = 0;
rbox_bottom = 0;
for (yit=0; yit<height; yit++)
{
for (xit=0; xit<width; xit++)
{
gboolean keep_pix;
gboolean opaq_pix;
/* Check if 'this' and 'last' are transparent */
if (!(this_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128)
&&
!(last_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128))
{
keep_pix = FALSE;
opaq_pix = FALSE;
goto decided;
}
/* Check if just 'this' is transparent */
if ((last_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128)
&&
!(this_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128))
{
keep_pix = TRUE;
opaq_pix = FALSE;
can_combine = FALSE;
goto decided;
}
/* Check if just 'last' is transparent */
if (!(last_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128)
&&
(this_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1]&128))
{
keep_pix = TRUE;
opaq_pix = TRUE;
goto decided;
}
/* If 'last' and 'this' are opaque, we have
* to check if they're the same colour - we
* only have to keep the pixel if 'last' or
* 'this' are opaque and different.
*/
keep_pix = FALSE;
opaq_pix = TRUE;
for (byteit=0; byteit<pixelstep-1; byteit++)
{
if (last_frame[yit*width*pixelstep + xit*pixelstep
+ byteit]
!=
this_frame[yit*width*pixelstep + xit*pixelstep
+ byteit])
{
keep_pix = TRUE;
goto decided;
}
}
decided:
if (opaq_pix)
{
if (xit<rbox_left) rbox_left=xit;
if (xit>rbox_right) rbox_right=xit;
if (yit<rbox_top) rbox_top=yit;
if (yit>rbox_bottom) rbox_bottom=yit;
}
if (keep_pix)
{
if (xit<bbox_left) bbox_left=xit;
if (xit>bbox_right) bbox_right=xit;
if (yit<bbox_top) bbox_top=yit;
if (yit>bbox_bottom) bbox_bottom=yit;
}
else
{
/* pixel didn't change this frame - make
* it transparent in our optimized buffer!
*/
opti_frame[yit*width*pixelstep + xit*pixelstep
+ pixelstep-1] = 0;
}
} /* xit */
} /* yit */
if (!can_combine)
{
bbox_left = rbox_left;
bbox_top = rbox_top;
bbox_right = rbox_right;
bbox_bottom = rbox_bottom;
}
bbox_right++;
bbox_bottom++;
/*
* Collapse opti_frame data down such that the data
* which occupies the bounding box sits at the start
* of the data (for convenience with ..set_rect()).
*/
destptr = opti_frame;
/*
* If can_combine, then it's safe to use our optimized
* alpha information. Otherwise, an opaque pixel became
* transparent this frame, and we'll have to use the
* actual true frame's alpha.
*/
if (can_combine)
srcptr = opti_frame;
else
srcptr = this_frame;
for (yit=bbox_top; yit<bbox_bottom; yit++)
{
for (xit=bbox_left; xit<bbox_right; xit++)
{
for (byteit=0; byteit<pixelstep; byteit++)
{
*(destptr++) = srcptr[yit*pixelstep*width +
pixelstep*xit + byteit];
}
}
}
} /* !bot frame? */
else
{
memcpy(opti_frame, this_frame, frame_sizebytes);
}
/*
*
* REMEMBER THE ANIMATION STATUS TO DELTA AGAINST NEXT TIME
*
*/
memcpy(last_frame, this_frame, frame_sizebytes);
/*
*
* PUT THIS FRAME INTO A NEW LAYER IN THE NEW IMAGE
*
*/
oldlayer_name =
gimp_layer_get_name(layers[total_frames-(this_frame_num+1)]);
buflen = strlen(oldlayer_name) + 40;
newlayer_name = g_malloc(buflen);
remove_disposal_tag(newlayer_name, oldlayer_name);
g_free(oldlayer_name);
oldlayer_name = g_malloc(buflen);
remove_ms_tag(oldlayer_name, newlayer_name);
g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
oldlayer_name, this_delay,
(this_frame_num == 0) ? "" :
can_combine ? "(combine)" : "(replace)");
g_free(oldlayer_name);
/* Empty frame! */
if (bbox_right <= bbox_left ||
bbox_bottom <= bbox_top)
{
cumulated_delay += this_delay;
g_free (newlayer_name);
oldlayer_name =
gimp_layer_get_name(last_true_frame);
buflen = strlen(oldlayer_name) + 40;
newlayer_name = g_malloc(buflen);
remove_disposal_tag(newlayer_name, oldlayer_name);
g_free(oldlayer_name);
oldlayer_name = g_malloc(buflen);
remove_ms_tag(oldlayer_name, newlayer_name);
g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
oldlayer_name, cumulated_delay,
(this_frame_num == 0) ? "" :
can_combine ? "(combine)" : "(replace)");
gimp_layer_set_name(last_true_frame, newlayer_name);
g_free (newlayer_name);
}
else
{
cumulated_delay = this_delay;
last_true_frame =
new_layer_id = gimp_layer_new(new_image_id,
newlayer_name,
bbox_right-bbox_left,
bbox_bottom-bbox_top,
drawabletype_alpha,
100.0,
GIMP_NORMAL_MODE);
g_free(newlayer_name);
gimp_image_add_layer (new_image_id, new_layer_id, 0);
drawable = gimp_drawable_get (new_layer_id);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
bbox_right-bbox_left,
bbox_bottom-bbox_top,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, opti_frame, 0, 0,
bbox_right-bbox_left,
bbox_bottom-bbox_top);
gimp_drawable_flush (drawable);
gimp_drawable_detach (drawable);
gimp_layer_translate (new_layer_id, (gint)bbox_left, (gint)bbox_top);
}
gimp_progress_update (((double)this_frame_num+1.0) /
((double)total_frames));
}
if (run_mode != GIMP_RUN_NONINTERACTIVE)
gimp_display_new (new_image_id);
g_free(rawframe);
rawframe = NULL;
g_free(last_frame);
last_frame = NULL;
g_free(this_frame);
this_frame = NULL;
g_free(opti_frame);
opti_frame = NULL;
return new_image_id;
}
/* Util. */
static DisposeType
get_frame_disposal (const guint whichframe)
{
gchar* layer_name;
DisposeType disposal;
layer_name = gimp_layer_get_name(layers[total_frames-(whichframe+1)]);
disposal = parse_disposal_tag(layer_name);
g_free(layer_name);
return(disposal);
}
static guint32
get_frame_duration (const guint whichframe)
{
gchar* layer_name;
gint duration = 0;
layer_name = gimp_layer_get_name(layers[total_frames-(whichframe+1)]);
if (layer_name != NULL)
{
duration = parse_ms_tag(layer_name);
g_free(layer_name);
}
if (duration < 0) duration = 100; /* FIXME for default-if-not-said */
if (duration == 0) duration = 100; /* FIXME - 0-wait is nasty */
return ((guint32) duration);
}
static int
is_ms_tag (const char *str, int *duration, int *taglength)
{
gint sum = 0;
gint offset;
gint length;
length = strlen(str);
if (str[0] != '(')
return 0;
offset = 1;
/* eat any spaces between open-parenthesis and number */
while ((offset<length) && (str[offset] == ' '))
offset++;
if ((offset>=length) || (!isdigit(str[offset])))
return 0;
do
{
sum *= 10;
sum += str[offset] - '0';
offset++;
}
while ((offset<length) && (isdigit(str[offset])));
if (length-offset <= 2)
return 0;
/* eat any spaces between number and 'ms' */
while ((offset<length) && (str[offset] == ' '))
offset++;
if ((length-offset <= 2) ||
(toupper(str[offset]) != 'M') || (toupper(str[offset+1]) != 'S'))
return 0;
offset += 2;
/* eat any spaces between 'ms' and close-parenthesis */
while ((offset<length) && (str[offset] == ' '))
offset++;
if ((length-offset < 1) || (str[offset] != ')'))
return 0;
offset++;
*duration = sum;
*taglength = offset;
return 1;
}
static int
parse_ms_tag (const char *str)
{
int i;
int rtn;
int dummy;
int length;
length = strlen(str);
for (i=0; i<length; i++)
{
if (is_ms_tag(&str[i], &rtn, &dummy))
return rtn;
}
return -1;
}
static int
is_disposal_tag (const char *str, DisposeType *disposal, int *taglength)
{
if (strlen(str) != 9)
return 0;
if (strncmp(str, "(combine)", 9) == 0)
{
*taglength = 9;
*disposal = DISPOSE_COMBINE;
return 1;
}
else if (strncmp(str, "(replace)", 9) == 0)
{
*taglength = 9;
*disposal = DISPOSE_REPLACE;
return 1;
}
return 0;
}
static DisposeType
parse_disposal_tag (const char *str)
{
DisposeType rtn;
int i, dummy;
gint length;
length = strlen(str);
for (i=0; i<length; i++)
{
if (is_disposal_tag(&str[i], &rtn, &dummy))
{
return rtn;
}
}
return (DISPOSE_UNDEFINED); /* FIXME */
}
static void
remove_disposal_tag (char *dest, char *src)
{
gint offset = 0;
gint destoffset = 0;
gint length;
int taglength;
DisposeType dummy;
length = strlen(src);
strcpy(dest, src);
while (offset<=length)
{
if (is_disposal_tag(&src[offset], &dummy, &taglength))
{
offset += taglength;
}
dest[destoffset] = src[offset];
destoffset++;
offset++;
}
dest[offset] = '\0';
}
static void
remove_ms_tag (char *dest, char *src)
{
gint offset = 0;
gint destoffset = 0;
gint length;
int taglength;
int dummy;
length = strlen(src);
strcpy(dest, src);
while (offset<=length)
{
if (is_ms_tag(&src[offset], &dummy, &taglength))
{
offset += taglength;
}
dest[destoffset] = src[offset];
destoffset++;
offset++;
}
dest[offset] = '\0';
}