From 4fe0ff84634a32a836788eaa6505c5d3b29bee19 Mon Sep 17 00:00:00 2001 From: Manish Singh Date: Wed, 11 Mar 1998 10:28:33 +0000 Subject: [PATCH] Added animoptimize plugin -Yosh --- ChangeLog | 4 + configure.in | 1 + plug-ins/Makefile.am | 1 + plug-ins/animoptimize/.cvsignore | 6 + plug-ins/animoptimize/Makefile.am | 39 ++ plug-ins/animoptimize/animoptimize.c | 957 +++++++++++++++++++++++++++ plug-ins/common/animoptimize.c | 957 +++++++++++++++++++++++++++ 7 files changed, 1965 insertions(+) create mode 100644 plug-ins/animoptimize/.cvsignore create mode 100644 plug-ins/animoptimize/Makefile.am create mode 100644 plug-ins/animoptimize/animoptimize.c create mode 100644 plug-ins/common/animoptimize.c diff --git a/ChangeLog b/ChangeLog index 3390d2a697..c6b20a6e6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Wed Mar 11 02:26:51 PST 1998 Manish Singh + + * added animoptimize plugin + Wed Mar 11 00:27:35 PST 1998 Manish Singh * app/convert.c: fixed memory overrun error diff --git a/configure.in b/configure.in index d25a16225a..e6c58f19a5 100644 --- a/configure.in +++ b/configure.in @@ -400,6 +400,7 @@ plug-ins/illusion/Makefile plug-ins/fractaltrace/Makefile plug-ins/flarefx/Makefile plug-ins/webbrowser/Makefile +plug-ins/animoptimze/Makefile app/Makefile docs/Makefile data/Makefile diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am index 814733ef98..737b55dbdd 100644 --- a/plug-ins/Makefile.am +++ b/plug-ins/Makefile.am @@ -15,6 +15,7 @@ SUBDIRS = \ @XPM@ \ CML_explorer \ align_layers \ + animoptimize \ apply_lens \ autocrop \ animationplay \ diff --git a/plug-ins/animoptimize/.cvsignore b/plug-ins/animoptimize/.cvsignore new file mode 100644 index 0000000000..8a6aa14e65 --- /dev/null +++ b/plug-ins/animoptimize/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.deps +_libs +.libs +animoptimize diff --git a/plug-ins/animoptimize/Makefile.am b/plug-ins/animoptimize/Makefile.am new file mode 100644 index 0000000000..1703e8c054 --- /dev/null +++ b/plug-ins/animoptimize/Makefile.am @@ -0,0 +1,39 @@ +## Process this file with automake to produce Makefile.in + +pluginlibdir = $(gimpplugindir)/plug-ins + +pluginlib_PROGRAMS = animoptimize + +animoptimize_SOURCES = \ + animoptimize.c + +INCLUDES = \ + $(X_CFLAGS) \ + -I$(top_srcdir) \ + -I$(includedir) + +LDADD = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la \ + $(X_LIBS) \ + \ + -lc + +DEPS = \ + $(top_builddir)/libgimp/libgimpui.la \ + $(top_builddir)/libgimp/libgimp.la + +animoptimize_DEPENDENCIES = $(DEPS) + +.PHONY: files + +files: + @files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \ + echo $$p; \ + done + @for subdir in $(SUBDIRS); do \ + files=`cd $$subdir; $(MAKE) files | grep -v "make\[[1-9]\]"`; \ + for file in $$files; do \ + echo $$subdir/$$file; \ + done; \ + done diff --git a/plug-ins/animoptimize/animoptimize.c b/plug-ins/animoptimize/animoptimize.c new file mode 100644 index 0000000000..fb7f2b900b --- /dev/null +++ b/plug-ins/animoptimize/animoptimize.c @@ -0,0 +1,957 @@ +/* + * Animation Optimizer plug-in version 0.60.0 + * + * by Adam D. Moss, 1997 + * adam@gimp.org + * adam@foxbox.org + * + * This is part of the GIMP package and falls under the GPL. + */ + +/* + * REVISION HISTORY: + * + * 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: + * Probably a few + */ + +/* + * TODO: + * User interface + * PDB interface + */ + +#include +#include +#include +#include +#include "libgimp/gimp.h" +#include "gtk/gtk.h" + + + +typedef enum +{ + DISPOSE_UNDEFINED = 0x00, + DISPOSE_COMBINE = 0x01, + DISPOSE_REPLACE = 0x02 +} DisposeType; + + + +/* Declare local functions. */ +static void query(void); +static void run(char *name, + int nparams, + GParam * param, + int *nreturn_vals, + GParam ** return_vals); + +static void do_optimizations (void); +static int parse_ms_tag (char *str); +static DisposeType parse_disposal_tag (char *str); + +static void window_close_callback (GtkWidget *widget, + gpointer data); +static void playstop_callback (GtkWidget *widget, + gpointer data); +static void rewind_callback (GtkWidget *widget, + gpointer data); +static void step_callback (GtkWidget *widget, + gpointer data); + +static DisposeType get_frame_disposal (guint whichframe); +static void render_frame (gint32 whichframe); +static void show_frame (void); + + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + + + + +/* Global widgets'n'stuff */ +GtkWidget* progress; +guint width,height; +gint32 image_id; +gint32 new_image_id; +gint32 total_frames; +guint frame_number; +gint32* layers; +GDrawable* drawable; +gboolean playing = FALSE; +GImageType imagetype; +GDrawableType drawabletype_alpha; +guchar pixelstep; +guchar* palette; +gint ncolours; + + + + +MAIN() + +static void query() +{ + static GParamDef args[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + }; + static GParamDef *return_vals = NULL; + static int nargs = sizeof(args) / sizeof(args[0]); + static int nreturn_vals = 0; + + gimp_install_procedure("plug_in_animationoptimize", + "This plugin applies various optimizations to" + " a GIMP layer-based animation.", + "", + "Adam D. Moss ", + "Adam D. Moss ", + "1997", + "/Filters/Animation/Animation Optimize", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs, nreturn_vals, + args, return_vals); +} + +static void run(char *name, int n_params, GParam * param, int *nreturn_vals, + GParam ** return_vals) +{ + static GParam values[1]; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + + *nreturn_vals = 1; + *return_vals = values; + + run_mode = param[0].data.d_int32; + + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + image_id = param[1].data.d_image; + + do_optimizations(); + + if (run_mode != RUN_NONINTERACTIVE) + gimp_displays_flush(); + } + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; +} + + + +static int +parse_ms_tag (char *str) +{ + gint sum = 0; + gint offset = 0; + gint length; + + length = strlen(str); + + while ((offset=length) + return(-1); + + if (!isdigit(str[++offset])) + return(-2); + + do + { + sum *= 10; + sum += str[offset] - '0'; + offset++; + } + while ((offsetaction_area), + button, TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + + { + /* The 'playback' half of the dialog */ + + /* windowname = g_malloc(strlen("Playback: ")+strlen(imagename)+1); + strcpy(windowname,"Playback: "); + strcat(windowname,imagename); + frame = gtk_frame_new (windowname); + g_free(windowname); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 3); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), + frame, TRUE, TRUE, 0); + + { + hbox = gtk_hbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 3); + gtk_container_add (GTK_CONTAINER (frame), hbox); + + { + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 3); + gtk_container_add (GTK_CONTAINER (hbox), vbox); + + { + progress = gtk_progress_bar_new (); + gtk_widget_set_usize (progress, 150, 15); + gtk_box_pack_start (GTK_BOX (vbox), progress, TRUE, TRUE, 0); + gtk_widget_show (progress); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox2), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox2, TRUE, TRUE, 0); + + { + button = gtk_button_new_with_label ("Play/Stop"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) playstop_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Rewind"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) rewind_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Step"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) step_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + } + if (total_frames<=1) gtk_widget_set_sensitive (hbox2, FALSE); + gtk_widget_show(hbox2); + + frame2 = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame2, FALSE, FALSE, 0); + + { + preview = gtk_preview_new (GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (preview), width, height); + gtk_container_add (GTK_CONTAINER (frame2), preview); + gtk_widget_show(preview); + } + gtk_widget_show(frame2); + } + gtk_widget_show(vbox); + + } + gtk_widget_show(hbox); + + } + gtk_widget_show(frame); + */ + } + gtk_widget_show(dlg); +} + + + +/* 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 void +do_optimizations(void) +{ + GPixelRgn 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; + + gboolean can_combine; + + gint32 bbox_top, bbox_bottom, bbox_left, bbox_right; + gint32 rbox_top, rbox_bottom, rbox_left, rbox_right; + + 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 == RGB) ? 4 : 2; + + drawabletype_alpha = (imagetype == RGB) ? RGBA_IMAGE : + ((imagetype == INDEXED) ? INDEXEDA_IMAGE : 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 == 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_numid)==0) + { + window_close_callback(NULL, NULL); + } + + 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 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! */ + + if (gimp_drawable_has_alpha (drawable->id)) + { /* RGB alpha, diff size */ + + destptr = this_frame; + srcptr = rawframe; + + for (j=rawy; j=0 && i=0 && jid)) + { /* 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=0 && i=0 && jrbox_right) rbox_right=xit; + if (yitrbox_bottom) rbox_bottom=yit; + } + if (keep_pix) + { + if (xitbbox_right) bbox_right=xit; + if (yitbbox_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 +#include +#include +#include +#include "libgimp/gimp.h" +#include "gtk/gtk.h" + + + +typedef enum +{ + DISPOSE_UNDEFINED = 0x00, + DISPOSE_COMBINE = 0x01, + DISPOSE_REPLACE = 0x02 +} DisposeType; + + + +/* Declare local functions. */ +static void query(void); +static void run(char *name, + int nparams, + GParam * param, + int *nreturn_vals, + GParam ** return_vals); + +static void do_optimizations (void); +static int parse_ms_tag (char *str); +static DisposeType parse_disposal_tag (char *str); + +static void window_close_callback (GtkWidget *widget, + gpointer data); +static void playstop_callback (GtkWidget *widget, + gpointer data); +static void rewind_callback (GtkWidget *widget, + gpointer data); +static void step_callback (GtkWidget *widget, + gpointer data); + +static DisposeType get_frame_disposal (guint whichframe); +static void render_frame (gint32 whichframe); +static void show_frame (void); + + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + + + + +/* Global widgets'n'stuff */ +GtkWidget* progress; +guint width,height; +gint32 image_id; +gint32 new_image_id; +gint32 total_frames; +guint frame_number; +gint32* layers; +GDrawable* drawable; +gboolean playing = FALSE; +GImageType imagetype; +GDrawableType drawabletype_alpha; +guchar pixelstep; +guchar* palette; +gint ncolours; + + + + +MAIN() + +static void query() +{ + static GParamDef args[] = + { + {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, + {PARAM_IMAGE, "image", "Input image"}, + {PARAM_DRAWABLE, "drawable", "Input drawable (unused)"}, + }; + static GParamDef *return_vals = NULL; + static int nargs = sizeof(args) / sizeof(args[0]); + static int nreturn_vals = 0; + + gimp_install_procedure("plug_in_animationoptimize", + "This plugin applies various optimizations to" + " a GIMP layer-based animation.", + "", + "Adam D. Moss ", + "Adam D. Moss ", + "1997", + "/Filters/Animation/Animation Optimize", + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs, nreturn_vals, + args, return_vals); +} + +static void run(char *name, int n_params, GParam * param, int *nreturn_vals, + GParam ** return_vals) +{ + static GParam values[1]; + GRunModeType run_mode; + GStatusType status = STATUS_SUCCESS; + + *nreturn_vals = 1; + *return_vals = values; + + run_mode = param[0].data.d_int32; + + if (run_mode == RUN_NONINTERACTIVE) + { + if (n_params != 3) + { + status = STATUS_CALLING_ERROR; + } + } + + if (status == STATUS_SUCCESS) + { + image_id = param[1].data.d_image; + + do_optimizations(); + + if (run_mode != RUN_NONINTERACTIVE) + gimp_displays_flush(); + } + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; +} + + + +static int +parse_ms_tag (char *str) +{ + gint sum = 0; + gint offset = 0; + gint length; + + length = strlen(str); + + while ((offset=length) + return(-1); + + if (!isdigit(str[++offset])) + return(-2); + + do + { + sum *= 10; + sum += str[offset] - '0'; + offset++; + } + while ((offsetaction_area), + button, TRUE, TRUE, 0); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + + { + /* The 'playback' half of the dialog */ + + /* windowname = g_malloc(strlen("Playback: ")+strlen(imagename)+1); + strcpy(windowname,"Playback: "); + strcat(windowname,imagename); + frame = gtk_frame_new (windowname); + g_free(windowname); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_border_width (GTK_CONTAINER (frame), 3); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), + frame, TRUE, TRUE, 0); + + { + hbox = gtk_hbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (hbox), 3); + gtk_container_add (GTK_CONTAINER (frame), hbox); + + { + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_border_width (GTK_CONTAINER (vbox), 3); + gtk_container_add (GTK_CONTAINER (hbox), vbox); + + { + progress = gtk_progress_bar_new (); + gtk_widget_set_usize (progress, 150, 15); + gtk_box_pack_start (GTK_BOX (vbox), progress, TRUE, TRUE, 0); + gtk_widget_show (progress); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_container_border_width (GTK_CONTAINER (hbox2), 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox2, TRUE, TRUE, 0); + + { + button = gtk_button_new_with_label ("Play/Stop"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) playstop_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Rewind"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) rewind_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Step"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + (GtkSignalFunc) step_callback, NULL); + gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + } + if (total_frames<=1) gtk_widget_set_sensitive (hbox2, FALSE); + gtk_widget_show(hbox2); + + frame2 = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame2, FALSE, FALSE, 0); + + { + preview = gtk_preview_new (GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (preview), width, height); + gtk_container_add (GTK_CONTAINER (frame2), preview); + gtk_widget_show(preview); + } + gtk_widget_show(frame2); + } + gtk_widget_show(vbox); + + } + gtk_widget_show(hbox); + + } + gtk_widget_show(frame); + */ + } + gtk_widget_show(dlg); +} + + + +/* 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 void +do_optimizations(void) +{ + GPixelRgn 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; + + gboolean can_combine; + + gint32 bbox_top, bbox_bottom, bbox_left, bbox_right; + gint32 rbox_top, rbox_bottom, rbox_left, rbox_right; + + 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 == RGB) ? 4 : 2; + + drawabletype_alpha = (imagetype == RGB) ? RGBA_IMAGE : + ((imagetype == INDEXED) ? INDEXEDA_IMAGE : 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 == 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_numid)==0) + { + window_close_callback(NULL, NULL); + } + + 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 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! */ + + if (gimp_drawable_has_alpha (drawable->id)) + { /* RGB alpha, diff size */ + + destptr = this_frame; + srcptr = rawframe; + + for (j=rawy; j=0 && i=0 && jid)) + { /* 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=0 && i=0 && jrbox_right) rbox_right=xit; + if (yitrbox_bottom) rbox_bottom=yit; + } + if (keep_pix) + { + if (xitbbox_right) bbox_right=xit; + if (yitbbox_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