gimp/plug-ins/common/animation-play.c

1547 lines
43 KiB
C
Raw Normal View History

1997-11-25 06:05:25 +08:00
/*
* Animation Playback plug-in version 0.99.1
1997-11-25 06:05:25 +08:00
*
* (c) Adam D. Moss : 1997-2000 : adam@gimp.org : adam@foxbox.org
* (c) Mircea Purdea : 2009 : someone_else@exhalus.net
1997-11-25 06:05:25 +08:00
*
* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1997-11-25 06:05:25 +08:00
*/
/*
* TODO:
* pdb interface - should we bother?
*
1997-11-25 06:05:25 +08:00
* speedups (caching? most bottlenecks seem to be in pixelrgns)
* -> do pixelrgns properly!
*
* write other half of the user interface (zoom, disposal &c)
1997-11-25 06:05:25 +08:00
*/
#include "config.h"
1997-11-25 06:05:25 +08:00
#include <string.h>
app/gimpui.[ch] removed & renamed some functions from gimpui.[ch] (see 2000-01-13 Michael Natterer <mitch@gimp.org> * app/gimpui.[ch] * app/preferences_dialog.c: removed & renamed some functions from gimpui.[ch] (see below). * libgimp/Makefile.am * libgimp/gimpwidgets.[ch]; new files. Functions moved from app/gimpui.[ch]. Added a constructor for the label + hscale + entry combination used in many plugins (now hscale + spinbutton). * libgimp/gimpui.h: include gimpwidgets.h * plug-ins/megawidget/megawidget.[ch]: removed all functions except the preview stuff (I'm not yet sure how to implement this in libgimp because the libgimp preview should be general enough to replace all the other plugin previews, too). * plug-ins/borderaverage/Makefile.am * plug-ins/borderaverage/borderaverage.c * plug-ins/common/plugin-defs.pl * plug-ins/common/Makefile.am * plug-ins/common/aa.c * plug-ins/common/align_layers.c * plug-ins/common/animationplay.c * plug-ins/common/apply_lens.c * plug-ins/common/blinds.c * plug-ins/common/bumpmap.c * plug-ins/common/checkerboard.c * plug-ins/common/colorify.c * plug-ins/common/convmatrix.c * plug-ins/common/cubism.c * plug-ins/common/curve_bend.c * plug-ins/common/deinterlace.c * plug-ins/common/despeckle.c * plug-ins/common/destripe.c * plug-ins/common/displace.c * plug-ins/common/edge.c * plug-ins/common/emboss.c * plug-ins/common/hot.c * plug-ins/common/nlfilt.c * plug-ins/common/pixelize.c * plug-ins/common/waves.c * plug-ins/sgi/sgi.c * plug-ins/sinus/sinus.c: ui updates like removing megawidget, using the dialog constructor, I18N fixes, indentation, ...
2000-01-13 23:39:26 +08:00
#include <libgimp/gimp.h>
#undef GDK_DISABLE_DEPRECATED
app/gimpui.[ch] removed & renamed some functions from gimpui.[ch] (see 2000-01-13 Michael Natterer <mitch@gimp.org> * app/gimpui.[ch] * app/preferences_dialog.c: removed & renamed some functions from gimpui.[ch] (see below). * libgimp/Makefile.am * libgimp/gimpwidgets.[ch]; new files. Functions moved from app/gimpui.[ch]. Added a constructor for the label + hscale + entry combination used in many plugins (now hscale + spinbutton). * libgimp/gimpui.h: include gimpwidgets.h * plug-ins/megawidget/megawidget.[ch]: removed all functions except the preview stuff (I'm not yet sure how to implement this in libgimp because the libgimp preview should be general enough to replace all the other plugin previews, too). * plug-ins/borderaverage/Makefile.am * plug-ins/borderaverage/borderaverage.c * plug-ins/common/plugin-defs.pl * plug-ins/common/Makefile.am * plug-ins/common/aa.c * plug-ins/common/align_layers.c * plug-ins/common/animationplay.c * plug-ins/common/apply_lens.c * plug-ins/common/blinds.c * plug-ins/common/bumpmap.c * plug-ins/common/checkerboard.c * plug-ins/common/colorify.c * plug-ins/common/convmatrix.c * plug-ins/common/cubism.c * plug-ins/common/curve_bend.c * plug-ins/common/deinterlace.c * plug-ins/common/despeckle.c * plug-ins/common/destripe.c * plug-ins/common/displace.c * plug-ins/common/edge.c * plug-ins/common/emboss.c * plug-ins/common/hot.c * plug-ins/common/nlfilt.c * plug-ins/common/pixelize.c * plug-ins/common/waves.c * plug-ins/sgi/sgi.c * plug-ins/sinus/sinus.c: ui updates like removing megawidget, using the dialog constructor, I18N fixes, indentation, ...
2000-01-13 23:39:26 +08:00
#include <libgimp/gimpui.h>
#include "libgimp/stdplugins-intl.h"
1997-11-25 06:05:25 +08:00
#define PLUG_IN_PROC "plug-in-animationplay"
Renamed tons of plug-ins to make more sense and to be consistent: 2008-03-24 Michael Natterer <mitch@gimp.org> Renamed tons of plug-ins to make more sense and to be consistent: * plug-ins/common/AlienMap2.c -> alien-map.c * plug-ins/common/CEL.c -> cel.c * plug-ins/common/CML_explorer.c -> cml-explorer.c * plug-ins/common/align_layers.c -> align-layers.c * plug-ins/common/animationplay.c -> animation-play.c * plug-ins/common/animoptimize.c -> animation-optimize.c * plug-ins/common/apply_lens.c -> lens-apply.c * plug-ins/common/autocrop.c -> crop-auto.c * plug-ins/common/autostretch_hsv.c -> contrast-stretch-hsv.c * plug-ins/common/borderaverage.c -> border-average.c * plug-ins/common/bumpmap.c -> bump-map.c * plug-ins/common/c_astretch.c -> contrast-stretch.c * plug-ins/common/ccanalyze.c -> color-cube-analyze.c * plug-ins/common/channel_mixer.c -> channel-mixer.c * plug-ins/common/color_enhance.c -> color-enhance.c * plug-ins/common/colortoalpha.c -> color-to-alpha.c * plug-ins/common/convmatrix.c -> convolution-matrix.c * plug-ins/common/curve_bend.c -> curve-bend.c * plug-ins/common/depthmerge.c -> depth-merge.c * plug-ins/common/dog.c -> edge-dog.c * plug-ins/common/exchange.c -> color-exchange.c * plug-ins/common/flarefx.c -> lens-flare.c * plug-ins/common/fp.c -> filter-pack.c * plug-ins/common/fractaltrace.c -> fractal-trace.c * plug-ins/common/gauss.c -> blur-gauss.c * plug-ins/common/gee_zoom.c -> gee-zoom.c * plug-ins/common/glasstile.c -> tile-glass.c * plug-ins/common/gqbist.c -> qbist.c * plug-ins/common/gradmap.c -> gradient-map.c * plug-ins/common/laplace.c -> edge-laplace.c * plug-ins/common/lens.c -> lens-distortion.c * plug-ins/common/lic.c -> van-gogh-lic.c * plug-ins/common/max_rgb.c -> max-rgb.c * plug-ins/common/mblur.c -> blur-motion.c * plug-ins/common/nlfilt.c -> nl-filter.c * plug-ins/common/noisify.c -> noise-rgb.c * plug-ins/common/normalize.c -> contrast-normalize.c * plug-ins/common/papertile.c -> tile-paper.c * plug-ins/common/polar.c -> polar-coords.c * plug-ins/common/randomize.c -> noise-randomize.c * plug-ins/common/redeye.c -> red-eye-removal.c * plug-ins/common/retinex.c -> contrast-retinex.c * plug-ins/common/sample_colorize.c -> sample-colorize.c * plug-ins/common/scatter_hsv.c -> noise-hsv.c * plug-ins/common/sel_gauss.c -> blur-gauss-selective.c * plug-ins/common/semiflatten.c -> semi-flatten.c * plug-ins/common/smooth_palette.c -> smooth-palette.c * plug-ins/common/snoise.c -> noise-solid.c * plug-ins/common/sobel.c -> edge-sobel.c * plug-ins/common/spheredesigner.c -> sphere-designer.c * plug-ins/common/spread.c -> noise-spread.c * plug-ins/common/struc.c -> apply-canvas.c * plug-ins/common/threshold_alpha.c -> threshold-alpha.c * plug-ins/common/tileit.c -> tile-small.c * plug-ins/common/tiler.c -> tile-seamless.c * plug-ins/common/uniteditor.c -> unit-editor.c * plug-ins/common/unsharp.c -> unsharp-mask.c * plug-ins/common/vinvert.c -> value-invert.c * plug-ins/common/vpropagate.c -> value-propagate.c * plug-ins/common/webbrowser.c -> web-browser.c * plug-ins/common/whirlpinch.c -> whirl-pinch.c * plug-ins/common/zealouscrop.c -> crop-zealous.c * plug-ins/common/plugin-defs.pl: changed accordingly. * plug-ins/common/Makefile.am: regenerated. svn path=/trunk/; revision=25192
2008-03-24 23:29:55 +08:00
#define PLUG_IN_BINARY "animation-play"
#define PLUG_IN_ROLE "gimp-animation-play"
#define DITHERTYPE GDK_RGB_DITHER_NORMAL
1997-11-25 06:05:25 +08:00
typedef enum
{
DISPOSE_UNDEFINED = 0x00,
DISPOSE_COMBINE = 0x01,
DISPOSE_REPLACE = 0x02
} DisposeType;
/* Declare local functions. */
static void query (void);
static void run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static void do_playback (void);
static void window_destroy (GtkWidget *widget);
static void play_callback (GtkToggleAction *action);
static void step_callback (GtkAction *action);
static void rewind_callback (GtkAction *action);
static void speed_up_callback (GtkAction *action);
static void speed_down_callback (GtkAction *action);
static void speed_reset_callback (GtkAction *action);
static void speedcombo_changed (GtkWidget *combo,
gpointer data);
static void fpscombo_changed (GtkWidget *combo,
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 (void);
static void update_combobox (void);
static gdouble get_duration_factor (gint index);
static gint get_fps (gint index);
1997-11-25 06:05:25 +08:00
/* tag util functions*/
static gint parse_ms_tag (const gchar *str);
static DisposeType parse_disposal_tag (const gchar *str);
static DisposeType get_frame_disposal (guint whichframe);
static guint32 get_frame_duration (guint whichframe);
static gboolean is_disposal_tag (const gchar *str,
DisposeType *disposal,
gint *taglength);
static gboolean is_ms_tag (const gchar *str,
gint *duration,
gint *taglength);
const GimpPlugInInfo PLUG_IN_INFO =
1997-11-25 06:05:25 +08:00
{
NULL, /* init_proc */
NULL, /* quit_proc */
1997-11-25 06:05:25 +08:00
query, /* query_proc */
run, /* run_proc */
1997-11-25 06:05:25 +08:00
};
/* Global widgets'n'stuff */
static GtkWidget *window = NULL;
static GtkUIManager *ui_manager = NULL;
static guchar *preview_data = NULL;
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 GtkWidget *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 gboolean playing = FALSE;
static guint timer = 0;
static GimpImageBaseType imagetype;
static guchar *palette;
static gint ncolours;
static gint duration_index = 3;
static gint default_frame_duration = 100; /* ms */
1997-11-25 06:05:25 +08:00
/* for shaping */
Changed plug-in menu registration again to allow passing just the menu 2004-05-07 Michael Natterer <mitch@gimp.org> Changed plug-in menu registration again to allow passing just the menu item's label (not the full path) in gimp_install_procedure() and only the path (excluding the item's label) in gimp_plugin_menu_register(). Matches the internal action system better and makes translating the menu paths much easier. (Of yourse it's still possible to use the old syntax for backward compatibility). * app/plug-in/plug-in-proc.[ch]: added "gchar *menu_label". * app/plug-in/plug-in-params.[ch]: added new functions plug_in_param_defs_check() and plug_in_proc_args_check() which check if a procedure's parameters match its menu location (e.g. <Image> needs RUN-MODE, IMAGE, DRAWABLE). * app/plug-in/plug-in-message.c (plug_in_handle_proc_install): if registering an old-style (full) menu_path, use plug_in_param_defs_check(), set proc_def->menu_label otherwise. * tools/pdbgen/pdb/plug_in.pdb (plugin_menu_register): use plug_in_proc_args_check() on the passed menu_path and make sugre old and new style menu registration are not mixed. * app/pdb/plug_in_cmds.c: regenerated. * app/plug-in/plug-in-rc.c: save/restore "menu_label". * app/actions/file-dialog-actions.c * app/actions/plug-in-actions.c * app/menus/plug-in-menus.c: changed action/menu creation accordingly. Some hacks needed to allow both old and new style menu_label/menu_paths. * app/plug-in/plug-in.c * app/widgets/gimpfiledialog.c * app/xcf/xcf.c: changed accordingly. * plug-ins/common/align_layers.c * plug-ins/common/animationplay.c * plug-ins/common/animoptimize.c * plug-ins/common/apply_lens.c * plug-ins/common/autocrop.c * plug-ins/common/autostretch_hsv.c * plug-ins/common/blinds.c * plug-ins/common/blur.c * plug-ins/common/borderaverage.c * plug-ins/common/bumpmap.c * plug-ins/common/c_astretch.c * plug-ins/common/ccanalyze.c * plug-ins/common/channel_mixer.c * plug-ins/common/checkerboard.c * plug-ins/common/color_enhance.c * plug-ins/common/colorify.c * plug-ins/common/colortoalpha.c * plug-ins/common/compose.c * plug-ins/common/convmatrix.c * plug-ins/common/cubism.c * plug-ins/common/curve_bend.c * plug-ins/common/decompose.c * plug-ins/common/deinterlace.c * plug-ins/common/depthmerge.c * plug-ins/common/destripe.c * plug-ins/common/diffraction.c * plug-ins/common/displace.c * plug-ins/common/edge.c * plug-ins/common/emboss.c * plug-ins/common/engrave.c * plug-ins/common/exchange.c * plug-ins/common/film.c * plug-ins/common/flarefx.c * plug-ins/common/fractaltrace.c * plug-ins/common/screenshot.c: ported the first few plug-ins to the new registration scheme.
2004-05-07 08:30:24 +08:00
typedef struct
{
gint x, y;
} CursorOffset;
static gchar *shape_preview_mask = NULL;
static GtkWidget *shape_window = NULL;
static GdkWindow *root_win = NULL;
static gboolean detached = FALSE;
static GtkWidget *speedcombo = NULL;
MAIN ()
1997-11-25 06:05:25 +08:00
static void
query (void)
1997-11-25 06:05:25 +08:00
{
static const GimpParamDef args[] =
1997-11-25 06:05:25 +08:00
{
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
1997-11-25 06:05:25 +08:00
};
gimp_install_procedure (PLUG_IN_PROC,
N_("Preview a GIMP layer-based animation"),
"",
"Adam D. Moss <adam@gimp.org>",
"Adam D. Moss <adam@gimp.org>",
"1997, 1998...",
Changed plug-in menu registration again to allow passing just the menu 2004-05-07 Michael Natterer <mitch@gimp.org> Changed plug-in menu registration again to allow passing just the menu item's label (not the full path) in gimp_install_procedure() and only the path (excluding the item's label) in gimp_plugin_menu_register(). Matches the internal action system better and makes translating the menu paths much easier. (Of yourse it's still possible to use the old syntax for backward compatibility). * app/plug-in/plug-in-proc.[ch]: added "gchar *menu_label". * app/plug-in/plug-in-params.[ch]: added new functions plug_in_param_defs_check() and plug_in_proc_args_check() which check if a procedure's parameters match its menu location (e.g. <Image> needs RUN-MODE, IMAGE, DRAWABLE). * app/plug-in/plug-in-message.c (plug_in_handle_proc_install): if registering an old-style (full) menu_path, use plug_in_param_defs_check(), set proc_def->menu_label otherwise. * tools/pdbgen/pdb/plug_in.pdb (plugin_menu_register): use plug_in_proc_args_check() on the passed menu_path and make sugre old and new style menu registration are not mixed. * app/pdb/plug_in_cmds.c: regenerated. * app/plug-in/plug-in-rc.c: save/restore "menu_label". * app/actions/file-dialog-actions.c * app/actions/plug-in-actions.c * app/menus/plug-in-menus.c: changed action/menu creation accordingly. Some hacks needed to allow both old and new style menu_label/menu_paths. * app/plug-in/plug-in.c * app/widgets/gimpfiledialog.c * app/xcf/xcf.c: changed accordingly. * plug-ins/common/align_layers.c * plug-ins/common/animationplay.c * plug-ins/common/animoptimize.c * plug-ins/common/apply_lens.c * plug-ins/common/autocrop.c * plug-ins/common/autostretch_hsv.c * plug-ins/common/blinds.c * plug-ins/common/blur.c * plug-ins/common/borderaverage.c * plug-ins/common/bumpmap.c * plug-ins/common/c_astretch.c * plug-ins/common/ccanalyze.c * plug-ins/common/channel_mixer.c * plug-ins/common/checkerboard.c * plug-ins/common/color_enhance.c * plug-ins/common/colorify.c * plug-ins/common/colortoalpha.c * plug-ins/common/compose.c * plug-ins/common/convmatrix.c * plug-ins/common/cubism.c * plug-ins/common/curve_bend.c * plug-ins/common/decompose.c * plug-ins/common/deinterlace.c * plug-ins/common/depthmerge.c * plug-ins/common/destripe.c * plug-ins/common/diffraction.c * plug-ins/common/displace.c * plug-ins/common/edge.c * plug-ins/common/emboss.c * plug-ins/common/engrave.c * plug-ins/common/exchange.c * plug-ins/common/film.c * plug-ins/common/flarefx.c * plug-ins/common/fractaltrace.c * plug-ins/common/screenshot.c: ported the first few plug-ins to the new registration scheme.
2004-05-07 08:30:24 +08:00
N_("_Playback..."),
"RGB*, INDEXED*, GRAY*",
GIMP_PLUGIN,
G_N_ELEMENTS (args), 0,
args, NULL);
Changed plug-in menu registration again to allow passing just the menu 2004-05-07 Michael Natterer <mitch@gimp.org> Changed plug-in menu registration again to allow passing just the menu item's label (not the full path) in gimp_install_procedure() and only the path (excluding the item's label) in gimp_plugin_menu_register(). Matches the internal action system better and makes translating the menu paths much easier. (Of yourse it's still possible to use the old syntax for backward compatibility). * app/plug-in/plug-in-proc.[ch]: added "gchar *menu_label". * app/plug-in/plug-in-params.[ch]: added new functions plug_in_param_defs_check() and plug_in_proc_args_check() which check if a procedure's parameters match its menu location (e.g. <Image> needs RUN-MODE, IMAGE, DRAWABLE). * app/plug-in/plug-in-message.c (plug_in_handle_proc_install): if registering an old-style (full) menu_path, use plug_in_param_defs_check(), set proc_def->menu_label otherwise. * tools/pdbgen/pdb/plug_in.pdb (plugin_menu_register): use plug_in_proc_args_check() on the passed menu_path and make sugre old and new style menu registration are not mixed. * app/pdb/plug_in_cmds.c: regenerated. * app/plug-in/plug-in-rc.c: save/restore "menu_label". * app/actions/file-dialog-actions.c * app/actions/plug-in-actions.c * app/menus/plug-in-menus.c: changed action/menu creation accordingly. Some hacks needed to allow both old and new style menu_label/menu_paths. * app/plug-in/plug-in.c * app/widgets/gimpfiledialog.c * app/xcf/xcf.c: changed accordingly. * plug-ins/common/align_layers.c * plug-ins/common/animationplay.c * plug-ins/common/animoptimize.c * plug-ins/common/apply_lens.c * plug-ins/common/autocrop.c * plug-ins/common/autostretch_hsv.c * plug-ins/common/blinds.c * plug-ins/common/blur.c * plug-ins/common/borderaverage.c * plug-ins/common/bumpmap.c * plug-ins/common/c_astretch.c * plug-ins/common/ccanalyze.c * plug-ins/common/channel_mixer.c * plug-ins/common/checkerboard.c * plug-ins/common/color_enhance.c * plug-ins/common/colorify.c * plug-ins/common/colortoalpha.c * plug-ins/common/compose.c * plug-ins/common/convmatrix.c * plug-ins/common/cubism.c * plug-ins/common/curve_bend.c * plug-ins/common/decompose.c * plug-ins/common/deinterlace.c * plug-ins/common/depthmerge.c * plug-ins/common/destripe.c * plug-ins/common/diffraction.c * plug-ins/common/displace.c * plug-ins/common/edge.c * plug-ins/common/emboss.c * plug-ins/common/engrave.c * plug-ins/common/exchange.c * plug-ins/common/film.c * plug-ins/common/flarefx.c * plug-ins/common/fractaltrace.c * plug-ins/common/screenshot.c: ported the first few plug-ins to the new registration scheme.
2004-05-07 08:30:24 +08:00
gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Animation");
gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_STOCK_ID,
(const guint8 *) GTK_STOCK_MEDIA_PLAY);
1997-11-25 06:05:25 +08:00
}
static void
run (const gchar *name,
gint n_params,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
1997-11-25 06:05:25 +08:00
{
static GimpParam values[1];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
1997-11-25 06:05:25 +08:00
*nreturn_vals = 1;
*return_vals = values;
1997-11-25 06:05:25 +08:00
run_mode = param[0].data.d_int32;
INIT_I18N ();
2012-09-22 06:46:05 +08:00
gegl_init (NULL, NULL);
if (run_mode == GIMP_RUN_NONINTERACTIVE && n_params != 3)
{
status = GIMP_PDB_CALLING_ERROR;
}
1997-11-25 06:05:25 +08:00
if (status == GIMP_PDB_SUCCESS)
{
image_id = param[1].data.d_image;
do_playback ();
if (run_mode != GIMP_RUN_NONINTERACTIVE)
gimp_displays_flush ();
}
1997-11-25 06:05:25 +08:00
values[0].type = GIMP_PDB_STATUS;
1997-11-25 06:05:25 +08:00
values[0].data.d_status = status;
}
static void
reshape_from_bitmap (const gchar *bitmap)
{
static gchar *prev_bitmap = NULL;
if ((!prev_bitmap) ||
(memcmp (prev_bitmap, bitmap, (width * height) / 8 + height)))
{
GdkBitmap *shape_mask;
shape_mask = gdk_bitmap_create_from_data (gtk_widget_get_window (shape_window),
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
popup_menu (GtkWidget *widget,
GdkEventButton *event)
{
GtkWidget *menu = gtk_ui_manager_get_widget (ui_manager, "/anim-play-popup");
gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
gtk_menu_popup (GTK_MENU (menu),
NULL, NULL, NULL, NULL,
event ? event->button : 0,
event ? event->time : gtk_get_current_event_time ());
return TRUE;
}
static gboolean
button_press (GtkWidget *widget,
GdkEventButton *event)
{
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
return popup_menu (widget, event);
return FALSE;
}
static gboolean
shape_pressed (GtkWidget *widget,
GdkEventButton *event)
{
if (button_press (widget, event))
return TRUE;
/* ignore double and triple click */
if (event->type == GDK_BUTTON_PRESS)
{
CursorOffset *p = g_object_get_data (G_OBJECT(widget), "cursor-offset");
if (!p)
return FALSE;
p->x = (gint) event->x;
p->y = (gint) event->y;
gtk_grab_add (widget);
gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK,
NULL, NULL, 0);
gdk_window_raise (gtk_widget_get_window (widget));
}
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_display_pointer_ungrab (gtk_widget_get_display (widget), 0);
gdk_flush ();
return FALSE;
}
static gboolean
shape_motion (GtkWidget *widget,
GdkEventMotion *event)
{
GdkModifierType mask;
gint xp, yp;
gdk_window_get_pointer (root_win, &xp, &yp, &mask);
/* if a button is still held by the time we process this event... */
if (mask & GDK_BUTTON1_MASK)
{
CursorOffset *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)
{
GtkStyle *style = gtk_widget_get_style (darea);
gdk_draw_rgb_image (gtk_widget_get_window (darea),
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)
{
GtkStyle *style = gtk_widget_get_style (darea);
gdk_draw_rgb_image (gtk_widget_get_window (darea),
style->white_gc,
0, 0, width, height,
(total_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
shape_drawing_area_data, width * 3);
return TRUE;
}
static void
close_callback (GtkAction *action,
gpointer data)
{
gtk_widget_destroy (GTK_WIDGET (data));
}
static void
help_callback (GtkAction *action,
gpointer data)
{
gimp_standard_help_func (PLUG_IN_PROC, data);
}
static void
detach_callback (GtkToggleAction *action)
{
gboolean active = gtk_toggle_action_get_active (action);
if (active == detached)
{
g_warning ("detached state and toggle action got out of sync");
return;
}
detached = active;
if (detached)
{
/* 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);
gtk_window_set_screen (GTK_WINDOW (shape_window),
gtk_widget_get_screen (drawing_area));
if (gtk_widget_get_realized (drawing_area))
{
gint x, y;
gdk_window_get_origin (gtk_widget_get_window (drawing_area), &x, &y);
gtk_window_move (GTK_WINDOW (shape_window), x + 6, y + 6);
}
gtk_widget_show (shape_window);
gdk_window_set_back_pixmap (gtk_widget_get_window (shape_drawing_area), NULL, TRUE);
memset (shape_preview_mask, 0, (width * height) / 8 + height);
render_frame (frame_number);
}
else
{
g_free (drawing_area_data);
drawing_area_data = shape_drawing_area_data;
render_frame (frame_number);
gtk_widget_hide (shape_window);
}
gtk_widget_queue_draw (drawing_area);
}
static GtkUIManager *
ui_manager_new (GtkWidget *window)
{
static GtkActionEntry actions[] =
{
{ "step", GTK_STOCK_MEDIA_NEXT,
N_("_Step"), NULL, N_("Step to next frame"),
G_CALLBACK (step_callback) },
{ "rewind", GTK_STOCK_MEDIA_REWIND,
NULL, NULL, N_("Rewind the animation"),
G_CALLBACK (rewind_callback) },
{ "help", GTK_STOCK_HELP,
NULL, NULL, NULL,
G_CALLBACK (help_callback) },
{ "close", GTK_STOCK_CLOSE,
NULL, "<control>W", NULL,
G_CALLBACK (close_callback)
},
{
"quit", GTK_STOCK_QUIT,
NULL, "<control>Q", NULL,
G_CALLBACK (close_callback)
},
{
"speed-up", NULL,
N_("Faster"), "<control>L", N_("Increase the speed of the animation"),
G_CALLBACK (speed_up_callback)
},
{
"speed-down", NULL,
N_("Slower"), "<control>J", N_("Decrease the speed of the animation"),
G_CALLBACK (speed_down_callback)
},
{
"speed-reset", NULL,
N_("Reset speed"), "<control>K", N_("Reset the speed of the animation"),
G_CALLBACK (speed_reset_callback)
}
};
static GtkToggleActionEntry toggle_actions[] =
{
2006-02-26 07:18:59 +08:00
{ "play", GTK_STOCK_MEDIA_PLAY,
NULL, NULL, N_("Start playback"),
2006-02-26 07:18:59 +08:00
G_CALLBACK (play_callback), FALSE },
{ "detach", GIMP_STOCK_DETACH,
N_("Detach"), NULL,
N_("Detach the animation from the dialog window"),
G_CALLBACK (detach_callback), FALSE }
};
GtkUIManager *ui_manager = gtk_ui_manager_new ();
GtkActionGroup *group = gtk_action_group_new ("Actions");
GError *error = NULL;
gtk_action_group_set_translation_domain (group, NULL);
gtk_action_group_add_actions (group,
actions,
G_N_ELEMENTS (actions),
window);
gtk_action_group_add_toggle_actions (group,
toggle_actions,
G_N_ELEMENTS (toggle_actions),
NULL);
gtk_window_add_accel_group (GTK_WINDOW (window),
gtk_ui_manager_get_accel_group (ui_manager));
gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
gtk_ui_manager_insert_action_group (ui_manager, group, -1);
g_object_unref (group);
gtk_ui_manager_add_ui_from_string (ui_manager,
"<ui>"
" <toolbar name=\"anim-play-toolbar\">"
" <toolitem action=\"play\" />"
" <toolitem action=\"step\" />"
" <toolitem action=\"rewind\" />"
" <separator />"
" <toolitem action=\"detach\" />"
" <separator name=\"space\" />"
" <toolitem action=\"help\" />"
" </toolbar>"
" <accelerator action=\"close\" />"
" <accelerator action=\"quit\" />"
"</ui>",
-1, &error);
if (error)
{
g_warning ("error parsing ui: %s", error->message);
g_clear_error (&error);
}
gtk_ui_manager_add_ui_from_string (ui_manager,
"<ui>"
" <popup name=\"anim-play-popup\">"
" <menuitem action=\"play\" />"
" <menuitem action=\"step\" />"
" <menuitem action=\"rewind\" />"
" <separator />"
" <menuitem action=\"speed-down\" />"
" <menuitem action=\"speed-up\" />"
" <menuitem action=\"speed-reset\" />"
" <separator />"
" <menuitem action=\"detach\" />"
" <menuitem action=\"close\" />"
" </popup>"
"</ui>",
-1, &error);
if (error)
{
g_warning ("error parsing ui: %s", error->message);
g_clear_error (&error);
}
return ui_manager;
}
1997-11-25 06:05:25 +08:00
static void
build_dialog (GimpImageBaseType basetype,
gchar *imagename)
1997-11-25 06:05:25 +08:00
{
GtkWidget *toolbar;
GtkWidget *frame;
GtkWidget *main_vbox;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *abox;
GtkToolItem *item;
GtkAction *action;
GdkCursor *cursor;
gchar *name;
gint index;
1997-11-25 06:05:25 +08:00
gimp_ui_init (PLUG_IN_BINARY, TRUE);
1997-11-25 06:05:25 +08:00
name = g_strconcat (_("Animation Playback:"), " ", imagename, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), name);
gtk_window_set_role (GTK_WINDOW (window), "animation-playback");
g_free (name);
g_signal_connect (window, "destroy",
G_CALLBACK (window_destroy),
NULL);
g_signal_connect (window, "popup-menu",
G_CALLBACK (popup_menu),
NULL);
gimp_help_connect (window, gimp_standard_help_func, PLUG_IN_PROC, NULL);
ui_manager = ui_manager_new (window);
2011-09-30 18:17:53 +08:00
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (window), main_vbox);
gtk_widget_show (main_vbox);
toolbar = gtk_ui_manager_get_widget (ui_manager, "/anim-play-toolbar");
gtk_box_pack_start (GTK_BOX (main_vbox), toolbar, FALSE, FALSE, 0);
gtk_widget_show (toolbar);
item =
GTK_TOOL_ITEM (gtk_ui_manager_get_widget (ui_manager,
"/anim-play-toolbar/space"));
gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
gtk_tool_item_set_expand (item, TRUE);
2011-09-30 18:17:53 +08:00
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
gtk_box_pack_start (GTK_BOX (vbox), abox, TRUE, TRUE, 0);
gtk_widget_show (abox);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (abox), frame);
gtk_widget_show (frame);
drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (drawing_area, width, height);
gtk_widget_add_events (drawing_area, GDK_BUTTON_PRESS_MASK);
gtk_container_add (GTK_CONTAINER (frame), drawing_area);
gtk_widget_show (drawing_area);
g_signal_connect (drawing_area, "button-press-event",
G_CALLBACK (button_press),
NULL);
2011-09-30 18:17:53 +08:00
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
progress = gtk_progress_bar_new ();
gtk_box_pack_end (GTK_BOX (hbox), progress, TRUE, TRUE, 0);
gtk_widget_show (progress);
speedcombo = gtk_combo_box_text_new ();
gtk_box_pack_end (GTK_BOX (hbox), speedcombo, FALSE, FALSE, 0);
gtk_widget_show (speedcombo);
for (index = 0; index < 9; index++)
{
gchar *text;
/* list is given in "fps" - frames per second */
text = g_strdup_printf (_("%d fps"), get_fps (index));
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (speedcombo), text);
g_free (text);
}
gtk_combo_box_set_active (GTK_COMBO_BOX (speedcombo), 0);
g_signal_connect (speedcombo, "changed",
G_CALLBACK (fpscombo_changed),
NULL);
gimp_help_set_help_data (speedcombo, _("Default framerate"), NULL);
speedcombo = gtk_combo_box_text_new ();
gtk_box_pack_end (GTK_BOX (hbox), speedcombo, FALSE, FALSE, 0);
gtk_widget_show (speedcombo);
for (index = 0; index < 7; index++)
{
gchar *text;
text = g_strdup_printf ("%g\303\227", (100 / get_duration_factor (index)) / 100);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (speedcombo), text);
g_free (text);
}
gtk_combo_box_set_active (GTK_COMBO_BOX (speedcombo), 3);
g_signal_connect (speedcombo, "changed",
G_CALLBACK (speedcombo_changed),
NULL);
gimp_help_set_help_data (speedcombo, _("Playback speed"), NULL);
if (total_frames < 2)
{
action = gtk_ui_manager_get_action (ui_manager,
"/ui/anim-play-toolbar/play");
gtk_action_set_sensitive (action, FALSE);
action = gtk_ui_manager_get_action (ui_manager,
"/ui/anim-play-toolbar/step");
gtk_action_set_sensitive (action, FALSE);
action = gtk_ui_manager_get_action (ui_manager,
"/ui/anim-play-toolbar/rewind");
gtk_action_set_sensitive (action, FALSE);
}
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-reset");
gtk_action_set_sensitive (action, FALSE);
gtk_widget_show (window);
/* 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_add_events (shape_drawing_area, GDK_BUTTON_PRESS_MASK);
gtk_widget_realize (shape_window);
gdk_window_set_back_pixmap (gtk_widget_get_window (shape_window), NULL, FALSE);
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (shape_window),
GDK_HAND2);
gdk_window_set_cursor (gtk_widget_get_window (shape_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);
g_object_set_data (G_OBJECT (shape_window),
"cursor-offset", g_new0 (CursorOffset, 1));
g_signal_connect (drawing_area, "expose-event",
G_CALLBACK (repaint_da),
NULL);
g_signal_connect (shape_drawing_area, "expose-event",
G_CALLBACK (maybeblocked_expose),
NULL);
root_win = gdk_get_default_root_window ();
1997-11-25 06:05:25 +08:00
}
static void
do_playback (void)
1997-11-25 06:05:25 +08:00
{
width = gimp_image_width (image_id);
height = gimp_image_height (image_id);
1997-11-25 06:05:25 +08:00
layers = gimp_image_get_layers (image_id, &total_frames);
imagetype = gimp_image_base_type (image_id);
1997-11-25 06:05:25 +08:00
if (imagetype == GIMP_INDEXED)
{
palette = gimp_image_get_colormap (image_id, &ncolours);
}
else if (imagetype == GIMP_GRAY)
1997-11-25 06:05:25 +08:00
{
gint i;
palette = g_new (guchar, 768);
for (i = 0; i < 256; i++)
palette[i * 3] = palette[i * 3 + 1] = palette[i * 3 + 2] = i;
1997-11-25 06:05:25 +08:00
ncolours = 256;
}
frame_number = 0;
init_preview ();
build_dialog (gimp_image_base_type (image_id),
gimp_image_get_name (image_id));
1997-11-25 06:05:25 +08:00
/* Make sure that whole preview is dirtied with pure-alpha */
total_alpha_preview (preview_data);
render_frame (0);
show_frame ();
1997-11-25 06:05:25 +08:00
gtk_main ();
}
/* Rendering Functions */
static void
render_frame (gint32 whichframe)
1997-11-25 06:05:25 +08:00
{
GtkStyle *shape_style = gtk_widget_get_style (shape_drawing_area);
GtkStyle *drawing_style = gtk_widget_get_style (drawing_area);
2012-09-22 06:46:05 +08:00
GeglBuffer *buffer;
gint drawable_id;
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
if (whichframe >= total_frames)
{
printf( "playback: Asked for frame number %d in a %d-frame animation!\n",
(int) (whichframe+1), (int) total_frames);
gimp_quit ();
1997-11-25 06:05:25 +08:00
}
2012-09-22 06:46:05 +08:00
drawable_id = layers[total_frames - (whichframe + 1)];
buffer = gimp_drawable_get_buffer (drawable_id);
/* Lame attempt to catch the case that a user has closed the image. */
2012-09-22 06:46:05 +08:00
if (!buffer)
{
gimp_message (_("Tried to display an invalid layer."));
gtk_main_quit ();
return;
}
1997-11-25 06:05:25 +08:00
dispose = get_frame_disposal (frame_number);
1997-11-25 06:05:25 +08:00
if (((dispose == DISPOSE_REPLACE) || (whichframe == 0)) &&
2012-09-22 06:46:05 +08:00
gimp_drawable_has_alpha (drawable_id))
1997-11-25 06:05:25 +08:00
{
total_alpha_preview (preview_data);
1997-11-25 06:05:25 +08:00
}
/* only get a new 'raw' drawable-data buffer if this and
the previous raw buffer were different sizes */
1997-11-25 06:05:25 +08:00
if ((rawwidth * rawheight * rawbpp) !=
2012-09-22 06:46:05 +08:00
((gimp_drawable_width (drawable_id) *
gimp_drawable_height (drawable_id) *
gimp_drawable_has_alpha (drawable_id) ? 4 : 3)))
1997-11-25 06:05:25 +08:00
{
if (rawframe != NULL)
g_free (rawframe);
2012-09-22 06:46:05 +08:00
rawwidth = gimp_drawable_width (drawable_id);
rawheight = gimp_drawable_height (drawable_id);
rawbpp = gimp_drawable_has_alpha (drawable_id) ? 4 : 3;
1997-11-25 06:05:25 +08:00
2012-09-22 06:46:05 +08:00
rawframe = g_malloc (rawwidth * rawheight * rawbpp);
}
1997-11-25 06:05:25 +08:00
/* Initialise and fetch the whole raw new frame */
2012-09-22 06:46:05 +08:00
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, rawwidth, rawheight), 1.0,
gimp_drawable_has_alpha (drawable_id) ?
babl_format ("R'G'B'A u8") :
babl_format ("R'G'B' u8"),
rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
1997-11-25 06:05:25 +08:00
2012-09-22 06:46:05 +08:00
gimp_drawable_offsets (drawable_id, &rawx, &rawy);
1997-11-25 06:05:25 +08:00
/* render... */
2012-09-22 06:46:05 +08:00
if ((rawwidth == width) &&
(rawheight == height) &&
(rawx == 0) &&
(rawy == 0))
1997-11-25 06:05:25 +08:00
{
2012-09-22 06:46:05 +08:00
/* --- 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))
{
2012-09-22 06:46:05 +08:00
destptr = preview_data;
srcptr = rawframe;
2012-09-22 06:46:05 +08:00
i = rawwidth * rawheight;
while (i--)
{
2012-09-22 06:46:05 +08:00
if (! (srcptr[3] & 128))
{
2012-09-22 06:46:05 +08:00
srcptr += 4;
destptr += 3;
continue;
}
2012-09-22 06:46:05 +08:00
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
*(destptr++) = *(srcptr++);
2012-09-22 06:46:05 +08:00
srcptr++;
}
/* calculate the shape mask */
if (detached)
{
srcptr = rawframe + 3;
2012-09-22 06:46:05 +08:00
for (j = 0; j < rawheight; j++)
{
2012-09-22 06:46:05 +08:00
k = j * ((7 + rawwidth) / 8);
2012-09-22 06:46:05 +08:00
for (i = 0; i < rawwidth; i++)
{
2012-09-22 06:46:05 +08:00
if ((*srcptr) & 128)
shape_preview_mask[k + i/8] |= (1 << (i&7));
2012-09-22 06:46:05 +08:00
srcptr += 4;
}
}
}
2012-09-22 06:46:05 +08:00
}
else /* no alpha */
{
if ((rawwidth == width) && (rawheight == height))
memcpy (preview_data, rawframe, width * height * 3);
if (detached)
{
2012-09-22 06:46:05 +08:00
/* opacify the shape mask */
memset (shape_preview_mask, 255,
(rawwidth * rawheight) / 8 + rawheight);
}
}
2012-09-22 06:46:05 +08:00
/* Display the preview buffer... finally. */
if (detached)
{
reshape_from_bitmap (shape_preview_mask);
gdk_draw_rgb_image (gtk_widget_get_window (shape_drawing_area),
shape_style->white_gc,
0, 0, width, height,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
preview_data, width * 3);
}
1997-11-25 06:05:25 +08:00
else
{
2012-09-22 06:46:05 +08:00
reshape_from_bitmap (shape_preview_mask);
gdk_draw_rgb_image (gtk_widget_get_window (drawing_area),
drawing_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. --- */
2012-09-22 06:46:05 +08:00
if (gimp_drawable_has_alpha (drawable_id))
{
srcptr = rawframe;
2012-09-22 06:46:05 +08:00
for (j = rawy; j < rawheight + rawy; j++)
{
2012-09-22 06:46:05 +08:00
for (i = rawx; i < rawwidth + rawx; i++)
{
2012-09-22 06:46:05 +08:00
if ((i >= 0 && i < width) &&
(j >= 0 && j < height))
{
2012-09-22 06:46:05 +08:00
if (srcptr[3] & 128)
{
preview_data[(j * width + i) * 3 ] = *(srcptr);
preview_data[(j * width + i) * 3 + 1] = *(srcptr + 1);
preview_data[(j * width + i) * 3 + 2] = *(srcptr + 2);
}
}
2012-09-22 06:46:05 +08:00
srcptr += 4;
}
}
2012-09-22 06:46:05 +08:00
if (detached)
{
2012-09-22 06:46:05 +08:00
srcptr = rawframe + 3;
2012-09-22 06:46:05 +08:00
for (j = rawy; j < rawheight + rawy; j++)
{
2012-09-22 06:46:05 +08:00
k = j * ((width + 7) / 8);
2012-09-22 06:46:05 +08:00
for (i = rawx; i < rawwidth + rawx; i++)
{
2012-09-22 06:46:05 +08:00
if ((i>=0 && i<width) &&
(j>=0 && j<height))
{
2012-09-22 06:46:05 +08:00
if ((*srcptr) & 128)
shape_preview_mask[k + i/8] |= (1 << (i&7));
}
2012-09-22 06:46:05 +08:00
srcptr += 4;
}
}
}
2012-09-22 06:46:05 +08:00
}
else
{
/* noalpha */
2012-09-22 06:46:05 +08:00
srcptr = rawframe;
2012-09-22 06:46:05 +08:00
for (j = rawy; j < rawheight + rawy; j++)
{
for (i = rawx; i < rawwidth + rawx; i++)
{
2012-09-22 06:46:05 +08:00
if ((i >= 0 && i < width) &&
(j >= 0 && j < height))
{
preview_data[(j * width + i) * 3 ] = *(srcptr);
preview_data[(j * width + i) * 3 + 1] = *(srcptr + 1);
preview_data[(j * width + i) * 3 + 2] = *(srcptr + 2);
}
2012-09-22 06:46:05 +08:00
srcptr += 3;
}
}
2012-09-22 06:46:05 +08:00
}
2012-09-22 06:46:05 +08:00
/* Display the preview buffer... finally. */
if (detached)
{
if ((dispose != DISPOSE_REPLACE) && (whichframe != 0))
{
2012-09-22 06:46:05 +08:00
gint top = MAX (rawy, 0);
gint bottom = MIN (rawy + rawheight, height);
reshape_from_bitmap (shape_preview_mask);
gdk_draw_rgb_image (gtk_widget_get_window (shape_drawing_area),
shape_style->white_gc,
2012-09-22 06:46:05 +08:00
0, top, width, bottom - top,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
2012-09-22 06:46:05 +08:00
preview_data + 3 * top * width,
width * 3);
}
else
{
2012-09-22 06:46:05 +08:00
reshape_from_bitmap (shape_preview_mask);
gdk_draw_rgb_image (gtk_widget_get_window (shape_drawing_area),
shape_style->white_gc,
0, 0, width, height,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
preview_data, width * 3);
}
}
1997-11-25 06:05:25 +08:00
else
{
2012-09-22 06:46:05 +08:00
if ((dispose != DISPOSE_REPLACE) && (whichframe != 0))
{
2012-09-22 06:46:05 +08:00
gint top = MAX (rawy, 0);
gint bottom = MIN (rawy + rawheight, height);
2012-09-22 06:46:05 +08:00
gdk_draw_rgb_image (gtk_widget_get_window (drawing_area),
drawing_style->white_gc,
0, top, width, bottom - top,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
preview_data + 3 * top * width,
width * 3);
}
else
{
2012-09-22 06:46:05 +08:00
gdk_draw_rgb_image (gtk_widget_get_window (drawing_area),
drawing_style->white_gc,
0, 0, width, height,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
preview_data, width * 3);
}
}
1997-11-25 06:05:25 +08:00
}
/* clean up */
2012-09-22 06:46:05 +08:00
g_object_unref (buffer);
1997-11-25 06:05:25 +08:00
}
static void
show_frame (void)
1997-11-25 06:05:25 +08:00
{
gchar *text;
1997-11-25 06:05:25 +08:00
/* update the dialog's progress bar */
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
((gfloat) frame_number /
(gfloat) (total_frames - 0.999)));
text = g_strdup_printf (_("Frame %d of %d"), frame_number + 1, total_frames);
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), text);
g_free (text);
1997-11-25 06:05:25 +08:00
}
static void
init_preview (void)
1997-11-25 06:05:25 +08:00
{
gint i;
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
for (i = 0; i < width; i++)
1997-11-25 06:05:25 +08:00
{
if (i & 8)
{
preview_alpha1_data[i*3 + 0] =
preview_alpha1_data[i*3 + 1] =
preview_alpha1_data[i*3 + 2] = 102;
preview_alpha2_data[i*3 + 0] =
preview_alpha2_data[i*3 + 1] =
preview_alpha2_data[i*3 + 2] = 154;
}
1997-11-25 06:05:25 +08:00
else
{
preview_alpha1_data[i*3 + 0] =
preview_alpha1_data[i*3 + 1] =
preview_alpha1_data[i*3 + 2] = 154;
preview_alpha2_data[i*3 + 0] =
preview_alpha2_data[i*3 + 1] =
preview_alpha2_data[i*3 + 2] = 102;
}
1997-11-25 06:05:25 +08:00
}
drawing_area_data = preview_data;
shape_drawing_area_data = preview_data;
1997-11-25 06:05:25 +08:00
}
static void
total_alpha_preview (guchar *ptr)
1997-11-25 06:05:25 +08:00
{
gint i;
for (i = 0; i < height; i++)
{
if (i & 8)
memcpy (&ptr[i * 3 * width], preview_alpha1_data, 3 * width);
else
memcpy (&ptr[i * 3 * width], preview_alpha2_data, 3 * width);
1997-11-25 06:05:25 +08:00
}
}
/* Util. */
static void
remove_timer (void)
1997-11-25 06:05:25 +08:00
{
if (timer)
{
g_source_remove (timer);
1997-11-25 06:05:25 +08:00
timer = 0;
}
}
static void
do_step (void)
1997-11-25 06:05:25 +08:00
{
frame_number = (frame_number + 1) % total_frames;
render_frame (frame_number);
1997-11-25 06:05:25 +08:00
}
/* Callbacks */
removed our own action_area API and use GtkDialog's one. Create all 2003-11-06 Michael Natterer <mitch@gimp.org> * libgimpwidgets/gimpdialog.[ch]: removed our own action_area API and use GtkDialog's one. Create all dialogs without separator. Changed almost everything else too. Fixes bug #125143. * libgimpwidgets/gimpquerybox.c * libgimpwidgets/gimpunitmenu.c: changed accordingly. * libgimp/gimpexport.[ch]: ditto. Renamed enum GimpExportReturnType to GimpExportReturn. * libgimp/gimpcompat.h: added a #define for the old name. * themes/Default/gtkrc: increased action_area border to 6 pixels. * app/display/gimpdisplayshell-filter-dialog.c * app/display/gimpdisplayshell-scale.c * app/display/gimpprogress.c * app/gui/brush-select.c * app/gui/channels-commands.c * app/gui/color-notebook.c * app/gui/convert-dialog.c * app/gui/file-new-dialog.c * app/gui/font-select.c * app/gui/gradient-editor-commands.c * app/gui/gradient-select.c * app/gui/grid-dialog.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/layers-commands.c * app/gui/module-browser.c * app/gui/offset-dialog.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/qmask-commands.c * app/gui/resize-dialog.c * app/gui/resolution-calibrate-dialog.c * app/gui/stroke-dialog.c * app/gui/templates-commands.c * app/gui/user-install-dialog.c * app/gui/vectors-commands.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpimagemaptool.c * app/tools/gimpmeasuretool.c * app/tools/gimptransformtool.c * app/widgets/gimptexteditor.c * app/widgets/gimptooldialog.[ch] * app/widgets/gimpviewabledialog.[ch] * app/widgets/gimpwidgets-utils.c: changed accordingly and increased the dialogs' outer borders to 6 pixels all over the place. * plug-ins/*/*.c: changed accordingly. The plug-ins may be arbitrarily broken, I tested none of them.
2003-11-06 23:27:05 +08:00
static void
window_destroy (GtkWidget *widget)
1997-11-25 06:05:25 +08:00
{
if (playing)
remove_timer ();
if (shape_window)
removed our own action_area API and use GtkDialog's one. Create all 2003-11-06 Michael Natterer <mitch@gimp.org> * libgimpwidgets/gimpdialog.[ch]: removed our own action_area API and use GtkDialog's one. Create all dialogs without separator. Changed almost everything else too. Fixes bug #125143. * libgimpwidgets/gimpquerybox.c * libgimpwidgets/gimpunitmenu.c: changed accordingly. * libgimp/gimpexport.[ch]: ditto. Renamed enum GimpExportReturnType to GimpExportReturn. * libgimp/gimpcompat.h: added a #define for the old name. * themes/Default/gtkrc: increased action_area border to 6 pixels. * app/display/gimpdisplayshell-filter-dialog.c * app/display/gimpdisplayshell-scale.c * app/display/gimpprogress.c * app/gui/brush-select.c * app/gui/channels-commands.c * app/gui/color-notebook.c * app/gui/convert-dialog.c * app/gui/file-new-dialog.c * app/gui/font-select.c * app/gui/gradient-editor-commands.c * app/gui/gradient-select.c * app/gui/grid-dialog.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/layers-commands.c * app/gui/module-browser.c * app/gui/offset-dialog.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/qmask-commands.c * app/gui/resize-dialog.c * app/gui/resolution-calibrate-dialog.c * app/gui/stroke-dialog.c * app/gui/templates-commands.c * app/gui/user-install-dialog.c * app/gui/vectors-commands.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpimagemaptool.c * app/tools/gimpmeasuretool.c * app/tools/gimptransformtool.c * app/widgets/gimptexteditor.c * app/widgets/gimptooldialog.[ch] * app/widgets/gimpviewabledialog.[ch] * app/widgets/gimpwidgets-utils.c: changed accordingly and increased the dialogs' outer borders to 6 pixels all over the place. * plug-ins/*/*.c: changed accordingly. The plug-ins may be arbitrarily broken, I tested none of them.
2003-11-06 23:27:05 +08:00
gtk_widget_destroy (GTK_WIDGET (shape_window));
gtk_main_quit ();
1997-11-25 06:05:25 +08:00
}
static gint
advance_frame_callback (gpointer data)
1997-11-25 06:05:25 +08:00
{
gdouble duration;
1997-11-25 06:05:25 +08:00
remove_timer();
duration = get_frame_duration ((frame_number + 1) % total_frames);
timer = g_timeout_add (duration * get_duration_factor (duration_index),
advance_frame_callback, NULL);
do_step ();
show_frame ();
return FALSE;
1997-11-25 06:05:25 +08:00
}
1997-11-25 06:05:25 +08:00
static void
2006-02-26 07:18:59 +08:00
play_callback (GtkToggleAction *action)
1997-11-25 06:05:25 +08:00
{
if (playing)
2006-02-26 07:18:59 +08:00
remove_timer ();
playing = gtk_toggle_action_get_active (action);
1997-11-25 06:05:25 +08:00
2006-02-26 07:18:59 +08:00
if (playing)
timer = g_timeout_add (get_frame_duration (frame_number) *
get_duration_factor (duration_index),
2006-02-26 07:18:59 +08:00
advance_frame_callback, NULL);
g_object_set (action,
"tooltip", playing ? _("Stop playback") : _("Start playback"),
NULL);
1997-11-25 06:05:25 +08:00
}
static gdouble
get_duration_factor (gint index)
{
switch (index)
{
case 0:
return 0.125;
case 1:
return 0.25;
case 2:
return 0.5;
case 3:
return 1.0;
case 4:
return 2.0;
case 5:
return 4.0;
case 6:
return 8.0;
default:
return 1.0;
}
}
static gint
get_fps (gint index)
{
switch (index)
{
case 0:
return 10;
case 1:
return 12;
case 2:
return 15;
case 3:
return 24;
case 4:
return 25;
case 5:
return 30;
case 6:
return 50;
case 7:
return 60;
case 8:
return 72;
default:
return 10;
}
}
1997-11-25 06:05:25 +08:00
static void
step_callback (GtkAction *action)
1997-11-25 06:05:25 +08:00
{
if (playing)
gtk_action_activate (gtk_ui_manager_get_action (ui_manager,
"/anim-play-toolbar/play"));
1997-11-25 06:05:25 +08:00
do_step();
show_frame();
}
static void
rewind_callback (GtkAction *action)
{
if (playing)
gtk_action_activate (gtk_ui_manager_get_action (ui_manager,
"/anim-play-toolbar/play"));
frame_number = 0;
render_frame (frame_number);
show_frame ();
}
static void
speed_up_callback (GtkAction *action)
{
if (duration_index > 0)
--duration_index;
gtk_action_set_sensitive (action, duration_index > 0);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-reset");
gtk_action_set_sensitive (action, duration_index != 3);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-down");
gtk_action_set_sensitive (action, TRUE);
update_combobox ();
}
static void
speed_down_callback (GtkAction *action)
{
if (duration_index < 6)
++duration_index;
gtk_action_set_sensitive (action, duration_index < 6);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-reset");
gtk_action_set_sensitive (action, duration_index != 3);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-up");
gtk_action_set_sensitive (action, TRUE);
update_combobox ();
}
static void
speed_reset_callback (GtkAction *action)
{
duration_index = 3;
gtk_action_set_sensitive (action, FALSE);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-down");
gtk_action_set_sensitive (action, TRUE);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-up");
gtk_action_set_sensitive (action, TRUE);
update_combobox ();
}
static void
speedcombo_changed (GtkWidget *combo, gpointer data)
{
GtkAction * action;
duration_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-reset");
gtk_action_set_sensitive (action, duration_index != 3);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-down");
gtk_action_set_sensitive (action, duration_index < 6);
action = gtk_ui_manager_get_action (ui_manager,
"/anim-play-popup/speed-up");
gtk_action_set_sensitive (action, duration_index > 0);
}
static void
fpscombo_changed (GtkWidget *combo, gpointer data)
{
default_frame_duration = 1000 / get_fps(gtk_combo_box_get_active (GTK_COMBO_BOX (combo)));
}
static void
update_combobox (void)
{
gtk_combo_box_set_active (GTK_COMBO_BOX (speedcombo), duration_index);
}
/* tag util. */
static DisposeType
get_frame_disposal (guint whichframe)
{
DisposeType disposal;
gchar *layer_name;
layer_name = gimp_item_get_name (layers[total_frames-(whichframe+1)]);
disposal = parse_disposal_tag (layer_name);
g_free (layer_name);
return disposal;
}
static guint32
get_frame_duration (guint whichframe)
{
gchar *layer_name;
gint duration = 0;
layer_name = gimp_item_get_name (layers[total_frames-(whichframe+1)]);
if (layer_name)
{
duration = parse_ms_tag (layer_name);
g_free (layer_name);
}
if (duration <= 0)
duration = default_frame_duration;
return (guint32) duration;
}
static gboolean
is_ms_tag (const gchar *str,
gint *duration,
gint *taglength)
{
gint sum = 0;
gint offset;
gint length;
length = strlen(str);
if (str[0] != '(')
return FALSE;
offset = 1;
/* eat any spaces between open-parenthesis and number */
while ((offset < length) && (str[offset] == ' '))
offset++;
if ((offset>=length) || (!g_ascii_isdigit (str[offset])))
return FALSE;
do
{
sum *= 10;
sum += str[offset] - '0';
offset++;
}
while ((offset<length) && (g_ascii_isdigit (str[offset])));
if (length - offset <= 2)
return FALSE;
/* eat any spaces between number and 'ms' */
while ((offset < length) && (str[offset] == ' '))
offset++;
if (length - offset <= 2 ||
g_ascii_toupper (str[offset]) != 'M' ||
g_ascii_toupper (str[offset + 1]) != 'S')
return FALSE;
offset += 2;
/* eat any spaces between 'ms' and close-parenthesis */
while ((offset < length) && (str[offset] == ' '))
offset++;
if ((length - offset < 1) || (str[offset] != ')'))
return FALSE;
offset++;
*duration = sum;
*taglength = offset;
return TRUE;
}
static gint
parse_ms_tag (const gchar *str)
{
gint i;
gint length = strlen (str);
for (i = 0; i < length; i++)
{
gint rtn;
gint dummy;
if (is_ms_tag (&str[i], &rtn, &dummy))
return rtn;
}
return -1;
}
static gboolean
is_disposal_tag (const gchar *str,
DisposeType *disposal,
gint *taglength)
{
if (strlen (str) != 9)
return FALSE;
if (strncmp (str, "(combine)", 9) == 0)
{
*taglength = 9;
*disposal = DISPOSE_COMBINE;
return TRUE;
}
else if (strncmp (str, "(replace)", 9) == 0)
{
*taglength = 9;
*disposal = DISPOSE_REPLACE;
return TRUE;
}
return FALSE;
}
static DisposeType
parse_disposal_tag (const gchar *str)
{
gint i;
gint length = strlen (str);
for (i = 0; i < length; i++)
{
DisposeType rtn;
gint dummy;
if (is_disposal_tag (&str[i], &rtn, &dummy))
return rtn;
}
return DISPOSE_UNDEFINED; /* FIXME */
}