/* * Animation Playback plug-in version 0.98.8 * * (c) Adam D. Moss : 1997-2000 : adam@gimp.org : adam@foxbox.org * * * This is part of the GIMP package and is released under the GNU * Public License. */ /* * REVISION HISTORY: * * 2000-08-30 : version 0.98.8 * Default frame timing is now 100ms instead of 125ms. * * 2000-06-05 : version 0.98.7 * Fix old bug which could cause errors in evaluating the * final pixel of each composed layer. * * 2000-01-13 : version 0.98.6 * Looser parsing of (XXXXX) layer-name tags * * 98.07.27 : version 0.98.5 * UI tweaks, fix for pseudocolor displays w/gdkrgb. * * 98.07.20 : version 0.98.4 * User interface improvements. * * 98.07.19 : version 0.98.2 * Another speedup for some kinds of shaped animations. * * 98.07.19 : version 0.98.0 * Adapted to use GDKRGB (from recent GTK >= 1.1) if * available - good speed and reliability improvement. * Plus some minor tweaks. * * 98.04.28 : version 0.94.2 * Fixed a time-parsing bug. * * 98.04.05 : version 0.94.0 * Improved performance and removed flicker when shaped. * Shaped mode also works with RGB* images now. * Fixed some longstanding potential visual debris. * * 98.04.04 : version 0.92.0 * Improved responsiveness and performance for the new * shaped-animation mode. Still some flicker. * * 98.04.02 : version 0.90.0 * EXPERIMENTAL wackyness - try dragging the animation * out of the plugin dialog's preview box... * (only works on non-RGB* images for now) * * 98.03.16 : version 0.85.0 * Implemented some more rare opaque/alpha combinations. * * 98.03.15 : version 0.84.0 * Tried to clear up the GTK object/cast warnings. Only * partially successful. Could use some help. * * 97.12.11 : version 0.83.0 * GTK's timer logic changed a little... adjusted * plugin to fit. * * 97.09.16 : version 0.81.7 * Fixed progress bar's off-by-one problem with * the new timing. Fixed erroneous black bars which * were sometimes visible when the first frame was * smaller than the image itself. Made playback * controls inactive when image doesn't have multiple * frames. Moved progress bar above control buttons, * it's less distracting there. More cosmetic stuff. * * 97.09.15 : version 0.81.0 * Now plays INDEXED and GRAY animations. * * 97.09.15 : version 0.75.0 * Next frame is generated ahead of time - results * in more precise timing. * * 97.09.14 : version 0.70.0 * Initial release. RGB only. */ /* * BUGS: * Gets understandably upset if the source image is deleted * while the animation is playing. Decent solution welcome. * * In shaped mode, the shaped-window's mask and its pixmap contents * can get way out of sync (specifically, the mask changes but * the contents are frozen). Starvation of GTK's redrawing thread? * How do I fix this? * * Any more? Let me know! */ /* * TODO: * pdb interface - should we bother? * * speedups (caching? most bottlenecks seem to be in pixelrgns) * -> do pixelrgns properly! * * write other half of the user interface (default timing, disposal &c) */ #include "config.h" #include #include #include #include #include #include #include #include "libgimp/stdplugins-intl.h" #define DITHERTYPE GDK_RGB_DITHER_NORMAL 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 void do_playback (void); static gboolean window_delete_callback (GtkWidget *widget, GdkEvent *event, gpointer data); 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 gboolean repaint_sda (GtkWidget *darea, GdkEventExpose *event, gpointer data); static gboolean repaint_da (GtkWidget *darea, GdkEventExpose *event, gpointer data); static void render_frame (gint32 whichframe); static void show_frame (void); static void total_alpha_preview (guchar* ptr); static void init_preview_misc (void); /* 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 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 guchar *preview_data; static GtkWidget *drawing_area = NULL; static GtkWidget *shape_drawing_area = NULL; static guchar *shape_drawing_area_data = NULL; static guchar *drawing_area_data = NULL; static GtkProgressBar *progress; static guint width, height; static guchar *preview_alpha1_data; static guchar *preview_alpha2_data; static gint32 image_id; static gint32 total_frames; static guint frame_number; static gint32 *layers; static GimpDrawable *drawable; static gboolean playing = FALSE; static guint timer = 0; static GimpImageBaseType imagetype; static guchar *palette; static gint ncolours; static GtkWidget *psbutton; /* for shaping */ static gchar *shape_preview_mask; static GtkWidget *shape_window; typedef struct _cursoroffset {gint x,y;} CursorOffset; static gint shaping = 0; static GdkWindow *root_win = NULL; 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)" } }; gimp_install_procedure ("plug_in_animationplay", "This plugin allows you to preview a GIMP layer-based animation.", "", "Adam D. Moss ", "Adam D. Moss ", "1997, 1998...", N_("/Filters/Animation/Animation Playback..."), "RGB*, INDEXED*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); } static void run (gchar *name, gint n_params, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; *nreturn_vals = 1; *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(); } else { INIT_I18N_UI(); } if (status == GIMP_PDB_SUCCESS) { image_id = param[1].data.d_image; do_playback(); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush(); } values[0].type = GIMP_PDB_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); find_another_bra: while ((offset=length) return(-1); if (!isdigit(str[++offset])) goto find_another_bra; do { sum *= 10; sum += str[offset] - '0'; offset++; } while ((offsetwindow, bitmap, width, height); gtk_widget_shape_combine_mask (shape_window, shape_mask, 0, 0); g_object_unref (shape_mask); if (!prev_bitmap) { prev_bitmap = g_malloc ((width*height)/8 +height); } memcpy (prev_bitmap, bitmap, (width*height)/8 +height); } } static gboolean shape_pressed (GtkWidget *widget, GdkEventButton *event) { CursorOffset *p; /* ignore double and triple click */ if (event->type != GDK_BUTTON_PRESS) return FALSE; p = g_object_get_data (G_OBJECT(widget), "cursor-offset"); if (!p) return FALSE; p->x = (int) event->x; p->y = (int) event->y; gtk_grab_add (widget); gdk_pointer_grab (widget->window, TRUE, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, NULL, 0); gdk_window_raise (widget->window); return FALSE; } static gboolean maybeblocked_expose (GtkWidget *widget, GdkEventExpose *event) { if (playing) return TRUE; return repaint_sda (widget, event, NULL); } static gboolean shape_released (GtkWidget *widget) { gtk_grab_remove (widget); gdk_pointer_ungrab (0); gdk_flush(); return FALSE; } static gboolean shape_motion (GtkWidget *widget, GdkEventMotion *event) { gint xp, yp; CursorOffset * p; GdkModifierType mask; gdk_window_get_pointer (root_win, &xp, &yp, &mask); /* printf("%u %d\n", mask, event->state);fflush(stdout); */ /* if a button is still held by the time we process this event... */ if (mask & (GDK_BUTTON1_MASK| GDK_BUTTON2_MASK| GDK_BUTTON3_MASK| GDK_BUTTON4_MASK| GDK_BUTTON5_MASK)) { p = g_object_get_data (G_OBJECT (widget), "cursor-offset"); if (!p) return FALSE; gtk_window_move (GTK_WINDOW (widget), xp - p->x, yp - p->y); } else /* the user has released all buttons */ { shape_released(widget); } return FALSE; } static gboolean repaint_da (GtkWidget *darea, GdkEventExpose *event, gpointer data) { /* printf("Repaint! Woohoo!\n");*/ gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX:DITHERTYPE, drawing_area_data, width * 3); return TRUE; } static gboolean repaint_sda (GtkWidget *darea, GdkEventExpose *event, gpointer data) { /*printf("Repaint! Woohoo!\n");*/ gdk_draw_rgb_image (shape_drawing_area->window, shape_drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX:DITHERTYPE, shape_drawing_area_data, width * 3); return TRUE; } static gboolean preview_pressed (GtkWidget *widget, GdkEventButton *event) { gint xp, yp; GdkModifierType mask; if (shaping) return FALSE; /* Create a total-alpha buffer merely for the not-shaped drawing area to now display. */ drawing_area_data = g_malloc (width * height * 3); total_alpha_preview (drawing_area_data); gdk_window_get_pointer (root_win, &xp, &yp, &mask); gtk_window_move (GTK_WINDOW (shape_window), xp - event->x, yp - event->y); gtk_widget_show (shape_window); gdk_window_set_back_pixmap(shape_window->window, NULL, 0); gdk_window_set_back_pixmap(shape_drawing_area->window, NULL, 1); show_frame(); shaping = 1; memset(shape_preview_mask, 0, (width*height)/8 + height); render_frame(frame_number); show_frame(); repaint_da (NULL, NULL, NULL); /* mildly amusing hack */ return shape_pressed (shape_window, event); } static void build_dialog (GimpImageBaseType basetype, gchar *imagename) { gchar* windowname; CursorOffset* icon_pos; GtkWidget* dlg; GtkWidget* button; GtkWidget* frame; GtkWidget* frame2; GtkWidget* vbox; GtkWidget* hbox; GtkWidget* hbox2; GtkWidget* eventbox; GdkCursor* cursor; gimp_ui_init ("animationplay", TRUE); windowname = g_strconcat (_("Animation Playback: "), imagename, NULL); dlg = gimp_dialog_new (windowname, "animationplay", gimp_standard_help_func, "filters/animationplay.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CLOSE, window_close_callback, NULL, 1, NULL, TRUE, TRUE, NULL); g_free (windowname); g_signal_connect (dlg, "delete_event", G_CALLBACK (window_delete_callback), NULL); { /* The 'playback' half of the dialog */ windowname = g_malloc (strlen (_("Playback: ")) + strlen (imagename) + 1); if (total_frames > 1) { strcpy (windowname, _("Playback: ")); strcat (windowname, imagename); } else { strcpy (windowname, imagename); } frame = gtk_frame_new (windowname); g_free (windowname); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_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_set_border_width (GTK_CONTAINER (hbox), 3); gtk_container_add (GTK_CONTAINER (frame), hbox); { vbox = gtk_vbox_new (FALSE, 5); gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); gtk_container_add (GTK_CONTAINER (hbox), vbox); { hbox2 = gtk_hbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox2), 0); gtk_box_pack_start (GTK_BOX (vbox), hbox2, TRUE, TRUE, 0); { psbutton = gtk_toggle_button_new_with_label ( _("Play/Stop")); g_signal_connect (psbutton, "toggled", G_CALLBACK (playstop_callback), NULL); gtk_box_pack_start (GTK_BOX (hbox2), psbutton, TRUE, TRUE, 0); gtk_widget_show (psbutton); button = gtk_button_new_with_label ( _("Rewind")); g_signal_connect (button, "clicked", G_CALLBACK (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")); g_signal_connect (button, "clicked", G_CALLBACK (step_callback), NULL); gtk_box_pack_start (GTK_BOX (hbox2), button, TRUE, TRUE, 0); gtk_widget_show (button); } /* If there aren't multiple frames, playback controls make no sense */ /* if (total_frames<=1) gtk_widget_set_sensitive (hbox2, FALSE); gtk_widget_show(hbox2);*/ if (total_frames>1) gtk_widget_show(hbox2); hbox2 = gtk_hbox_new (TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox2), 0); gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 0); { frame2 = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox2), frame2, FALSE, FALSE, 0); { eventbox = gtk_event_box_new(); gtk_container_add (GTK_CONTAINER (frame2), GTK_WIDGET (eventbox)); { drawing_area = gtk_drawing_area_new (); gtk_widget_set_size_request (drawing_area, width, height); gtk_container_add (GTK_CONTAINER (eventbox), GTK_WIDGET (drawing_area)); gtk_widget_show (drawing_area); } gtk_widget_show(eventbox); gtk_widget_set_events (eventbox, gtk_widget_get_events (eventbox) | GDK_BUTTON_PRESS_MASK); } gtk_widget_show(frame2); } gtk_widget_show(hbox2); progress = GTK_PROGRESS_BAR (gtk_progress_bar_new ()); gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (progress), TRUE, TRUE, 0); if (total_frames>1) gtk_widget_show (GTK_WIDGET (progress)); } gtk_widget_show(vbox); } gtk_widget_show(hbox); } gtk_widget_show(frame); } gtk_widget_show(dlg); /* let's get into shape. */ shape_window = gtk_window_new (GTK_WINDOW_POPUP); { shape_drawing_area = gtk_drawing_area_new (); { gtk_widget_set_size_request (shape_drawing_area, width, height); gtk_container_add (GTK_CONTAINER (shape_window), shape_drawing_area); } gtk_widget_show (shape_drawing_area); gtk_widget_set_events (shape_drawing_area, gtk_widget_get_events (shape_drawing_area) | GDK_BUTTON_PRESS_MASK); gtk_widget_realize (shape_window); gdk_window_set_back_pixmap(shape_window->window, NULL, 0); cursor = gdk_cursor_new (GDK_CENTER_PTR); gdk_window_set_cursor(shape_window->window, cursor); gdk_cursor_unref (cursor); g_signal_connect (shape_window, "button_press_event", G_CALLBACK (shape_pressed),NULL); g_signal_connect (shape_window, "button_release_event", G_CALLBACK (shape_released),NULL); g_signal_connect (shape_window, "motion_notify_event", G_CALLBACK (shape_motion),NULL); icon_pos = g_new (CursorOffset, 1); g_object_set_data (G_OBJECT (shape_window), "cursor-offset", icon_pos); } /* gtk_widget_show (shape_window);*/ g_signal_connect (eventbox, "button_press_event", G_CALLBACK (preview_pressed), NULL); g_signal_connect (drawing_area, "expose_event", G_CALLBACK (repaint_da), drawing_area); g_signal_connect (shape_drawing_area, "expose_event", G_CALLBACK (maybeblocked_expose), shape_drawing_area); root_win = gdk_get_default_root_window (); } static void do_playback (void) { int i; 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); if (imagetype == GIMP_INDEXED) palette = gimp_image_get_cmap(image_id, &ncolours); else if (imagetype == GIMP_GRAY) { /* This is a bit sick, until this plugin ever gets real GRAY support (not worth it?) */ palette = g_malloc(768); for (i=0;i<256;i++) { palette[i*3] = palette[i*3+1] = palette[i*3+2] = i; } ncolours = 256; } frame_number = 0; /* cache hint "cache nothing", since we iterate over every tile in every layer. */ gimp_tile_cache_size (0); init_preview_misc(); build_dialog (gimp_image_base_type (image_id), gimp_image_get_name (image_id)); /* Make sure that whole preview is dirtied with pure-alpha */ total_alpha_preview(preview_data); render_frame(0); show_frame(); gtk_main (); gdk_flush (); } /* Rendering Functions */ static void render_frame (gint32 whichframe) { 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,k; /* imaginative loop variables */ DisposeType dispose; if (whichframe >= total_frames) { printf( "playback: Asked for frame number %d in a %d-frame animation!\n", (int) (whichframe+1), (int) total_frames); exit(-1); } drawable = gimp_drawable_get (layers[total_frames-(whichframe+1)]); /* Lame attempt to catch the case that a user has closed the image. */ if (! (drawable->width > 0 && drawable->height > 0)) { gtk_main_quit (); return; } dispose = get_frame_disposal(frame_number); /* 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->drawable_id)==0) window_close_callback (NULL, NULL); if (((dispose==DISPOSE_REPLACE)||(whichframe==0)) && gimp_drawable_has_alpha(drawable->drawable_id)) { total_alpha_preview(preview_data); } /* 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->drawable_id)* gimp_drawable_height(drawable->drawable_id)* gimp_drawable_bpp(drawable->drawable_id)))) { if (rawframe != NULL) g_free(rawframe); rawframe = g_malloc((gimp_drawable_width(drawable->drawable_id)) * (gimp_drawable_height(drawable->drawable_id)) * (gimp_drawable_bpp(drawable->drawable_id))); } rawwidth = gimp_drawable_width(drawable->drawable_id); rawheight = gimp_drawable_height(drawable->drawable_id); rawbpp = gimp_drawable_bpp(drawable->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->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->drawable_id)) { /* alpha */ destptr = preview_data; srcptr = rawframe; i = rawwidth*rawheight; while (i--) { if (!(*(srcptr+3)&128)) { srcptr += 4; destptr += 3; continue; } *(destptr++) = *(srcptr++); *(destptr++) = *(srcptr++); *(destptr++) = *(srcptr++); srcptr++; } /* calculate the shape mask */ if (shaping) { srcptr = rawframe + 3; for (j=0;jwindow, shape_drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, preview_data, width * 3); } else { reshape_from_bitmap (shape_preview_mask); gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, preview_data, width * 3); } } else { /* --- These are suboptimal catch-all cases for when --- */ /* --- this frame is bigger/smaller than the preview --- */ /* --- buffer, and/or offset within it. --- */ if (gimp_drawable_has_alpha (drawable->drawable_id)) { /* alpha */ srcptr = rawframe; for (j=rawy; j=0 && i=0 && j=0 && i=0 && j=0 && i=0 && jwindow, shape_drawing_area->style->white_gc, 0, top, width, bottom-top, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, &preview_data[3*top*width], width * 3); } else { reshape_from_bitmap (shape_preview_mask); gdk_draw_rgb_image (shape_drawing_area->window, shape_drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, preview_data, width * 3); } } else { if ((dispose!=DISPOSE_REPLACE)&&(whichframe!=0)) { int top, bottom; top = (rawy < 0) ? 0 : rawy; bottom = (rawy+rawheight) < height ? (rawy+rawheight) : height-1; gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, top, width, bottom-top, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, &preview_data[3*top*width], width * 3); } else { gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX: DITHERTYPE, preview_data, width * 3); } } } break; 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->drawable_id)) { /* alpha */ destptr = preview_data; srcptr = rawframe; i = rawwidth*rawheight; while (i--) { if (!(*(srcptr+1))) { srcptr += 2; destptr += 3; continue; } *(destptr++) = palette[3*(*(srcptr))]; *(destptr++) = palette[1+3*(*(srcptr))]; *(destptr++) = palette[2+3*(*(srcptr))]; srcptr+=2; } /* calculate the shape mask */ if (shaping) { srcptr = rawframe + 1; for (j=0;jwindow, shape_drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX: DITHERTYPE, preview_data, width * 3); } else { gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX: DITHERTYPE, preview_data, width * 3); } } else { /* --- These are suboptimal catch-all cases for when --- */ /* --- this frame is bigger/smaller than the preview --- */ /* --- buffer, and/or offset within it. --- */ if (gimp_drawable_has_alpha (drawable->drawable_id)) { /* alpha */ srcptr = rawframe; for (j=rawy; j=0 && i=0 && j=0 && i=0 && j=0 && i=0 && jwindow, shape_drawing_area->style->white_gc, 0, top, width, bottom-top, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, &preview_data[3*top*width], width * 3); } else { reshape_from_bitmap (shape_preview_mask); gdk_draw_rgb_image (shape_drawing_area->window, shape_drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, preview_data, width * 3); } } else { if ((dispose!=DISPOSE_REPLACE)&&(whichframe!=0)) { int top, bottom; top = (rawy < 0) ? 0 : rawy; bottom = (rawy+rawheight) < height ? (rawy+rawheight) : height-1; gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, top, width, bottom-top, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, &preview_data[3*top*width], width * 3); } else { gdk_draw_rgb_image (drawing_area->window, drawing_area->style->white_gc, 0, 0, width, height, (total_frames==1)?GDK_RGB_DITHER_MAX :DITHERTYPE, preview_data, width * 3); } } } break; } /* clean up */ gimp_drawable_detach(drawable); } /* If we're using GDKRGB, we don't reshape in this function because it's too late (GDKRGB is synchronous). So this just updates the progress bar. */ static void show_frame (void) { gchar *text; /* update the dialog's progress bar */ gtk_progress_bar_set_fraction (progress, ((float)frame_number / (float)(total_frames-0.999))); text = g_strdup_printf (_("Frame %d of %d"), frame_number, total_frames); gtk_progress_bar_set_text (progress, text); g_free (text); } static void init_preview_misc (void) { int i; preview_data = g_malloc(width*height*3); shape_preview_mask = g_malloc((width*height)/8 + 1 + height); preview_alpha1_data = g_malloc(width*3); preview_alpha2_data = g_malloc(width*3); for (i=0;i=length) || (!isdigit(str[offset]))) return 0; do { sum *= 10; sum += str[offset] - '0'; offset++; } while ((offset