diff --git a/ChangeLog b/ChangeLog index 2e672970f8..924440869b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 2e672970f8..924440869b 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,46 @@ +2001-05-04 Havoc Pennington + + * configure.in: fix some shell typos + + * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix + + * gtk/gtkimage.c: handle animations + + * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request + border_width * 2, not just border_width + + * gtk/gtkscale.c: add "format_value" signal to allow people + to override the way values are drawn. + (gtk_scale_get_value_size): fix width/height mistake, + and compute size from actual displayed text, not + from made-up text. + + * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in + signal registration + + * tests/testtext.c: Add "Remove all tags" menu item for testing + + * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement + + * demos/gtk-demo/main.c (main): add hack so we can find modules + without installing gtk + + * demos/gtk-demo/textview.c (insert_text): demo font scaling + + * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling + factor) + (gtk_cell_renderer_text_set_property): remove some bogus + g_object_notify + + * gtk/gtktexttag.c: add "scale" property which is a font scaling + factor + + * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute + to layout + + * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from + gtk_text_iter_is_first + 2001-01-06 Hans Breuer * gdk/gdk.def : updated exports diff --git a/configure.in b/configure.in index 026edc1566..590460ff1f 100644 --- a/configure.in +++ b/configure.in @@ -563,18 +563,18 @@ AM_CONDITIONAL(HAVE_TIFF, test "x$LIBTIFF" != x) AM_CONDITIONAL(HAVE_PNG, test "x$LIBPNG" != x) AM_CONDITIONAL(HAVE_JPEG, test "x$LIBJPEG" != x) -if test $dynworks = no ; then +if $dynworks ; then STATIC_LIB_DEPS="$LIBTIFF $LIBJPEG $LIBPNG" else STATIC_LIB_DEPS= if echo "$included_loaders" | grep "\(^\|\,\)tiff\(\$\|\,\)" > /dev/null; then - STATIC_LIB_DEPS="STATIC_LIB_DEPS $LIBTIFF" + STATIC_LIB_DEPS="$STATIC_LIB_DEPS $LIBTIFF" fi if echo "$included_loaders" | grep "\(^\|\,\)jpeg\(\$\|\,\)" > /dev/null; then - STATIC_LIB_DEPS="STATIC_LIB_DEPS $LIBJPEG" + STATIC_LIB_DEPS="$STATIC_LIB_DEPS $LIBJPEG" fi if echo "$included_loaders" | grep "\(^\|\,\)png\(\$\|\,\)" > /dev/null; then - STATIC_LIB_DEPS="STATIC_LIB_DEPS $LIBPNG" + STATIC_LIB_DEPS="$STATIC_LIB_DEPS $LIBPNG" fi fi diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 3cb530503b..7d199e09c5 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -59,8 +59,10 @@ gtk_demo_SOURCES = \ gtk_demo_DEPENDENCIES = $(DEPS) gtk_demo_LDADD = $(LDADDS) -IMAGEFILES= apple-red.png \ +IMAGEFILES= alphatest.png \ + apple-red.png \ background.jpg \ + floppybuddy.gif \ gnome-applets.png \ gnome-calendar.png \ gnome-foot.png \ diff --git a/demos/gtk-demo/alphatest.png b/demos/gtk-demo/alphatest.png new file mode 100644 index 0000000000..eb5885f89c Binary files /dev/null and b/demos/gtk-demo/alphatest.png differ diff --git a/demos/gtk-demo/floppybuddy.gif b/demos/gtk-demo/floppybuddy.gif new file mode 100644 index 0000000000..ac986c8ed3 Binary files /dev/null and b/demos/gtk-demo/floppybuddy.gif differ diff --git a/demos/gtk-demo/images.c b/demos/gtk-demo/images.c index 353cb4c8c2..7faeb53cca 100644 --- a/demos/gtk-demo/images.c +++ b/demos/gtk-demo/images.c @@ -8,6 +8,8 @@ * * If you want to put image data in your program as a C variable, * use the make-inline-pixbuf program that comes with GTK+. + * This way you won't need to depend on loading external files, your + * application binary can be self-contained. */ #include @@ -39,14 +41,14 @@ progressive_prepared_callback (GdkPixbufLoader* loader, gpointer data) static void progressive_updated_callback (GdkPixbufLoader* loader, - guint x, guint y, guint width, guint height, + gint x, gint y, gint width, gint height, gpointer data) { GtkWidget* image; image = GTK_WIDGET (data); - /* We know the pixbuf inside the image has changed, but the image + /* We know the pixbuf inside the GtkImage has changed, but the image * itself doesn't know this; so queue a redraw. If we wanted to be * really efficient, we could use a drawing area or something * instead of a GtkImage, so we could control the exact position of @@ -85,7 +87,7 @@ progressive_timeout (gpointer data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - "Failure reading image file 'gtk-logo-rgb.gif': %s", + "Failure reading image file 'alphatest.png': %s", g_strerror (errno)); gtk_signal_connect (GTK_OBJECT (dialog), @@ -180,10 +182,10 @@ progressive_timeout (gpointer data) { const gchar *filename; - if (g_file_test ("./gtk-logo-rgb.gif", G_FILE_TEST_EXISTS)) - filename = "./gtk-logo-rgb.gif"; + if (g_file_test ("./alphatest.png", G_FILE_TEST_EXISTS)) + filename = "./alphatest.png"; else - filename = DEMOCODEDIR"/gtk-logo-rgb.gif"; + filename = DEMOCODEDIR"/alphatest.png"; image_stream = fopen (filename, "r"); @@ -195,7 +197,7 @@ progressive_timeout (gpointer data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - "Unable to open image file 'gtk-logo-rgb.gif': %s", + "Unable to open image file 'alphatest.png': %s", g_strerror (errno)); gtk_signal_connect (GTK_OBJECT (dialog), @@ -246,7 +248,7 @@ start_progressive_loading (GtkWidget *image) * The timeout simply simulates a slow data source by inserting * pauses in the reading process. */ - load_timeout = g_timeout_add (300, + load_timeout = g_timeout_add (150, progressive_timeout, image); } @@ -359,6 +361,37 @@ do_images (void) gtk_container_add (GTK_CONTAINER (frame), image); + + /* Animation */ + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + "Animation loaded from a file"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + /* The alignment keeps the frame from growing when users resize + * the window + */ + align = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align), frame); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + + /* We look for the image in the current directory first, + * so you can run gtk-demo without installing GTK + */ + if (g_file_test ("./floppybuddy.gif", G_FILE_TEST_EXISTS)) + image = gtk_image_new_from_file ("./floppybuddy.gif"); + else + image = gtk_image_new_from_file (DEMOCODEDIR"/floppybuddy.gif"); + + gtk_container_add (GTK_CONTAINER (frame), image); + + + /* Progressive */ + + label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (label), "Progressive image loading"); diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c index fabf898d44..83a27b362f 100644 --- a/demos/gtk-demo/main.c +++ b/demos/gtk-demo/main.c @@ -465,6 +465,18 @@ main (int argc, char **argv) GtkWidget *tree; GtkTextTag *tag; + /* Most code in gtk-demo is intended to be exemplary, but not + * these few lines, which are just a hack so gtk-demo will work + * in the GTK tree without installing it. + */ + if (g_file_test ("../../gdk-pixbuf/.libs/libpixbufloader-pnm.so", + G_FILE_TEST_EXISTS)) + { + putenv ("GDK_PIXBUF_MODULEDIR=../../gdk-pixbuf/.libs"); + putenv ("GTK_IM_MODULE_FILE=../../modules/input/gtk.immodules"); + } + /* -- End of hack -- */ + gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); diff --git a/demos/gtk-demo/textview.c b/demos/gtk-demo/textview.c index 928df9073b..2fc970bfd4 100644 --- a/demos/gtk-demo/textview.c +++ b/demos/gtk-demo/textview.c @@ -84,6 +84,12 @@ create_tags (GtkTextBuffer *buffer) /* points times the PANGO_SCALE factor */ "size", 30 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag (buffer, "xx-small", + "scale", PANGO_SCALE_XX_SMALL, NULL); + + gtk_text_buffer_create_tag (buffer, "x-large", + "scale", PANGO_SCALE_X_LARGE, NULL); + gtk_text_buffer_create_tag (buffer, "monospace", "family", "monospace", NULL); @@ -199,8 +205,17 @@ insert_text (GtkTextBuffer *buffer) gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "big", -1, "big", NULL); - gtk_text_buffer_insert (buffer, &iter, " text.\n\n", -1); - + gtk_text_buffer_insert (buffer, &iter, " text. ", -1); + gtk_text_buffer_insert (buffer, &iter, "It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as ", -1); + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, + "xx-small", -1, + "xx-small", NULL); + gtk_text_buffer_insert (buffer, &iter, " or ", -1); + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, + "x-large", -1, + "x-large", NULL); + gtk_text_buffer_insert (buffer, &iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n", -1); + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Colors. ", -1, "heading", NULL); diff --git a/demos/testanimation.c b/demos/testanimation.c index a823249b42..82b932c2d0 100644 --- a/demos/testanimation.c +++ b/demos/testanimation.c @@ -23,466 +23,426 @@ #include #include #include +#include #include -#include -typedef struct { - FILE *imagefile; - GdkPixbufLoader *loader; - GtkWidget **rgbwin; - guchar *buf; - guint timeout; - guint readlen; +typedef struct _LoadContext LoadContext; -} ProgressFileStatus; - - -#define DEFAULT_WIDTH 24 -#define DEFAULT_HEIGHT 24 - -static const unsigned char default_image[] = { - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xae, 0xb3, 0xb3, 0xc6, 0xc9, 0xcd, 0xd7, 0xd4, 0xdf, - 0xec, 0xde, 0xf3, 0xe7, 0xcb, 0xe9, 0xd9, 0xb5, 0xd3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xb1, 0xb7, 0xa5, - 0xb0, 0xb8, 0xad, 0xb3, 0xb9, 0xb6, 0xc1, 0xc6, 0xc8, 0xd5, 0xd3, 0xdc, - 0xec, 0xde, 0xf3, 0xe5, 0xca, 0xe6, 0xe0, 0xbb, 0xd7, 0xe1, 0xad, 0xc2, - 0xe3, 0xac, 0xa3, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xca, 0xc1, 0xa4, 0xc5, 0xc7, 0xac, - 0xb7, 0xbe, 0xaf, 0xad, 0xb4, 0xaf, 0xbd, 0xc2, 0xc3, 0xd1, 0xd0, 0xd8, - 0xec, 0xde, 0xf3, 0xe5, 0xc7, 0xe4, 0xe0, 0xb6, 0xd1, 0xe7, 0xa9, 0xb4, - 0xed, 0xcd, 0xb6, 0xd6, 0xcf, 0xae, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xdf, 0xa7, 0x9f, 0xdd, 0xbf, 0xaa, 0xcf, 0xc5, 0xa9, - 0xc1, 0xc4, 0xac, 0xb2, 0xba, 0xaf, 0xb6, 0xbb, 0xbb, 0xcd, 0xce, 0xd4, - 0xec, 0xde, 0xf3, 0xe4, 0xc4, 0xe1, 0xe0, 0xaf, 0xc7, 0xea, 0xbc, 0xae, - 0xe1, 0xd6, 0xb6, 0xc7, 0xcc, 0xae, 0xa2, 0xab, 0x9a, 0x00, 0x00, 0x00, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xe3, 0xab, 0xc0, 0xe6, 0xa3, 0xa7, 0xdf, 0xba, 0xa8, - 0xcf, 0xc5, 0xa9, 0xbd, 0xc2, 0xae, 0xad, 0xb4, 0xaf, 0xc6, 0xc9, 0xcd, - 0xec, 0xde, 0xf3, 0xe2, 0xbf, 0xdc, 0xe7, 0xa9, 0xb4, 0xe7, 0xd6, 0xb8, - 0xc7, 0xcc, 0xae, 0xac, 0xb6, 0xa6, 0x9d, 0xa8, 0x9f, 0x00, 0x00, 0x00, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xd9, 0xaf, 0xcf, 0xe1, 0xb4, 0xd2, 0xe2, 0xb0, 0xcb, 0xe4, 0xa9, 0xbb, - 0xe2, 0xb2, 0xa6, 0xcf, 0xc5, 0xa9, 0x6a, 0x6a, 0x6a, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x6a, 0x6a, 0x6a, 0xed, 0xcd, 0xb6, 0xc7, 0xcc, 0xae, - 0xa6, 0xb1, 0xa3, 0x98, 0xa2, 0x9c, 0x8f, 0x97, 0x96, 0x7e, 0x84, 0x85, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xe8, 0xc6, 0xe7, 0xe5, 0xc2, 0xe3, 0xe3, 0xbd, 0xdd, 0xe1, 0xb6, 0xd5, - 0xe2, 0xb0, 0xcb, 0x6a, 0x6a, 0x6a, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x6a, 0x6a, 0x6a, 0x9d, 0xa8, 0x9f, - 0x8f, 0x97, 0x96, 0x8b, 0x90, 0x92, 0x97, 0x9e, 0xa2, 0xa0, 0xa7, 0xae, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xe7, 0xd3, 0xed, 0xe8, 0xd1, 0xed, 0xe8, 0xce, 0xec, 0xe9, 0xcc, 0xeb, - 0xe8, 0xc6, 0xe7, 0x0d, 0x0d, 0x0d, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x0d, 0x0d, 0x0d, 0x97, 0x9e, 0xa2, - 0xa7, 0xae, 0xb7, 0xb2, 0xb6, 0xc5, 0xba, 0xbc, 0xce, 0xbf, 0xbe, 0xd3, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xe9, 0xdf, 0xf0, 0xe9, 0xdf, 0xf0, 0xe9, 0xdf, 0xf0, 0xe9, 0xdf, 0xf0, - 0xe9, 0xdf, 0xf0, 0x0d, 0x0d, 0x0d, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x0d, 0x0d, 0x0d, 0xe1, 0xd2, 0xf7, - 0xe1, 0xd2, 0xf7, 0xe1, 0xd2, 0xf7, 0xe1, 0xd2, 0xf7, 0xe1, 0xd2, 0xf7, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xca, 0xc7, 0xd2, 0xc5, 0xc4, 0xcd, 0xbf, 0xbf, 0xc7, 0xb8, 0xb9, 0xc0, - 0xae, 0xaf, 0xb6, 0x6a, 0x6a, 0x6a, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x6a, 0x6a, 0x6a, 0xd5, 0xa8, 0xe1, - 0xd8, 0xb2, 0xe9, 0xd9, 0xb8, 0xed, 0xdb, 0xbd, 0xf0, 0xdc, 0xbf, 0xf1, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0xa4, 0xa6, 0xac, 0xa8, 0xaa, 0xaf, 0xa0, 0xa6, 0xa8, 0x98, 0x9e, 0x9c, - 0xa1, 0xa8, 0x9e, 0xb1, 0xb6, 0xa1, 0x6a, 0x6a, 0x6a, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x6a, 0x6a, 0x6a, 0xc0, 0x8c, 0xad, 0xcc, 0x90, 0xb5, - 0xd3, 0x94, 0xca, 0xd6, 0xa2, 0xdb, 0xd5, 0xa8, 0xe1, 0xcf, 0xa7, 0xdf, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0x98, 0x9f, 0x9b, 0xa1, 0xa8, 0x9e, 0xac, 0xb3, 0xa0, - 0xb9, 0xb9, 0xa4, 0xd0, 0xb8, 0xa8, 0xc5, 0xb5, 0xb8, 0xb6, 0xbb, 0xad, - 0xe3, 0xd7, 0xb5, 0xdd, 0xb4, 0xa9, 0xcb, 0x89, 0xac, 0xc0, 0x8c, 0xad, - 0xc8, 0x91, 0xb5, 0xd1, 0x8d, 0xb7, 0xd3, 0x94, 0xca, 0x00, 0x00, 0x00, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xa1, 0xa7, 0x98, 0xb1, 0xb6, 0xa1, 0xbd, 0xb9, 0xa5, - 0xd0, 0xb8, 0xa8, 0xca, 0xb5, 0xb7, 0xb8, 0xb1, 0xb1, 0xc2, 0xc8, 0xb2, - 0xe3, 0xd7, 0xb5, 0xe1, 0xbf, 0xaf, 0xdb, 0x92, 0x9a, 0xbe, 0x82, 0xa6, - 0xc0, 0x8c, 0xad, 0xc8, 0x91, 0xb4, 0xc7, 0x8b, 0xb0, 0x00, 0x00, 0x00, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xbc, 0xb6, 0xa1, 0xd0, 0xb8, 0xa8, - 0xcd, 0xb6, 0xb7, 0xc0, 0xb4, 0xb5, 0xb1, 0xb1, 0xaa, 0xca, 0xd1, 0xb4, - 0xe3, 0xd7, 0xb5, 0xe2, 0xc1, 0xb0, 0xdb, 0xa8, 0xa3, 0xd2, 0x8a, 0xa9, - 0xb7, 0x7e, 0xa2, 0xbd, 0x89, 0xa9, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xc9, 0xaf, 0xaf, - 0xc5, 0xb5, 0xb8, 0xb8, 0xb1, 0xb1, 0xb6, 0xbb, 0xad, 0xd0, 0xd6, 0xb5, - 0xe3, 0xd7, 0xb5, 0xe2, 0xbf, 0xaf, 0xdd, 0xb4, 0xa9, 0xdb, 0x92, 0x9a, - 0xc6, 0x84, 0xa7, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xac, 0xaa, 0xa6, 0xbd, 0xc3, 0xb0, 0xd2, 0xd7, 0xb5, - 0xe3, 0xd7, 0xb5, 0xe2, 0xbf, 0xae, 0xdb, 0xb6, 0xa8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff +struct _LoadContext +{ + gchar *filename; + GtkWidget *window; + GdkPixbufLoader *pixbuf_loader; + guint load_timeout; + FILE* image_stream; }; static void -quit_func (GtkWidget *widget, gpointer dummy) +destroy_context (gpointer data) { - gtk_main_quit (); + LoadContext *lc = data; + + g_free (lc->filename); + + if (lc->load_timeout) + g_source_remove (lc->load_timeout); + + if (lc->image_stream) + fclose (lc->image_stream); + + if (lc->pixbuf_loader) + { + gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL); + g_object_unref (G_OBJECT (lc->pixbuf_loader)); + } + + g_free (lc); +} + +static LoadContext* +get_load_context (GtkWidget *image) +{ + LoadContext *lc; + + lc = g_object_get_data (G_OBJECT (image), "lc"); + + if (lc == NULL) + { + lc = g_new0 (LoadContext, 1); + + g_object_set_data_full (G_OBJECT (image), + "lc", + lc, + destroy_context); + } + + return lc; } static void -expose_func (GtkWidget *drawing_area, GdkEventExpose *event, gpointer data) +progressive_prepared_callback (GdkPixbufLoader* loader, + gpointer data) { - GdkPixbuf *pixbuf; + GdkPixbuf* pixbuf; + GtkWidget* image; - pixbuf = (GdkPixbuf *)gtk_object_get_data(GTK_OBJECT(drawing_area), "pixbuf"); - - if (gdk_pixbuf_get_has_alpha (pixbuf)) { - gdk_draw_rgb_32_image (drawing_area->window, - drawing_area->style->black_gc, - event->area.x, event->area.y, - event->area.width, - event->area.height, - GDK_RGB_DITHER_MAX, - gdk_pixbuf_get_pixels (pixbuf) - + (event->area.y * gdk_pixbuf_get_rowstride (pixbuf)) - + (event->area.x * gdk_pixbuf_get_n_channels (pixbuf)), - gdk_pixbuf_get_rowstride (pixbuf)); - } else { - gdk_draw_rgb_image (drawing_area->window, - drawing_area->style->white_gc, - event->area.x, event->area.y, - event->area.width, - event->area.height, - GDK_RGB_DITHER_NORMAL, - gdk_pixbuf_get_pixels (pixbuf) - + (event->area.y * gdk_pixbuf_get_rowstride (pixbuf)) - + (event->area.x * gdk_pixbuf_get_n_channels (pixbuf)), - gdk_pixbuf_get_rowstride (pixbuf)); - } -} - -static void -config_func (GtkWidget *drawing_area, GdkEventConfigure *event, gpointer data) -{ - GdkPixbuf *pixbuf; + image = GTK_WIDGET (data); - pixbuf = (GdkPixbuf *)gtk_object_get_data(GTK_OBJECT(drawing_area), "pixbuf"); + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); -#if 0 - if (((event->width) != gdk_pixbuf_get_width (pixbuf)) || - ((event->height) != gdk_pixbuf_get_height (pixbuf))) - gdk_pixbuf_scale(pixbuf, event->width, event->height); -#endif + /* Avoid displaying random memory contents, since the pixbuf + * isn't filled in yet. + */ + gdk_pixbuf_fill (pixbuf, 0xaaaaaaff); + + /* Could set the pixbuf instead, if we only wanted to display + * static images. + */ + gtk_image_set_from_animation (GTK_IMAGE (image), + gdk_pixbuf_loader_get_animation (loader)); } -static GtkWidget* -new_testrgb_window (GdkPixbuf *pixbuf, gchar *title) +static void +progressive_updated_callback (GdkPixbufLoader* loader, + gint x, gint y, gint width, gint height, + gpointer data) { - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *temp_box; - GtkWidget *button; - GtkWidget *drawing_area; - gint w, h; - - w = gdk_pixbuf_get_width (pixbuf); - h = gdk_pixbuf_get_height (pixbuf); + GtkWidget* image; + + image = GTK_WIDGET (data); - window = gtk_widget_new (gtk_window_get_type (), - "GtkObject::user_data", NULL, - "GtkWindow::type", GTK_WINDOW_TOPLEVEL, - "GtkWindow::title", "testrgb", - "GtkWindow::allow_shrink", TRUE, - NULL); - gtk_signal_connect (GTK_OBJECT (window), "destroy", - (GtkSignalFunc) quit_func, NULL); + /* We know the pixbuf inside the GtkImage has changed, but the image + * itself doesn't know this; so queue a redraw. If we wanted to be + * really efficient, we could use a drawing area or something + * instead of a GtkImage, so we could control the exact position of + * the pixbuf on the display, then we could queue a draw for only + * the updated area of the image. + */ - vbox = gtk_vbox_new (FALSE, 0); - - if (title) - gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (title), - TRUE, TRUE, 0); - - drawing_area = gtk_drawing_area_new (); - - temp_box = gtk_hbox_new (FALSE, 0); - gtk_widget_set_usize (GTK_WIDGET (drawing_area), w, h); - gtk_box_pack_start (GTK_BOX (temp_box), drawing_area, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox), temp_box, FALSE, FALSE, 0); - - - gtk_signal_connect (GTK_OBJECT(drawing_area), "expose_event", - GTK_SIGNAL_FUNC(expose_func), NULL); - gtk_signal_connect (GTK_OBJECT(drawing_area), "configure_event", - GTK_SIGNAL_FUNC (config_func), NULL); - - gtk_object_set_data (GTK_OBJECT(drawing_area), "pixbuf", pixbuf); - - gtk_widget_show (drawing_area); - - button = gtk_button_new_with_label ("Quit"); - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); - gtk_signal_connect_object (GTK_OBJECT (button), "clicked", - (GtkSignalFunc) gtk_widget_destroy, - GTK_OBJECT (window)); - - gtk_widget_show (button); - - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_widget_show_all (vbox); - - gtk_widget_show (window); - - return window; + /* We only really need to redraw if the image's animation iterator + * is gdk_pixbuf_animation_iter_on_currently_loading_frame(), but + * who cares. + */ + + gtk_widget_queue_draw (image); } -#if 0 - static gint -update_timeout(gpointer data) +progressive_timeout (gpointer data) { - ProgressFileStatus *status = data; - gboolean done; + GtkWidget *image; + LoadContext *lc; + + image = GTK_WIDGET (data); + lc = get_load_context (image); + + /* This shows off fully-paranoid error handling, so looks scary. + * You could factor out the error handling code into a nice separate + * function to make things nicer. + */ + + if (lc->image_stream) + { + size_t bytes_read; + guchar buf[256]; + GError *error = NULL; + + bytes_read = fread (buf, 1, 256, lc->image_stream); - done = TRUE; - if (!feof(status->imagefile)) { - gint nbytes; + if (ferror (lc->image_stream)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failure reading image file 'alphatest.png': %s", + g_strerror (errno)); - nbytes = fread(status->buf, 1, status->readlen, - status->imagefile); + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); - done = !gdk_pixbuf_loader_write (GDK_PIXBUF_LOADER (status->loader), status->buf, nbytes); - - } + fclose (lc->image_stream); + lc->image_stream = NULL; - if (done) { - gtk_widget_queue_draw(*status->rgbwin); - gdk_pixbuf_loader_close (GDK_PIXBUF_LOADER (status->loader)); - g_object_destroy (G_OBJECT(status->loader)); - fclose (status->imagefile); - g_free (status->buf); - } + gtk_widget_show (dialog); + + lc->load_timeout = 0; - return !done; + return FALSE; /* uninstall the timeout */ + } + + if (!gdk_pixbuf_loader_write (lc->pixbuf_loader, + buf, bytes_read, + &error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to load image: %s", + error->message); + + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + fclose (lc->image_stream); + lc->image_stream = NULL; + + gtk_widget_show (dialog); + + lc->load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + if (feof (lc->image_stream)) + { + fclose (lc->image_stream); + lc->image_stream = NULL; + + /* Errors can happen on close, e.g. if the image + * file was truncated we'll know on close that + * it was incomplete. + */ + error = NULL; + if (!gdk_pixbuf_loader_close (lc->pixbuf_loader, + &error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to load image: %s", + error->message); + + g_error_free (error); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + + g_object_unref (G_OBJECT (lc->pixbuf_loader)); + lc->pixbuf_loader = NULL; + + lc->load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + g_object_unref (G_OBJECT (lc->pixbuf_loader)); + lc->pixbuf_loader = NULL; + } + } + else + { + lc->image_stream = fopen (lc->filename, "r"); + + if (lc->image_stream == NULL) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Unable to open image file '%s': %s", + lc->filename, + g_strerror (errno)); + + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + + lc->load_timeout = 0; + + return FALSE; /* uninstall the timeout */ + } + + if (lc->pixbuf_loader) + { + gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL); + g_object_unref (G_OBJECT (lc->pixbuf_loader)); + lc->pixbuf_loader = NULL; + } + + lc->pixbuf_loader = gdk_pixbuf_loader_new (); + + g_signal_connect_data (G_OBJECT (lc->pixbuf_loader), + "area_prepared", + G_CALLBACK (progressive_prepared_callback), + image, + NULL, FALSE, FALSE); + + g_signal_connect_data (G_OBJECT (lc->pixbuf_loader), + "area_updated", + G_CALLBACK (progressive_updated_callback), + image, + NULL, FALSE, FALSE); + } + + /* leave timeout installed */ + return TRUE; } static void -progressive_prepared_callback(GdkPixbufLoader* loader, gpointer data) +start_progressive_loading (GtkWidget *image) { - GtkWidget** retloc = data; - GdkPixbuf* pixbuf; + LoadContext *lc; - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - g_assert(pixbuf != NULL); - - gdk_pixbuf_ref(pixbuf); /* for the RGB window */ - - *retloc = new_testrgb_window(pixbuf, "Progressive"); - - return; + lc = get_load_context (image); + + /* This is obviously totally contrived (we slow down loading + * on purpose to show how incremental loading works). + * The real purpose of incremental loading is the case where + * you are reading data from a slow source such as the network. + * The timeout simply simulates a slow data source by inserting + * pauses in the reading process. + */ + lc->load_timeout = g_timeout_add (100, + progressive_timeout, + image); } +static GtkWidget * +do_image (const char *filename) +{ + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *align; + GtkWidget *window; + gchar *str, *escaped; + LoadContext *lc; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Image Loading"); + + gtk_container_set_border_width (GTK_CONTAINER (window), 8); + + vbox = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (window), vbox); + + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + escaped = g_markup_escape_text (filename, -1); + str = g_strdup_printf ("Progressively loading: %s", escaped); + gtk_label_set_markup (GTK_LABEL (label), + str); + g_free (escaped); + g_free (str); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + /* The alignment keeps the frame from growing when users resize + * the window + */ + align = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align), frame); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + + image = gtk_image_new_from_pixbuf (NULL); + gtk_container_add (GTK_CONTAINER (frame), image); + + lc = get_load_context (image); + + lc->window = window; + lc->filename = g_strdup (filename); + + start_progressive_loading (image); + + gtk_widget_show_all (window); + + return window; +} static void -progressive_updated_callback(GdkPixbufLoader* loader, guint x, guint y, guint width, guint height, gpointer data) +do_nonprogressive (const gchar *filename) { - GtkWidget** window_loc = data; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *align; + GtkWidget *window; + gchar *str, *escaped; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Animation"); -/* g_print ("progressive_updated_callback:\n\t%d\t%d\t%d\t%d\n", x, y, width, height); */ + gtk_container_set_border_width (GTK_CONTAINER (window), 8); - if (*window_loc != NULL) - gtk_widget_queue_draw_area(*window_loc, - x, y, width, height); + vbox = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (window), vbox); - return; + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + escaped = g_markup_escape_text (filename, -1); + str = g_strdup_printf ("Loaded from file: %s", escaped); + gtk_label_set_markup (GTK_LABEL (label), + str); + g_free (escaped); + g_free (str); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + /* The alignment keeps the frame from growing when users resize + * the window + */ + align = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align), frame); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + + image = gtk_image_new_from_file (filename); + gtk_container_add (GTK_CONTAINER (frame), image); + + gtk_widget_show_all (window); } -#endif - -static int readlen = 4096; - -extern void pixbuf_init(); - int -main (int argc, char **argv) +main (int argc, + char **argv) { - int i; - int found_valid = FALSE; + gint i; + + gtk_init (&argc, &argv); - GdkPixbufAnimation *animation; + i = 1; + while (i < argc) + { + do_image (argv[i]); + do_nonprogressive (argv[i]); + + ++i; + } - pixbuf_init (); - - gtk_init (&argc, &argv); - - gdk_rgb_set_verbose (TRUE); - - gdk_rgb_init (); - - gtk_widget_set_default_colormap (gdk_rgb_get_cmap ()); - - { - char *tbf_readlen = getenv("TBF_READLEN"); - if(tbf_readlen) readlen = atoi(tbf_readlen); - } - - { - char *tbf_bps = getenv("TBF_KBPS"); - guint bps; - - if (tbf_bps) { - bps = atoi(tbf_bps); - g_print ("Simulating %d kBytes/sec\n", bps); - readlen = (bps*1024)/10; - } - } - - i = 1; - if (argc == 1) { - g_print ("USAGE: testanimation FILE1 ...\n"); - return 0; - } else { - for (i = 1; i < argc; i++) { - GError *error; - - error = NULL; - animation = gdk_pixbuf_animation_new_from_file (argv[i], - &error); - - if (animation == NULL) { - g_warning ("Failed to load animation: %s", - error->message); - g_error_free (error); - } - - if (animation) { - gint i = 0; - GList *listptr; - for (listptr = gdk_pixbuf_animation_get_frames (animation); - listptr; - listptr = listptr->next) { - GdkPixbufFrame *frame; - GdkPixbuf *pixbuf; - gchar *title; - - frame = listptr->data; - pixbuf = gdk_pixbuf_frame_get_pixbuf (frame); - - title = g_strdup_printf ("Frame %d", i); - g_print ("Frame %d x:%d y:%d width:%d height:%d\n", - i, - gdk_pixbuf_frame_get_x_offset (frame), - gdk_pixbuf_frame_get_y_offset (frame), - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf)); - new_testrgb_window (pixbuf, title); - g_free (title); - i++; - } - found_valid = TRUE; - } - } -#if 0 - { - GtkWidget* rgb_window = NULL; - ProgressFileStatus status; - GdkPixbufLoader *pixbuf_loader; - - pixbuf_loader = gdk_pixbuf_loader_new (); - status.loader = pixbuf_loader; - - status.rgbwin = &rgb_window; - - status.buf = g_malloc (readlen); - g_signal_connect_data(G_OBJECT(pixbuf_loader), - "area_prepared", - GTK_SIGNAL_FUNC(progressive_prepared_callback), - &rgb_window, - NULL, FALSE, FALSE); - - g_signal_connect_data(G_OBJECT(pixbuf_loader), - "area_updated", - GTK_SIGNAL_FUNC(progressive_updated_callback), - &rgb_window, - NULL, FALSE, FALSE); - - - status.imagefile = fopen (argv[1], "r"); - g_assert (status.imagefile != NULL); - - status.readlen = readlen; - - status.timeout = gtk_timeout_add(100, update_timeout, &status); - } -#endif - } - - if (found_valid) - gtk_main (); - - return 0; + gtk_main (); + + return 0; } + + diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 9d29563171..d59095b692 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,4 +1,8 @@ -2001-04-20 Havoc Pennington +2001-04-26 Havoc Pennington + + * gtk/tmpl/gtkimage.sgml: document GtkImageType + + * gtk/gtk-sections.txt: GtkImageType is public * gdk/tmpl/images.sgml: add warning about gtk_image_new_bitmap diff --git a/docs/reference/gdk-pixbuf/tmpl/animation.sgml b/docs/reference/gdk-pixbuf/tmpl/animation.sgml index 9e9d4b2ff5..f13b066aca 100644 --- a/docs/reference/gdk-pixbuf/tmpl/animation.sgml +++ b/docs/reference/gdk-pixbuf/tmpl/animation.sgml @@ -2,15 +2,17 @@ Animations -Animations as multi-frame structures. +Animated images. - The &gdk-pixbuf; library provides a simple mechanism to load and - represent animations, primarily animated GIF files. Animations - are represented as lists of #GdkPixbufFrame structures. Each - frame structure contains a #GdkPixbuf structure and information - about the frame's overlay mode and duration. + The &gdk-pixbuf; library provides a simple mechanism to load and represent + animations. An animation is conceptually a series of frames to be displayed + over time. Each frame is the same size. The animation may not be represented + as a series of frames internally; for example, it may be stored as a + sprite and instructions for moving the sprite around a background. To display + an animation you don't need to understand its representation, however; you just + ask &gdk-pixbuf; what should be displayed at a given point in time. @@ -18,34 +20,9 @@ Animations as multi-frame structures. #GdkPixbufLoader - - - Each animation frame can have several things happen to it when the - next frame is displayed. The #GdkPixbufFrameAction determines - this. These are essentially the overlay modes supported by GIF - animations. - - -@GDK_PIXBUF_FRAME_RETAIN: The previous image should remain displayed, -and will potentially be occluded by the new frame. -@GDK_PIXBUF_FRAME_DISPOSE: The animation will be reverted to the state -before the frame was shown. -@GDK_PIXBUF_FRAME_REVERT: The animation will be reverted to the first -frame. - - - - This structure describes a frame in a #GdkPixbufAnimation. Each - frame consists of a #GdkPixbuf, an offset of the frame within the - animation's bounding box, a duration, and an overlay mode or - action. - - - - This structure describes an animation, which is represented as a - list of #GdkPixbufFrame structures. + This object describes an animation. @@ -76,15 +53,6 @@ frame. @animation: - - - - - -@animation: -@Returns: - - @@ -94,15 +62,6 @@ frame. @Returns: - - - - - -@animation: -@Returns: - - @@ -112,70 +71,3 @@ frame. @Returns: - - - - - -@src: -@Returns: - - - - - - - -@frame: - - - - - - - -@frame: -@Returns: - - - - - - - -@frame: -@Returns: - - - - - - - -@frame: -@Returns: - - - - - - - -@frame: -@Returns: - - - - - - - -@frame: -@Returns: - - diff --git a/docs/reference/gdk-pixbuf/tmpl/gdk-pixbuf-loader.sgml b/docs/reference/gdk-pixbuf/tmpl/gdk-pixbuf-loader.sgml index b2f07bd0ef..acdebe537c 100644 --- a/docs/reference/gdk-pixbuf/tmpl/gdk-pixbuf-loader.sgml +++ b/docs/reference/gdk-pixbuf/tmpl/gdk-pixbuf-loader.sgml @@ -132,15 +132,6 @@ Application-driven progressive image loading. @Returns: - - - This signal is emitted when an animation is done loading. - - -@gdkpixbufloader: the object which received the signal. - -@loader: Loader which emitted the signal. - This signal is emitted when the pixbuf loader has been fed the @@ -194,15 +185,3 @@ sgml-parent-document: ("../gdk-pixbuf.sgml" "book" "refsect2" "") End: --> - - - This signal is emitted when a frame is done loading. It will be - emitted for each frame in an animation data stream. - - -@gdkpixbufloader: the object which received the signal. -@arg1: - -@loader: Loader which emitted the signal. -@frame: Frame which just completed loading. - diff --git a/docs/reference/gdk-pixbuf/tmpl/module_interface.sgml b/docs/reference/gdk-pixbuf/tmpl/module_interface.sgml index 563fabb57e..139ee467d3 100644 --- a/docs/reference/gdk-pixbuf/tmpl/module_interface.sgml +++ b/docs/reference/gdk-pixbuf/tmpl/module_interface.sgml @@ -28,6 +28,7 @@ Module Interface @pixbuf: +@anim: @user_data: @@ -44,24 +45,6 @@ Module Interface @user_data: - - - - - -@frame: -@user_data: - - - - - - - -@pixbuf: -@user_data: - - diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index ddd6cd94fb..4e5b743ff3 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -967,6 +967,7 @@ GTK_HSEPARATOR_GET_CLASS gtkimage GtkImage GtkImage +GtkImageType gtk_image_get_icon_set gtk_image_get_image gtk_image_get_pixbuf @@ -1002,7 +1003,6 @@ GtkImageImageData GtkImagePixbufData GtkImagePixmapData GtkImageStockData -GtkImageType
diff --git a/docs/reference/gtk/tmpl/gtk-unused.sgml b/docs/reference/gtk/tmpl/gtk-unused.sgml index 4cb131d8ce..20397f167d 100644 --- a/docs/reference/gtk/tmpl/gtk-unused.sgml +++ b/docs/reference/gtk/tmpl/gtk-unused.sgml @@ -1621,19 +1621,3 @@ fundamental type. @window: @Returns: - - - - - -@window: -@decorations: - - - - - - -@window: -@functions: - diff --git a/docs/reference/gtk/tmpl/gtkimage.sgml b/docs/reference/gtk/tmpl/gtkimage.sgml index 6f8bfdd021..67e5d22ae6 100644 --- a/docs/reference/gtk/tmpl/gtkimage.sgml +++ b/docs/reference/gtk/tmpl/gtkimage.sgml @@ -28,6 +28,24 @@ below. + + +Describes the representation stored by a #GtkImage. If you want to get the image +from the widget, you can only get the currently-stored representation. e.g. if +the gtk_image_get_storage_type() returns #GTK_IMAGE_PIXBUF, then you can call +gtk_image_get_pixbuf() but not gtk_image_get_stock(). For empty images, you can +request any storage type (call any of the "get" functions), but they will all +return %NULL values. + + +@GTK_IMAGE_EMPTY: there is no image displayed by the widget +@GTK_IMAGE_PIXMAP: the widget contains a #GdkPixmap +@GTK_IMAGE_IMAGE: the widget contains a #GdkImage +@GTK_IMAGE_PIXBUF: the widget contains a #GdkPixbuf +@GTK_IMAGE_STOCK: the widget contains a stock icon name +@GTK_IMAGE_ICON_SET: the widget contains a #GtkIconSet +@GTK_IMAGE_ANIMATION: + diff --git a/docs/reference/gtk/tmpl/gtkrc.sgml b/docs/reference/gtk/tmpl/gtkrc.sgml index 178b403922..88a0a4a507 100644 --- a/docs/reference/gtk/tmpl/gtkrc.sgml +++ b/docs/reference/gtk/tmpl/gtkrc.sgml @@ -387,7 +387,6 @@ can define other sizes. It's also possible to use custom icons for a given state, for example: -You can specify custom icons for specific sizes, as follows: stock["my-stock-item"] = { @@ -496,6 +495,7 @@ This can later be composited together with other #GtkRcStyle structures to form a #GtkStyle. +@parent_instance: @name: @bg_pixmap_name: @font_desc: diff --git a/docs/reference/gtk/tmpl/gtkscale.sgml b/docs/reference/gtk/tmpl/gtkscale.sgml index 877a40d09b..3df9530673 100644 --- a/docs/reference/gtk/tmpl/gtkscale.sgml +++ b/docs/reference/gtk/tmpl/gtkscale.sgml @@ -43,7 +43,9 @@ slider. -Sets the number of decimal places that are displayed in the value. +Sets the number of decimal places that are displayed in the value. Also causes +the value of the adjustment to be rounded off to this number of digits, so the +retrieved value matches the value the user saw. @scale: a #GtkScale. @@ -80,6 +82,15 @@ string. @Returns: the maximum width needed to display the value string. + + + + + +@scale: the object which received the signal. +@arg1: +@Returns: + The number of decimal places that are displayed in the value. diff --git a/docs/reference/gtk/tmpl/gtksignal.sgml b/docs/reference/gtk/tmpl/gtksignal.sgml index 1a5a4f3e27..52c116c266 100644 --- a/docs/reference/gtk/tmpl/gtksignal.sgml +++ b/docs/reference/gtk/tmpl/gtksignal.sgml @@ -289,7 +289,7 @@ you don't want a return value. the callbacks. - + Given the name of the signal and the type of object it connects to, get the signal's identifying integer. Emitting the signal @@ -304,7 +304,7 @@ It also tries the ancestors of the given type. @Returns: the signal's identifying number, or 0 if no signal was found. - + Given the signal's identifier, find its name. @@ -381,7 +381,7 @@ an array of GtkArgs instead of using C's varargs mechanism. followed by one which is a pointer to the return type. - + This function aborts a signal's current emission. @@ -415,7 +415,7 @@ except it will lookup the signal id for you. @name: the name of the signal you wish to stop. - + Attach a function pointer and user data to a signal for a particular object. @@ -467,7 +467,7 @@ is getting pressed, this is that button. @d: - + Attach a function pointer and user data to a signal so that this handler will be called after the other handlers. @@ -485,7 +485,7 @@ so that this handler will be called after the other handlers. @d: - + This function is for registering a callback that will call another object's callback. That is, @@ -520,7 +520,7 @@ really pass any gpointer as the #slot_object .) @d: - + Attach a signal hook to a signal, passing in an alternate object as the first parameter, and guaranteeing @@ -626,7 +626,7 @@ should signal the removal of this signal. @name: name of the signal. - + Destroy a user-defined handler connection. @@ -635,7 +635,7 @@ Destroy a user-defined handler connection. @handler_id: the connection id. - + Destroy all connections for a particular object, with the given function-pointer and user-data. @@ -650,7 +650,7 @@ the given function-pointer and user-data. @d: - + Destroy all connections for a particular object, with the given user-data. @@ -663,7 +663,7 @@ the given user-data. @d: - + Prevent an user-defined handler from being invoked. All other signal processing will go on as normal, but this particular @@ -674,7 +674,7 @@ handler will ignore it. @handler_id: the connection id. - + Prevent a user-defined handler from being invoked, by reference to the user-defined handler's function pointer and user data. (It may result in @@ -690,7 +690,7 @@ multiple hooks being blocked, if you've called connect multiple times.) @d: - + Prevent all user-defined handlers with a certain user data from being invoked. @@ -702,7 +702,7 @@ Prevent all user-defined handlers with a certain user data from being invoked. @d: - + Undo a block, by connection id. Note that undoing a block doesn't necessarily make the hook callable, because if you block a @@ -714,7 +714,7 @@ hook twice, you must unblock it twice. gtk_signal_connect(), etc. - + Undo a block, by function pointer and data. Note that undoing a block doesn't @@ -731,7 +731,7 @@ hook twice, you must unblock it twice. @d: - + Undo block(s), to all signals for a particular object with a particular user-data pointer @@ -744,7 +744,7 @@ with a particular user-data pointer @d: - + Returns a connection id corresponding to a given signal id and object. @@ -766,7 +766,7 @@ handler. @b: - + Returns a connection id corresponding to a given signal id, object, function pointer and user data. diff --git a/docs/reference/gtk/tmpl/gtkstyle.sgml b/docs/reference/gtk/tmpl/gtkstyle.sgml index a78f5ec008..73abe4c49f 100644 --- a/docs/reference/gtk/tmpl/gtkstyle.sgml +++ b/docs/reference/gtk/tmpl/gtkstyle.sgml @@ -41,6 +41,7 @@ Styles @mid: @text: @base: +@text_aa: @black: @white: @font: @@ -54,6 +55,7 @@ Styles @mid_gc: @text_gc: @base_gc: +@text_aa_gc: @black_gc: @white_gc: @bg_pixmap: @@ -504,6 +506,7 @@ Styles @style: @window: @state_type: +@use_text: @x: @y: @layout: @@ -874,6 +877,7 @@ Styles @style: @window: @state_type: +@use_text: @area: @widget: @detail: diff --git a/docs/reference/gtk/tmpl/gtktextiter.sgml b/docs/reference/gtk/tmpl/gtktextiter.sgml index 1615165632..a7d68099e0 100644 --- a/docs/reference/gtk/tmpl/gtktextiter.sgml +++ b/docs/reference/gtk/tmpl/gtktextiter.sgml @@ -378,15 +378,6 @@ types related to the text widget and how they work together. @Returns: - - - - - -@iter: -@Returns: - - diff --git a/docs/reference/gtk/tmpl/gtktexttag.sgml b/docs/reference/gtk/tmpl/gtktexttag.sgml index be71c7e39d..7cff611ab8 100644 --- a/docs/reference/gtk/tmpl/gtktexttag.sgml +++ b/docs/reference/gtk/tmpl/gtktexttag.sgml @@ -58,6 +58,7 @@ Describes a type of line wrapping. @justification: @direction: @font: +@font_scale: @left_margin: @indent: @right_margin: @@ -279,6 +280,11 @@ Font as a #PangoFontDescription. + + + + + @@ -419,6 +425,11 @@ applies to the first character in a paragraph. + + + + + diff --git a/docs/reference/gtk/tmpl/gtktypeutils.sgml b/docs/reference/gtk/tmpl/gtktypeutils.sgml index 6295e2dfa1..e6a3ad7abf 100644 --- a/docs/reference/gtk/tmpl/gtktypeutils.sgml +++ b/docs/reference/gtk/tmpl/gtktypeutils.sgml @@ -560,7 +560,7 @@ Create a new, unique type. @type_info: must not be null, and @type_info->type_name must also not be null. - + @@ -568,7 +568,7 @@ Create a new, unique type. @Returns: a pointer to the name of a type, or NULL if it has none. - + Get the internal representation of a type given its name. @@ -577,7 +577,7 @@ Get the internal representation of a type given its name. @Returns: a GtkType - + @@ -608,7 +608,7 @@ has all the proper initializers called. @Returns: gpointer to a GtkTypeObject - + Look in the type hierarchy to see if @type has @is_a_type among its ancestors. Do so with a simple lookup, not a loop. diff --git a/docs/reference/gtk/tmpl/gtkwindow.sgml b/docs/reference/gtk/tmpl/gtkwindow.sgml index 6f4ab3766a..adab57e9f5 100644 --- a/docs/reference/gtk/tmpl/gtkwindow.sgml +++ b/docs/reference/gtk/tmpl/gtkwindow.sgml @@ -393,6 +393,15 @@ it's larger @setting: + + + + + +@window: +@decorations: + + @@ -405,6 +414,15 @@ it's larger @bottom: + + + + + +@window: +@functions: + + diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 9dea2a36a3..f00bda8f65 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,32 @@ +2001-05-04 Havoc Pennington + + * pixops/pixops.c (pixops_process): merge fix from stable: Patch + from hoshem@mel.comcen.com.au to fix nonzero X offsets. Fixes + bug #50371. + + * gdk-pixbuf/pixops/pixops.c (pixops_composite_nearest): merge + from stable: Patch from OKADA Mitsuru to fix + confusion of using "src" instead of "p". + (pixops_composite_color_nearest): Use a more accurate (and + correct, to begin with) compositing method. This cures checks + showing through on images with no alpha. + + * gdk-pixbuf.c (gdk_pixbuf_fill): fix bug that left some trailing + bytes unfilled. + + * gdk-pixbuf-io.h: fix UpdatedNotifyFunc to use signed ints + + * gdk-pixbuf-loader.h (struct _GdkPixbufLoaderClass): Change + area_updated signal to use signed ints. Removed animation-related + signals. + + * io-gif.c, io-gif-animation.h, io-gif-animation.c: Massive + rewrite action + + * gdk-pixbuf-animation.c: Add GdkPixbufAnimationIter to abstract + all the pesky details. Remove old frame-based API. Make + GdkPixbufAnimation an abstract base class, derived by the loaders. + Sun Apr 22 15:51:32 2001 Owen Taylor * Makefile.am (LDADDS): Add $(MATH_LIB). diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index c8ba6e0883..b006435ccd 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -32,8 +32,8 @@ libpixbufloader_xpm_la_LIBADD = # # The GIF loader # -libpixbufloader_static_gif_la_SOURCES = io-gif.c -libpixbufloader_gif_la_SOURCES = io-gif.c +libpixbufloader_static_gif_la_SOURCES = io-gif.c io-gif-animation.c io-gif-animation.h +libpixbufloader_gif_la_SOURCES = io-gif.c io-gif-animation.c io-gif-animation.h libpixbufloader_gif_la_LDFLAGS = -avoid-version -module libpixbufloader_gif_la_LIBADD = diff --git a/gdk-pixbuf/gdk-pixbuf-animation.c b/gdk-pixbuf/gdk-pixbuf-animation.c index cc32559048..274813c503 100644 --- a/gdk-pixbuf/gdk-pixbuf-animation.c +++ b/gdk-pixbuf/gdk-pixbuf-animation.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - Simple animation support * * Copyright (C) 1999 The Free Software Foundation @@ -26,12 +27,59 @@ #include "gdk-pixbuf-io.h" #include "gdk-pixbuf-private.h" -static void gdk_pixbuf_animation_class_init (GdkPixbufAnimationClass *klass); -static void gdk_pixbuf_animation_finalize (GObject *object); +typedef struct _GdkPixbufNonAnim GdkPixbufNonAnim; +typedef struct _GdkPixbufNonAnimClass GdkPixbufNonAnimClass; + +#define GDK_TYPE_PIXBUF_NON_ANIM (gdk_pixbuf_non_anim_get_type ()) +#define GDK_PIXBUF_NON_ANIM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnim)) +#define GDK_IS_PIXBUF_NON_ANIM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM)) + +#define GDK_PIXBUF_NON_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass)) +#define GDK_IS_PIXBUF_NON_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM)) +#define GDK_PIXBUF_NON_ANIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass)) + +/* Private part of the GdkPixbufNonAnim structure */ +struct _GdkPixbufNonAnim { + GdkPixbufAnimation parent_instance; + + GdkPixbuf *pixbuf; +}; + +struct _GdkPixbufNonAnimClass { + GdkPixbufAnimationClass parent_class; + +}; + +static GType gdk_pixbuf_non_anim_get_type (void) G_GNUC_CONST; -static gpointer parent_class; +typedef struct _GdkPixbufNonAnimIter GdkPixbufNonAnimIter; +typedef struct _GdkPixbufNonAnimIterClass GdkPixbufNonAnimIterClass; + + +#define GDK_TYPE_PIXBUF_NON_ANIM_ITER (gdk_pixbuf_non_anim_iter_get_type ()) +#define GDK_PIXBUF_NON_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIter)) +#define GDK_IS_PIXBUF_NON_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER)) + +#define GDK_PIXBUF_NON_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass)) +#define GDK_IS_PIXBUF_NON_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER)) +#define GDK_PIXBUF_NON_ANIM_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass)) + +struct _GdkPixbufNonAnimIter { + GdkPixbufAnimationIter parent_instance; + + GdkPixbufNonAnim *non_anim; +}; + +struct _GdkPixbufNonAnimIterClass { + GdkPixbufAnimationIterClass parent_class; + +}; + +static GType gdk_pixbuf_non_anim_iter_get_type (void) G_GNUC_CONST; + + GType gdk_pixbuf_animation_get_type (void) @@ -43,7 +91,7 @@ gdk_pixbuf_animation_get_type (void) sizeof (GdkPixbufAnimationClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gdk_pixbuf_animation_class_init, + (GClassInitFunc) NULL, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GdkPixbufAnimation), @@ -59,35 +107,6 @@ gdk_pixbuf_animation_get_type (void) return object_type; } -static void -gdk_pixbuf_animation_class_init (GdkPixbufAnimationClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - object_class->finalize = gdk_pixbuf_animation_finalize; -} - -static void -gdk_pixbuf_animation_finalize (GObject *object) -{ - GdkPixbufAnimation *animation = GDK_PIXBUF_ANIMATION (object); - - GList *l; - GdkPixbufFrame *frame; - - for (l = animation->frames; l; l = l->next) { - frame = l->data; - gdk_pixbuf_unref (frame->pixbuf); - g_free (frame); - } - - g_list_free (animation->frames); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - /** @@ -154,7 +173,6 @@ gdk_pixbuf_animation_new_from_file (const char *filename, if (image_module->load_animation == NULL) { GdkPixbuf *pixbuf; - GdkPixbufFrame *frame; /* Keep this logic in sync with gdk_pixbuf_new_from_file() */ @@ -191,20 +209,9 @@ gdk_pixbuf_animation_new_from_file (const char *filename, if (pixbuf == NULL) return NULL; - frame = g_new (GdkPixbufFrame, 1); - frame->pixbuf = pixbuf; - g_object_ref (G_OBJECT (frame->pixbuf)); - frame->x_offset = 0; - frame->y_offset = 0; - frame->delay_time = -1; - frame->action = GDK_PIXBUF_FRAME_RETAIN; + animation = gdk_pixbuf_non_anim_new (pixbuf); - animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - animation->n_frames = 1; - animation->frames = g_list_prepend (NULL, frame); - animation->width = gdk_pixbuf_get_width (pixbuf); - animation->height = gdk_pixbuf_get_height (pixbuf); + g_object_unref (G_OBJECT (pixbuf)); } else { fseek (f, 0, SEEK_SET); animation = (* image_module->load_animation) (f, error); @@ -261,6 +268,46 @@ gdk_pixbuf_animation_unref (GdkPixbufAnimation *animation) g_object_unref (G_OBJECT (animation)); } +/** + * gdk_pixbuf_animation_is_static_image: + * @animation: a #GdkPixbufAnimation + * + * If you load a file with gdk_pixbuf_animation_new_from_file() and it turns + * out to be a plain, unanimated image, then this function will return + * %TRUE. Use gdk_pixbuf_animation_get_static_image() to retrieve + * the image. + * + * Return value: %TRUE if the "animation" was really just an image + **/ +gboolean +gdk_pixbuf_animation_is_static_image (GdkPixbufAnimation *animation) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), FALSE); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->is_static_image (animation); +} + +/** + * gdk_pixbuf_animation_get_static_image: + * @animation: a #GdkPixbufAnimation + * + * If an animation is really just a plain image (has only one frame), + * this function returns that image. If the animation is an animation, + * this function returns a reasonable thing to display as a static + * unanimated image, which might be the first frame, or something more + * sophisticated. If an animation hasn't loaded any frames yet, this + * function will return %NULL. + * + * Return value: unanimated image representing the animation + **/ +GdkPixbuf* +gdk_pixbuf_animation_get_static_image (GdkPixbufAnimation *animation) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_static_image (animation); +} + /** * gdk_pixbuf_animation_get_width: * @animation: An animation. @@ -272,9 +319,16 @@ gdk_pixbuf_animation_unref (GdkPixbufAnimation *animation) int gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation) { - g_return_val_if_fail (animation != NULL, -1); + int width; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0); - return animation->width; + width = 0; + GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation, + &width, NULL); + + + return width; } /** @@ -288,179 +342,444 @@ gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation) int gdk_pixbuf_animation_get_height (GdkPixbufAnimation *animation) { - g_return_val_if_fail (animation != NULL, -1); + int height; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0); - return animation->height; + height = 0; + GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation, + NULL, &height); + + + return height; } -/** - * gdk_pixbuf_animation_get_num_frames: - * @animation: An animation. - * - * Queries the number of frames in a pixbuf animation. - * - * Return value: Number of frames in the animation. - **/ -int -gdk_pixbuf_animation_get_num_frames (GdkPixbufAnimation *animation) -{ - g_return_val_if_fail (animation != NULL, -1); - - return animation->n_frames; -} /** - * gdk_pixbuf_animation_get_frames: - * @animation: An animation. - * - * Queries the list of frames of an animation. + * gdk_pixbuf_animation_get_iter: + * @animation: a #GdkPixbufAnimation + * @start_time: time when the animation starts playing * - * Return value: List of frames in the animation; this is a #GList of - * #GdkPixbufFrame structures. + * Get an iterator for displaying an animation. The iterator provides + * the frames that should be displayed at a given time. + * It should be freed after use with g_object_unref(). + * + * @start_time would normally come from g_get_current_time(), and + * marks the beginning of animation playback. After creating an + * iterator, you should immediately display the pixbuf returned by + * gdk_pixbuf_animation_iter_get_pixbuf(). Then, you should install a + * timeout (with g_timeout_add()) or by some other mechanism ensure + * that you'll update the image after + * gdk_pixbuf_animation_iter_get_delay_time() milliseconds. Each time + * the image is updated, you should reinstall the timeout with the new, + * possibly-changed delay time. + * + * As a shortcut, if @start_time is %NULL, the result of + * g_get_current_time() will be used automatically. + * + * To update the image (i.e. possibly change the result of + * gdk_pixbuf_animation_iter_get_pixbuf() to a new frame of the animation), + * call gdk_pixbuf_animation_iter_advance(). + * + * If you're using #GdkPixbufLoader, in addition to updating the image + * after the delay time, you should also update it whenever you + * receive the area_updated signal and + * gdk_pixbuf_animation_iter_on_currently_loading_frame() returns + * %TRUE. In this case, the frame currently being fed into the loader + * has received new data, so needs to be refreshed. The delay time for + * a frame may also be modified after an area_updated signal, for + * example if the delay time for a frame is encoded in the data after + * the frame itself. So your timeout should be reinstalled after any + * area_updated signal. + * + * A delay time of -1 is possible, indicating "infinite." + * + * Return value: an iterator to move over the animation **/ -GList * -gdk_pixbuf_animation_get_frames (GdkPixbufAnimation *animation) +GdkPixbufAnimationIter* +gdk_pixbuf_animation_get_iter (GdkPixbufAnimation *animation, + const GTimeVal *start_time) { - g_return_val_if_fail (animation != NULL, NULL); + GTimeVal val; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL); - return animation->frames; + + if (start_time) + val = *start_time; + else + g_get_current_time (&val); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_iter (animation, &val); } -/** - * gdk_pixbuf_frame_get_pixbuf: - * @frame: A pixbuf animation frame. - * - * Queries the pixbuf of an animation frame. - * - * Return value: A pixbuf. - **/ -GdkPixbuf * -gdk_pixbuf_frame_get_pixbuf (GdkPixbufFrame *frame) +GType +gdk_pixbuf_animation_iter_get_type (void) { - g_return_val_if_fail (frame != NULL, NULL); + static GType object_type = 0; - return frame->pixbuf; -} - -/** - * gdk_pixbuf_frame_get_x_offset: - * @frame: A pixbuf animation frame. - * - * Queries the X offset of an animation frame. - * - * Return value: X offset from the top left corner of the animation. - **/ -int -gdk_pixbuf_frame_get_x_offset (GdkPixbufFrame *frame) -{ - g_return_val_if_fail (frame != NULL, -1); - - return frame->x_offset; -} - -/** - * gdk_pixbuf_frame_get_y_offset: - * @frame: A pixbuf animation frame. - * - * Queries the Y offset of an animation frame. - * - * Return value: Y offset from the top left corner of the animation. - **/ -int -gdk_pixbuf_frame_get_y_offset (GdkPixbufFrame *frame) -{ - g_return_val_if_fail (frame != NULL, -1); - - return frame->y_offset; -} - -/** - * gdk_pixbuf_frame_get_delay_time: - * @frame: A pixbuf animation frame. - * - * Queries the delay time in milliseconds of an animation frame. - * - * Return value: Delay time in milliseconds. - **/ -int -gdk_pixbuf_frame_get_delay_time (GdkPixbufFrame *frame) -{ - g_return_val_if_fail (frame != NULL, -1); - - return frame->delay_time; -} - -/** - * gdk_pixbuf_frame_get_action: - * @frame: A pixbuf animation frame. - * - * Queries the overlay action of an animation frame. - * - * Return value: Overlay action for this frame. - **/ -GdkPixbufFrameAction -gdk_pixbuf_frame_get_action (GdkPixbufFrame *frame) -{ - g_return_val_if_fail (frame != NULL, GDK_PIXBUF_FRAME_RETAIN); - - return frame->action; -} - -/** - * gdk_pixbuf_frame_copy: - * @src: a #GdkPixbufFrame to copy - * - * Copies a #GdkPixbufFrame. Free the result - * with gdk_pixbuf_frame_free(). - * - * Return value: a new #GdkPixbufFrame - **/ -GdkPixbufFrame* -gdk_pixbuf_frame_copy (GdkPixbufFrame *src) -{ - GdkPixbufFrame *frame; - - frame = g_new (GdkPixbufFrame, 1); - frame->pixbuf = src->pixbuf; - g_object_ref (G_OBJECT (frame->pixbuf)); - frame->x_offset = src->x_offset; - frame->y_offset = src->y_offset; - frame->delay_time = src->delay_time; - frame->action = src->action; + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufAnimationIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufAnimationIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, + "GdkPixbufAnimationIter", + &object_info, 0); + } - return frame; + return object_type; } /** - * gdk_pixbuf_frame_free: - * @frame: a #GdkPixbufFrame + * gdk_pixbuf_animation_iter_get_delay_time: + * @iter: an animation iterator * - * Frees a #GdkPixbufFrame. Don't do this with frames you got from - * #GdkPixbufAnimation, usually the animation owns those (it doesn't - * make a copy before returning the frame). + * Gets the number of milliseconds the current pixbuf should be displayed, + * or -1 if the current pixbuf should be displayed forever. g_timeout_add() + * conveniently takes a timeout in milliseconds, so you can use a timeout + * to schedule the next update. + * + * Return value: delay time in milliseconds (thousandths of a second) **/ -void -gdk_pixbuf_frame_free (GdkPixbufFrame *frame) +int +gdk_pixbuf_animation_iter_get_delay_time (GdkPixbufAnimationIter *iter) { - g_return_if_fail (frame != NULL); - - g_object_unref (G_OBJECT (frame->pixbuf)); - g_free (frame); + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), -1); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_delay_time (iter); } +/** + * gdk_pixbuf_animation_iter_get_pixbuf: + * @iter: an animation iterator + * + * Gets the current pixbuf which should be displayed; the pixbuf will + * be the same size as the animation itself + * (gdk_pixbuf_animation_get_width(), + * gdk_pixbuf_animation_get_height()). This pixbuf should be displayed + * for gdk_pixbuf_animation_get_delay_time() milliseconds. The caller + * of this function does not own a reference to the returned pixbuf; + * the returned pixbuf will become invalid when the iterator advances + * to the next frame, which may happen anytime you call + * gdk_pixbuf_animation_iter_advance(). Copy the pixbuf to keep it + * (don't just add a reference), as it may get recycled as you advance + * the iterator. + * + * Return value: the pixbuf to be displayed + **/ +GdkPixbuf* +gdk_pixbuf_animation_iter_get_pixbuf (GdkPixbufAnimationIter *iter) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), NULL); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_pixbuf (iter); +} + +/** + * gdk_pixbuf_animation_iter_on_currently_loading_frame: + * @iter: a #GdkPixbufAnimationIter + * + * Used to determine how to respond to the area_updated signal on + * #GdkPixbufLoader when loading an animation. area_updated is emitted + * for an area of the frame currently streaming in to the loader. So if + * you're on the currently loading frame, you need to redraw the screen for + * the updated area. + * + * Return value: %TRUE if the frame we're on is partially loaded, or the last frame + **/ +gboolean +gdk_pixbuf_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->on_currently_loading_frame (iter); +} + +/** + * gdk_pixbuf_animation_iter_advance: + * @iter: a #GdkPixbufAnimationIter + * @current_time: current time + * + * Possibly advances an animation to a new frame. Chooses the frame based + * on the start time passed to gdk_pixbuf_animation_get_iter(). + * + * @current_time would normally come from g_get_current_time(), and + * must be greater than or equal to the time passed to + * gdk_pixbuf_animation_get_iter(), and must increase or remain + * unchanged each time gdk_pixbuf_animation_iter_get_pixbuf() is + * called. That is, you can't go backward in time; animations only + * play forward. + * + * As a shortcut, pass %NULL for the current time and g_get_current_time() + * will be invoked on your behalf. So you only need to explicitly pass + * @current_time if you're doing something odd like playing the animation + * at double speed. + * + * If this function returns %FALSE, there's no need to update the animation + * display, assuming the display had been rendered prior to advancing; + * if %TRUE, you need to call gdk_animation_iter_get_pixbuf() and update the + * display with the new pixbuf. + * + * Returns: %TRUE if the image may need updating + * + **/ +gboolean +gdk_pixbuf_animation_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time) +{ + GTimeVal val; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE); + + if (current_time) + val = *current_time; + else + g_get_current_time (&val); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->advance (iter, &val); +} + + + +static void gdk_pixbuf_non_anim_class_init (GdkPixbufNonAnimClass *klass); +static void gdk_pixbuf_non_anim_finalize (GObject *object); + +static gboolean gdk_pixbuf_non_anim_is_static_image (GdkPixbufAnimation *animation); +static GdkPixbuf* gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation); +static void gdk_pixbuf_non_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height); +static GdkPixbufAnimationIter* gdk_pixbuf_non_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time); + + + + + +static gpointer non_parent_class; + GType -gdk_pixbuf_frame_get_type (void) +gdk_pixbuf_non_anim_get_type (void) { - static GType our_type = 0; + static GType object_type = 0; - if (our_type == 0) - our_type = g_boxed_type_register_static ("GdkPixbufFrame", - NULL, - gdk_pixbuf_frame_copy, - gdk_pixbuf_frame_free, - FALSE); - - return our_type; + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufNonAnimClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_non_anim_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufNonAnim), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION, + "GdkPixbufNonAnim", + &object_info, 0); + } + + return object_type; } + +static void +gdk_pixbuf_non_anim_class_init (GdkPixbufNonAnimClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass); + + non_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_non_anim_finalize; + + anim_class->is_static_image = gdk_pixbuf_non_anim_is_static_image; + anim_class->get_static_image = gdk_pixbuf_non_anim_get_static_image; + anim_class->get_size = gdk_pixbuf_non_anim_get_size; + anim_class->get_iter = gdk_pixbuf_non_anim_get_iter; +} + +static void +gdk_pixbuf_non_anim_finalize (GObject *object) +{ + GdkPixbufNonAnim *non_anim = GDK_PIXBUF_NON_ANIM (object); + + if (non_anim->pixbuf) + g_object_unref (G_OBJECT (non_anim->pixbuf)); + + G_OBJECT_CLASS (non_parent_class)->finalize (object); +} + +GdkPixbufAnimation* +gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM, NULL); + + non_anim->pixbuf = pixbuf; + + if (pixbuf) + g_object_ref (G_OBJECT (pixbuf)); + + return GDK_PIXBUF_ANIMATION (non_anim); +} + +static gboolean +gdk_pixbuf_non_anim_is_static_image (GdkPixbufAnimation *animation) +{ + + return TRUE; +} + +static GdkPixbuf* +gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = GDK_PIXBUF_NON_ANIM (animation); + + return non_anim->pixbuf; +} + +static void +gdk_pixbuf_non_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = GDK_PIXBUF_NON_ANIM (anim); + + if (width) + *width = gdk_pixbuf_get_width (non_anim->pixbuf); + + if (height) + *height = gdk_pixbuf_get_height (non_anim->pixbuf); +} + + +static GdkPixbufAnimationIter* +gdk_pixbuf_non_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time) +{ + GdkPixbufNonAnimIter *iter; + + iter = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM_ITER, NULL); + + iter->non_anim = GDK_PIXBUF_NON_ANIM (anim); + + g_object_ref (G_OBJECT (iter->non_anim)); + + return GDK_PIXBUF_ANIMATION_ITER (iter); +} + + + +static void gdk_pixbuf_non_anim_iter_class_init (GdkPixbufNonAnimIterClass *klass); +static void gdk_pixbuf_non_anim_iter_finalize (GObject *object); +static int gdk_pixbuf_non_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter); +static GdkPixbuf* gdk_pixbuf_non_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_non_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); + + + + + +static gpointer non_iter_parent_class; + +GType +gdk_pixbuf_non_anim_iter_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufNonAnimIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_non_anim_iter_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufNonAnimIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER, + "GdkPixbufNonAnimIter", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_non_anim_iter_class_init (GdkPixbufNonAnimIterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationIterClass *anim_iter_class = + GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); + + non_iter_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_non_anim_iter_finalize; + + anim_iter_class->get_delay_time = gdk_pixbuf_non_anim_iter_get_delay_time; + anim_iter_class->get_pixbuf = gdk_pixbuf_non_anim_iter_get_pixbuf; + anim_iter_class->on_currently_loading_frame = gdk_pixbuf_non_anim_iter_on_currently_loading_frame; + anim_iter_class->advance = gdk_pixbuf_non_anim_iter_advance; +} + +static void +gdk_pixbuf_non_anim_iter_finalize (GObject *object) +{ + GdkPixbufNonAnimIter *iter = GDK_PIXBUF_NON_ANIM_ITER (object); + + g_object_unref (G_OBJECT (iter->non_anim)); + + G_OBJECT_CLASS (non_iter_parent_class)->finalize (object); +} + +static int +gdk_pixbuf_non_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter) +{ + return -1; /* show only frame forever */ +} + +static GdkPixbuf* +gdk_pixbuf_non_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter) +{ + return GDK_PIXBUF_NON_ANIM_ITER (iter)->non_anim->pixbuf; +} + + +static gboolean +gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) +{ + return TRUE; +} + +static gboolean +gdk_pixbuf_non_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time) +{ + + /* Advancing never requires a refresh */ + return FALSE; +} + diff --git a/gdk-pixbuf/gdk-pixbuf-io.h b/gdk-pixbuf/gdk-pixbuf-io.h index d4cf389fdd..b66bcaf08b 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.h +++ b/gdk-pixbuf/gdk-pixbuf-io.h @@ -39,16 +39,15 @@ extern "C" { -typedef void (* ModulePreparedNotifyFunc) (GdkPixbuf *pixbuf, gpointer user_data); -typedef void (* ModuleUpdatedNotifyFunc) (GdkPixbuf *pixbuf, - guint x, guint y, - guint width, guint height, - gpointer user_data); -/* Needed only for animated images. */ -typedef void (* ModuleFrameDoneNotifyFunc) (GdkPixbufFrame *frame, - gpointer user_data); -typedef void (* ModuleAnimationDoneNotifyFunc) (GdkPixbuf *pixbuf, - gpointer user_data); +typedef void (* ModulePreparedNotifyFunc) (GdkPixbuf *pixbuf, + GdkPixbufAnimation *anim, + gpointer user_data); +typedef void (* ModuleUpdatedNotifyFunc) (GdkPixbuf *pixbuf, + int x, + int y, + int width, + int height, + gpointer user_data); typedef struct _GdkPixbufModule GdkPixbufModule; struct _GdkPixbufModule { @@ -63,8 +62,6 @@ struct _GdkPixbufModule { gpointer (* begin_load) (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); gboolean (* stop_load) (gpointer context, diff --git a/gdk-pixbuf/gdk-pixbuf-loader.c b/gdk-pixbuf/gdk-pixbuf-loader.c index 2a8d2658e4..a9148d7fd5 100644 --- a/gdk-pixbuf/gdk-pixbuf-loader.c +++ b/gdk-pixbuf/gdk-pixbuf-loader.c @@ -35,8 +35,6 @@ enum { AREA_UPDATED, AREA_PREPARED, - FRAME_DONE, - ANIMATION_DONE, CLOSED, LAST_SIGNAL }; @@ -56,7 +54,6 @@ static guint pixbuf_loader_signals[LAST_SIGNAL] = { 0 }; typedef struct { - GdkPixbuf *pixbuf; GdkPixbufAnimation *animation; gboolean closed; guchar header_buf[LOADER_HEADER_SIZE]; @@ -136,25 +133,6 @@ gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class) G_TYPE_INT, G_TYPE_INT); - pixbuf_loader_signals[FRAME_DONE] = - g_signal_newc ("frame_done", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GdkPixbufLoaderClass, frame_done), - NULL, NULL, - gdk_pixbuf_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - GDK_TYPE_PIXBUF_FRAME); - - pixbuf_loader_signals[ANIMATION_DONE] = - g_signal_newc ("animation_done", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GdkPixbufLoaderClass, animation_done), - NULL, NULL, - gdk_pixbuf_marshal_VOID__VOID, - G_TYPE_NONE, 0); - pixbuf_loader_signals[CLOSED] = g_signal_newc ("closed", G_TYPE_FROM_CLASS (object_class), @@ -188,9 +166,6 @@ gdk_pixbuf_loader_finalize (GObject *object) if (priv->animation) gdk_pixbuf_animation_unref (priv->animation); - - if (priv->pixbuf) - gdk_pixbuf_unref (priv->pixbuf); g_free (priv); @@ -198,26 +173,30 @@ gdk_pixbuf_loader_finalize (GObject *object) } static void -gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf, - gpointer loader) +gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf, + GdkPixbufAnimation *anim, + gpointer loader) { GdkPixbufLoaderPrivate *priv = NULL; priv = GDK_PIXBUF_LOADER (loader)->priv; - gdk_pixbuf_ref (pixbuf); - g_assert (priv->pixbuf == NULL); + if (anim) + g_object_ref (G_OBJECT (anim)); + else + anim = gdk_pixbuf_non_anim_new (pixbuf); + + priv->animation = anim; - priv->pixbuf = pixbuf; g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[AREA_PREPARED], 0); } static void gdk_pixbuf_loader_update (GdkPixbuf *pixbuf, - guint x, - guint y, - guint width, - guint height, + gint x, + gint y, + gint width, + gint height, gpointer loader) { GdkPixbufLoaderPrivate *priv = NULL; @@ -229,82 +208,8 @@ gdk_pixbuf_loader_update (GdkPixbuf *pixbuf, 0, x, y, /* sanity check in here. Defend against an errant loader */ - MIN (width, gdk_pixbuf_get_width (priv->pixbuf)), - MIN (height, gdk_pixbuf_get_height (priv->pixbuf))); -} - -static void -gdk_pixbuf_loader_frame_done (GdkPixbufFrame *frame, - gpointer loader) -{ - GdkPixbufLoaderPrivate *priv = NULL; - - priv = GDK_PIXBUF_LOADER (loader)->priv; - - priv->pixbuf = NULL; - - if (priv->animation == NULL) - { - priv->animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - priv->animation->n_frames = 0; - priv->animation->width = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - priv->animation->height = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - } - else - { - int w, h; - - /* update bbox size */ - w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - - if (w > priv->animation->width) { - priv->animation->width = w; - } - if (h > priv->animation->height) { - priv->animation->height = h; - } - } - - priv->animation->frames = g_list_append (priv->animation->frames, frame); - priv->animation->n_frames++; - g_signal_emit (G_OBJECT (loader), - pixbuf_loader_signals[FRAME_DONE], - 0, - frame); -} - -static void -gdk_pixbuf_loader_animation_done (GdkPixbuf *pixbuf, - gpointer loader) -{ - GdkPixbufLoaderPrivate *priv = NULL; - GdkPixbufFrame *frame; - GList *current = NULL; - gint h, w; - - priv = GDK_PIXBUF_LOADER (loader)->priv; - priv->pixbuf = NULL; - - current = gdk_pixbuf_animation_get_frames (priv->animation); - - while (current) - { - frame = (GdkPixbufFrame *) current->data; - - /* update bbox size */ - w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - - if (w > priv->animation->width) - priv->animation->width = w; - if (h > priv->animation->height) - priv->animation->height = h; - current = current->next; - } - - g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[ANIMATION_DONE], 0); + MIN (width, gdk_pixbuf_animation_get_width (priv->animation)), + MIN (height, gdk_pixbuf_animation_get_height (priv->animation))); } static gint @@ -353,8 +258,6 @@ gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader, priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_prepare, gdk_pixbuf_loader_update, - gdk_pixbuf_loader_frame_done, - gdk_pixbuf_loader_animation_done, loader, error); @@ -450,7 +353,7 @@ gdk_pixbuf_loader_write (GdkPixbufLoader *loader, { gint eaten; - eaten = gdk_pixbuf_loader_eat_header_write(loader, buf, count, error); + eaten = gdk_pixbuf_loader_eat_header_write (loader, buf, count, error); if (eaten <= 0) return FALSE; @@ -540,13 +443,14 @@ gdk_pixbuf_loader_new_with_type (const char *image_type, * "area_prepared" signal has been emitted by the loader; this means * that enough data has been read to know the size of the image that * will be allocated. If the loader has not received enough data via - * gdk_pixbuf_loader_write(), then this function returns NULL. The + * gdk_pixbuf_loader_write(), then this function returns %NULL. The * returned pixbuf will be the same in all future calls to the loader, * so simply calling gdk_pixbuf_ref() should be sufficient to continue * using it. Additionally, if the loader is an animation, it will - * return the first frame of the animation. + * return the "static image" of the animation + * (see gdk_pixbuf_animation_get_static_image()). * - * Return value: The GdkPixbuf that the loader is creating, or NULL if not + * Return value: The #GdkPixbuf that the loader is creating, or %NULL if not * enough data has been read to determine how to create the image buffer. **/ GdkPixbuf * @@ -560,19 +464,9 @@ gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader) priv = loader->priv; if (priv->animation) - { - GList *list; - - list = gdk_pixbuf_animation_get_frames (priv->animation); - if (list != NULL) - { - GdkPixbufFrame *frame = list->data; - - return gdk_pixbuf_frame_get_pixbuf (frame); - } - } - - return priv->pixbuf; + return gdk_pixbuf_animation_get_static_image (priv->animation); + else + return NULL; } /** @@ -581,8 +475,9 @@ gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader) * * Queries the GdkPixbufAnimation that a pixbuf loader is currently creating. * In general it only makes sense to call this function afer the "area_prepared" - * signal has been emitted by the loader. If the image is not an animation, - * then it will return NULL. + * signal has been emitted by the loader. If the loader doesn't have enough + * bytes yet (hasn't emitted the area_prepared signal) this function will return + * %NULL. * * Return value: The GdkPixbufAnimation that the loader is loading, or NULL if not enough data has been read to determine the information. diff --git a/gdk-pixbuf/gdk-pixbuf-loader.h b/gdk-pixbuf/gdk-pixbuf-loader.h index 9c47478a22..eea72f30d8 100644 --- a/gdk-pixbuf/gdk-pixbuf-loader.h +++ b/gdk-pixbuf/gdk-pixbuf-loader.h @@ -51,21 +51,21 @@ typedef struct _GdkPixbufLoaderClass GdkPixbufLoaderClass; struct _GdkPixbufLoaderClass { GObjectClass parent_class; - - void (*area_prepared) (GdkPixbufLoader *loader); - void (*area_updated) (GdkPixbufLoader *loader, - guint x, - guint y, - guint width, - guint height); - void (*frame_done) (GdkPixbufLoader *loader, - GdkPixbufFrame *frame); - void (*animation_done) (GdkPixbufLoader *loader); - void (*closed) (GdkPixbufLoader *loader); + + void (*area_prepared) (GdkPixbufLoader *loader); + + /* Last known frame needs a redraw for x, y, width, height */ + void (*area_updated) (GdkPixbufLoader *loader, + int x, + int y, + int width, + int height); + + void (*closed) (GdkPixbufLoader *loader); }; -GType gdk_pixbuf_loader_get_type (void) G_GNUC_CONST; +GType gdk_pixbuf_loader_get_type (void) G_GNUC_CONST; GdkPixbufLoader * gdk_pixbuf_loader_new (void); GdkPixbufLoader * gdk_pixbuf_loader_new_with_type (const char *image_type, GError **error); diff --git a/gdk-pixbuf/gdk-pixbuf-private.h b/gdk-pixbuf/gdk-pixbuf-private.h index e8eb7248a4..0bdae8715d 100644 --- a/gdk-pixbuf/gdk-pixbuf-private.h +++ b/gdk-pixbuf/gdk-pixbuf-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - Private declarations * * Copyright (C) 1999 The Free Software Foundation @@ -73,22 +74,6 @@ struct _GdkPixbufClass { }; -/* Private part of the GdkPixbufFrame structure */ -struct _GdkPixbufFrame { - /* The pixbuf with this frame's image data */ - GdkPixbuf *pixbuf; - - /* Offsets for overlaying onto the animation's area */ - int x_offset; - int y_offset; - - /* Frame duration in ms */ - int delay_time; - - /* Overlay mode */ - GdkPixbufFrameAction action; -}; - typedef struct _GdkPixbufAnimationClass GdkPixbufAnimationClass; #define GDK_PIXBUF_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_ANIMATION, GdkPixbufAnimationClass)) @@ -99,24 +84,52 @@ typedef struct _GdkPixbufAnimationClass GdkPixbufAnimationClass; struct _GdkPixbufAnimation { GObject parent_instance; - /* Number of frames */ - int n_frames; - - /* List of GdkPixbufFrame structures */ - GList *frames; - - /* bounding box size */ - int width, height; }; struct _GdkPixbufAnimationClass { GObjectClass parent_class; + gboolean (*is_static_image) (GdkPixbufAnimation *anim); + + GdkPixbuf* (*get_static_image) (GdkPixbufAnimation *anim); + + void (*get_size) (GdkPixbufAnimation *anim, + int *width, + int *height); + + GdkPixbufAnimationIter* (*get_iter) (GdkPixbufAnimation *anim, + const GTimeVal *start_time); }; +typedef struct _GdkPixbufAnimationIterClass GdkPixbufAnimationIterClass; + +#define GDK_PIXBUF_ANIMATION_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIterClass)) +#define GDK_IS_PIXBUF_ANIMATION_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_ANIMATION_ITER)) +#define GDK_PIXBUF_ANIMATION_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIterClass)) + +struct _GdkPixbufAnimationIter { + GObject parent_instance; + +}; + +struct _GdkPixbufAnimationIterClass { + GObjectClass parent_class; + + int (*get_delay_time) (GdkPixbufAnimationIter *iter); + + GdkPixbuf* (*get_pixbuf) (GdkPixbufAnimationIter *iter); + + gboolean (*on_currently_loading_frame) (GdkPixbufAnimationIter *iter); + + gboolean (*advance) (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); +}; + + + #define GDK_PIXBUF_INLINE_MAGIC_NUMBER 0x47646B50 /* 'GdkP' */ typedef enum @@ -125,6 +138,8 @@ typedef enum GDK_PIXBUF_INLINE_RLE = 1 } GdkPixbufInlineFormat; + +GdkPixbufAnimation* gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf); #endif diff --git a/gdk-pixbuf/gdk-pixbuf-util.c b/gdk-pixbuf/gdk-pixbuf-util.c index bee4fb5bb7..0f869b9471 100644 --- a/gdk-pixbuf/gdk-pixbuf-util.c +++ b/gdk-pixbuf/gdk-pixbuf-util.c @@ -121,6 +121,8 @@ gdk_pixbuf_copy_area (const GdkPixbuf *src_pixbuf, g_return_if_fail (dest_x >= 0 && dest_x + width <= dest_pixbuf->width); g_return_if_fail (dest_y >= 0 && dest_y + height <= dest_pixbuf->height); + g_return_if_fail (!(gdk_pixbuf_get_has_alpha (src_pixbuf) && !gdk_pixbuf_get_has_alpha (dest_pixbuf))); + /* This will perform format conversions automatically */ gdk_pixbuf_scale (src_pixbuf, diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c index 12b8e61441..cfc0bb323c 100644 --- a/gdk-pixbuf/gdk-pixbuf.c +++ b/gdk-pixbuf/gdk-pixbuf.c @@ -412,7 +412,7 @@ gdk_pixbuf_error_quark (void) /** * gdk_pixbuf_fill: * @pixbuf: a #GdkPixbuf - * @pixel: RGBA pixel to clear to (0xffffff00 is opaque white, 0x000000ff transparent black) + * @pixel: RGBA pixel to clear to (0xffffffff is opaque white, 0x00000000 transparent black) * * Clears a pixbuf to the given RGBA value, converting the RGBA value into * the pixbuf's pixel format. The alpha will be ignored if the pixbuf @@ -454,7 +454,7 @@ gdk_pixbuf_fill (GdkPixbuf *pixbuf, p = pixels; end = pixels + pixbuf->rowstride * pixbuf->height; - end -= (pixbuf->rowstride - pixbuf->width); + end -= (pixbuf->rowstride - pixbuf->width * pixbuf->n_channels); while (p < end) { *p++ = r; diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h index b8b5a03af0..46ff3533ce 100644 --- a/gdk-pixbuf/gdk-pixbuf.h +++ b/gdk-pixbuf/gdk-pixbuf.h @@ -54,6 +54,7 @@ typedef enum { /* All of these are opaque structures */ typedef struct _GdkPixbuf GdkPixbuf; typedef struct _GdkPixbufAnimation GdkPixbufAnimation; +typedef struct _GdkPixbufAnimationIter GdkPixbufAnimationIter; typedef struct _GdkPixbufFrame GdkPixbufFrame; #define GDK_TYPE_PIXBUF (gdk_pixbuf_get_type ()) @@ -64,6 +65,9 @@ typedef struct _GdkPixbufFrame GdkPixbufFrame; #define GDK_PIXBUF_ANIMATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_ANIMATION, GdkPixbufAnimation)) #define GDK_IS_PIXBUF_ANIMATION(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_ANIMATION)) +#define GDK_TYPE_PIXBUF_ANIMATION_ITER (gdk_pixbuf_animation_iter_get_type ()) +#define GDK_PIXBUF_ANIMATION_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIter)) +#define GDK_IS_PIXBUF_ANIMATION_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_ANIMATION_ITER)) /* Handler that must free the pixel array */ typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data); @@ -255,13 +259,6 @@ GdkPixbuf *gdk_pixbuf_composite_color_simple (const GdkPixbuf *src, /* Animation support */ -/* GIF-like animation overlay modes for frames */ -typedef enum { - GDK_PIXBUF_FRAME_RETAIN, - GDK_PIXBUF_FRAME_DISPOSE, - GDK_PIXBUF_FRAME_REVERT -} GdkPixbufFrameAction; - GType gdk_pixbuf_animation_get_type (void) G_GNUC_CONST; GdkPixbufAnimation *gdk_pixbuf_animation_new_from_file (const char *filename, @@ -272,21 +269,20 @@ void gdk_pixbuf_animation_unref (GdkPixbufAnimation *an int gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation); int gdk_pixbuf_animation_get_height (GdkPixbufAnimation *animation); -GList *gdk_pixbuf_animation_get_frames (GdkPixbufAnimation *animation); -int gdk_pixbuf_animation_get_num_frames (GdkPixbufAnimation *animation); +gboolean gdk_pixbuf_animation_is_static_image (GdkPixbufAnimation *animation); +GdkPixbuf *gdk_pixbuf_animation_get_static_image (GdkPixbufAnimation *animation); -/* Frame accessors */ +GdkPixbufAnimationIter *gdk_pixbuf_animation_get_iter (GdkPixbufAnimation *animation, + const GTimeVal *start_time); +GType gdk_pixbuf_animation_iter_get_type (void) G_GNUC_CONST; +int gdk_pixbuf_animation_iter_get_delay_time (GdkPixbufAnimationIter *iter); +GdkPixbuf *gdk_pixbuf_animation_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +gboolean gdk_pixbuf_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +gboolean gdk_pixbuf_animation_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); -GdkPixbuf *gdk_pixbuf_frame_get_pixbuf (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_x_offset (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_y_offset (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_delay_time (GdkPixbufFrame *frame); -GdkPixbufFrameAction gdk_pixbuf_frame_get_action (GdkPixbufFrame *frame); -GdkPixbufFrame *gdk_pixbuf_frame_copy (GdkPixbufFrame *src); -void gdk_pixbuf_frame_free (GdkPixbufFrame *frame); -GType gdk_pixbuf_frame_get_type (void) G_GNUC_CONST; -#define GDK_TYPE_PIXBUF_FRAME gdk_pixbuf_frame_get_type () + #include diff --git a/gdk-pixbuf/io-bmp.c b/gdk-pixbuf/io-bmp.c index c5108bcc1d..ce112f85ac 100644 --- a/gdk-pixbuf/io-bmp.c +++ b/gdk-pixbuf/io-bmp.c @@ -174,9 +174,7 @@ struct bmp_progressive_state { static gpointer gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error); static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error); @@ -198,7 +196,7 @@ static GdkPixbuf *gdk_pixbuf__bmp_image_load(FILE * f, GError **error) GdkPixbuf *pb; State = - gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, NULL, NULL, + gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, error); if (State == NULL) @@ -312,7 +310,7 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ - (*State->prepared_func) (State->pixbuf, State->user_data); + (*State->prepared_func) (State->pixbuf, NULL, State->user_data); } @@ -328,9 +326,7 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH, static gpointer gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error) { struct bmp_progressive_state *context; diff --git a/gdk-pixbuf/io-gif-animation.c b/gdk-pixbuf/io-gif-animation.c new file mode 100644 index 0000000000..fe164a0958 --- /dev/null +++ b/gdk-pixbuf/io-gif-animation.c @@ -0,0 +1,557 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* GdkPixbuf library - animated gif support + * + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Jonathan Blandford + * Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gdk-pixbuf-io.h" +#include "gdk-pixbuf-private.h" +#include "io-gif-animation.h" + +static void gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass); +static void gdk_pixbuf_gif_anim_finalize (GObject *object); + +static gboolean gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation); +static GdkPixbuf* gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation); + +static void gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height); +static GdkPixbufAnimationIter* gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time); + + + + +static gpointer parent_class; + +GType +gdk_pixbuf_gif_anim_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufGifAnimClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_gif_anim_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufGifAnim), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION, + "GdkPixbufGifAnim", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_gif_anim_finalize; + + anim_class->is_static_image = gdk_pixbuf_gif_anim_is_static_image; + anim_class->get_static_image = gdk_pixbuf_gif_anim_get_static_image; + anim_class->get_size = gdk_pixbuf_gif_anim_get_size; + anim_class->get_iter = gdk_pixbuf_gif_anim_get_iter; +} + +static void +gdk_pixbuf_gif_anim_finalize (GObject *object) +{ + GdkPixbufGifAnim *gif_anim = GDK_PIXBUF_GIF_ANIM (object); + + GList *l; + GdkPixbufFrame *frame; + + for (l = gif_anim->frames; l; l = l->next) { + frame = l->data; + gdk_pixbuf_unref (frame->pixbuf); + if (frame->composited) + gdk_pixbuf_unref (frame->composited); + if (frame->revert) + gdk_pixbuf_unref (frame->revert); + g_free (frame); + } + + g_list_free (gif_anim->frames); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (animation); + + return (gif_anim->frames != NULL && + gif_anim->frames->next == NULL); +} + +static GdkPixbuf* +gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (animation); + + if (gif_anim->frames == NULL) + return NULL; + else + return GDK_PIXBUF (((GdkPixbufFrame*)gif_anim->frames->data)->pixbuf); +} + +static void +gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (anim); + + if (width) + *width = gif_anim->width; + + if (height) + *height = gif_anim->height; +} + + +static void +iter_clear (GdkPixbufGifAnimIter *iter) +{ + iter->current_frame = NULL; +} + +static void +iter_restart (GdkPixbufGifAnimIter *iter) +{ + iter_clear (iter); + + iter->current_frame = iter->gif_anim->frames; +} + +static GdkPixbufAnimationIter* +gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time) +{ + GdkPixbufGifAnimIter *iter; + + iter = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM_ITER, NULL); + + iter->gif_anim = GDK_PIXBUF_GIF_ANIM (anim); + + g_object_ref (G_OBJECT (iter->gif_anim)); + + iter_restart (iter); + + iter->start_time = *start_time; + iter->current_time = *start_time; + + return GDK_PIXBUF_ANIMATION_ITER (iter); +} + + + +static void gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass); +static void gdk_pixbuf_gif_anim_iter_finalize (GObject *object); + +static int gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter); +static GdkPixbuf* gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); + + + +static gpointer iter_parent_class; + +GType +gdk_pixbuf_gif_anim_iter_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufGifAnimIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_gif_anim_iter_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufGifAnimIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER, + "GdkPixbufGifAnimIter", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationIterClass *anim_iter_class = + GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); + + iter_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_gif_anim_iter_finalize; + + anim_iter_class->get_delay_time = gdk_pixbuf_gif_anim_iter_get_delay_time; + anim_iter_class->get_pixbuf = gdk_pixbuf_gif_anim_iter_get_pixbuf; + anim_iter_class->on_currently_loading_frame = gdk_pixbuf_gif_anim_iter_on_currently_loading_frame; + anim_iter_class->advance = gdk_pixbuf_gif_anim_iter_advance; +} + +static void +gdk_pixbuf_gif_anim_iter_finalize (GObject *object) +{ + GdkPixbufGifAnimIter *iter = GDK_PIXBUF_GIF_ANIM_ITER (object); + + iter_clear (iter); + + g_object_unref (G_OBJECT (iter->gif_anim)); + + G_OBJECT_CLASS (iter_parent_class)->finalize (object); +} + +static gboolean +gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *anim_iter, + const GTimeVal *current_time) +{ + GdkPixbufGifAnimIter *iter; + gint elapsed; + GList *tmp; + GList *old; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + iter->current_time = *current_time; + + /* We use milliseconds for all times */ + elapsed = + (((iter->current_time.tv_sec - iter->start_time.tv_sec) * G_USEC_PER_SEC + + iter->current_time.tv_usec - iter->start_time.tv_usec)) / 1000; + + if (elapsed < 0) { + /* Try to compensate; probably the system clock + * was set backwards + */ + iter->start_time = iter->current_time; + elapsed = 0; + } + + /* See how many times we've already played the full animation, + * and subtract time for that. + */ + elapsed = elapsed % iter->gif_anim->total_time; + + g_assert (elapsed < iter->gif_anim->total_time); + + iter->position = elapsed; + + /* Now move to the proper frame */ + tmp = iter->gif_anim->frames; + while (tmp != NULL) { + GdkPixbufFrame *frame = tmp->data; + + if (iter->position >= frame->elapsed && + iter->position < (frame->elapsed + frame->delay_time)) + break; + + tmp = tmp->next; + } + + old = iter->current_frame; + + iter->current_frame = tmp; + + return iter->current_frame != old; +} + +int +gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufFrame *frame; + GdkPixbufGifAnimIter *iter; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + if (iter->current_frame) { + frame = iter->current_frame->data; + +#if 0 + g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n", + frame->elapsed, + iter->position, + frame->delay_time, + frame->delay_time - (iter->position - frame->elapsed)); +#endif + + return frame->delay_time - (iter->position - frame->elapsed); + } else { + return -1; /* show last frame forever */ + } +} + +void +gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, + GdkPixbufFrame *frame) +{ + GList *link; + GList *tmp; + + link = g_list_find (gif_anim->frames, frame); + + if (frame->need_recomposite || frame->composited == NULL) { + /* For now, to composite we start with the last + * composited frame and composite everything up to + * here. + */ + + /* Rewind to last composited frame. */ + tmp = link; + while (tmp != NULL) { + GdkPixbufFrame *f = tmp->data; + + if (f->need_recomposite) { + if (f->composited) { + g_object_unref (G_OBJECT (f->composited)); + f->composited = NULL; + } + } + + if (f->composited != NULL) + break; + + tmp = tmp->prev; + } + + /* Go forward, compositing all frames up to the current frame */ + if (tmp == NULL) + tmp = gif_anim->frames; + + while (tmp != NULL) { + GdkPixbufFrame *f = tmp->data; + + if (f->need_recomposite) { + if (f->composited) { + g_object_unref (G_OBJECT (f->composited)); + f->composited = NULL; + } + } + + if (f->composited != NULL) + goto next; + + if (tmp->prev == NULL) { + /* First frame may be smaller than the whole image; + * if so, we make the area outside it full alpha if the + * image has alpha, and background color otherwise. + * GIF spec doesn't actually say what to do about this. + */ + f->composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, gif_anim->width, gif_anim->height); + + /* alpha gets dumped if f->composited has no alpha */ + + gdk_pixbuf_fill (f->composited, + (gif_anim->bg_red << 24) | + (gif_anim->bg_green << 16) | + (gif_anim->bg_blue << 8) | + (f->bg_transparent ? 0 : 255)); + + gdk_pixbuf_composite (f->pixbuf, + f->composited, + f->x_offset, + f->y_offset, + gdk_pixbuf_get_width (f->pixbuf), + gdk_pixbuf_get_height (f->pixbuf), + f->x_offset, f->y_offset, + 1.0, 1.0, + GDK_INTERP_BILINEAR, + 255); +#if 0 + gdk_pixbuf_copy_area (f->pixbuf, + 0, 0, + gdk_pixbuf_get_width (f->pixbuf), + gdk_pixbuf_get_height (f->pixbuf), + f->composited, + f->x_offset, + f->y_offset); + +#endif + + if (f->action == GDK_PIXBUF_FRAME_REVERT) + g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image"); + + f->need_recomposite = FALSE; + } else { + GdkPixbufFrame *prev_frame; + + prev_frame = tmp->prev->data; + + /* Init f->composited with what we should have after the previous + * frame + */ + + if (prev_frame->action == GDK_PIXBUF_FRAME_RETAIN) { + f->composited = gdk_pixbuf_copy (prev_frame->composited); + + } else if (prev_frame->action == GDK_PIXBUF_FRAME_DISPOSE) { + GdkPixbuf *area; + + f->composited = gdk_pixbuf_copy (prev_frame->composited); + + /* Clear area of previous frame to background */ + area = gdk_pixbuf_new_subpixbuf (f->composited, + prev_frame->x_offset, + prev_frame->y_offset, + gdk_pixbuf_get_width (prev_frame->pixbuf), + gdk_pixbuf_get_height (prev_frame->pixbuf)); + + gdk_pixbuf_fill (area, + (gif_anim->bg_red << 24) | + (gif_anim->bg_green << 16) | + (gif_anim->bg_blue << 8) | + prev_frame->bg_transparent ? 0 : 255); + + g_object_unref (G_OBJECT (area)); + + } else if (prev_frame->action == GDK_PIXBUF_FRAME_REVERT) { + f->composited = gdk_pixbuf_copy (prev_frame->composited); + + /* Copy in the revert frame */ + gdk_pixbuf_copy_area (prev_frame->revert, + 0, 0, + gdk_pixbuf_get_width (prev_frame->revert), + gdk_pixbuf_get_height (prev_frame->revert), + f->composited, + prev_frame->x_offset, + prev_frame->y_offset); + } else { + g_warning ("Unknown revert action for GIF frame"); + } + + if (f->revert == NULL && + f->action == GDK_PIXBUF_FRAME_REVERT) { + /* We need to save the contents before compositing */ + GdkPixbuf *area; + + area = gdk_pixbuf_new_subpixbuf (f->composited, + f->x_offset, + f->y_offset, + gdk_pixbuf_get_width (f->pixbuf), + gdk_pixbuf_get_height (f->pixbuf)); + + f->revert = gdk_pixbuf_copy (area); + + g_object_unref (G_OBJECT (area)); + } + + /* Put current frame onto f->composited */ + gdk_pixbuf_composite (f->pixbuf, + f->composited, + f->x_offset, + f->y_offset, + gdk_pixbuf_get_width (f->pixbuf), + gdk_pixbuf_get_height (f->pixbuf), + f->x_offset, f->y_offset, + 1.0, 1.0, + GDK_INTERP_NEAREST, + 255); + + f->need_recomposite = FALSE; + } + + next: + if (tmp == link) + break; + + tmp = tmp->next; + } + } + + g_assert (frame->composited != NULL); + g_assert (gdk_pixbuf_get_width (frame->composited) == gif_anim->width); + g_assert (gdk_pixbuf_get_height (frame->composited) == gif_anim->height); +} + +GdkPixbuf* +gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufGifAnimIter *iter; + GdkPixbufFrame *frame; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + frame = iter->current_frame ? iter->current_frame->data : NULL; + +#if 0 + if (FALSE && frame) + g_print ("current frame %d dispose mode %d %d x %d\n", + g_list_index (iter->gif_anim->frames, + frame), + frame->action, + gdk_pixbuf_get_width (frame->pixbuf), + gdk_pixbuf_get_height (frame->pixbuf)); +#endif + + if (frame == NULL) + return NULL; + + gdk_pixbuf_gif_anim_frame_composite (iter->gif_anim, frame); + + return frame->composited; +} + +static gboolean +gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufGifAnimIter *iter; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + return iter->current_frame == NULL || iter->current_frame->next == NULL; +} diff --git a/gdk-pixbuf/io-gif-animation.h b/gdk-pixbuf/io-gif-animation.h new file mode 100644 index 0000000000..ce1ebaa6f9 --- /dev/null +++ b/gdk-pixbuf/io-gif-animation.h @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* GdkPixbuf library - GIF loader declarations + * + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Mark Crichton + * Miguel de Icaza + * Federico Mena-Quintero + * Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDK_PIXBUF_GIF_H +#define GDK_PIXBUF_GIF_H + +#include "gdk-pixbuf-private.h" + +typedef enum { + /* Keep this frame and composite next frame over it */ + /* (GIF disposal method 1) */ + GDK_PIXBUF_FRAME_RETAIN, + /* Revert to background color before compositing next frame */ + /* (GIF disposal method 2) */ + GDK_PIXBUF_FRAME_DISPOSE, + /* Revert to previously-displayed composite image after + * displaying this frame + */ + /* (GIF disposal method 3) */ + GDK_PIXBUF_FRAME_REVERT +} GdkPixbufFrameAction; + + + +typedef struct _GdkPixbufGifAnim GdkPixbufGifAnim; +typedef struct _GdkPixbufGifAnimClass GdkPixbufGifAnimClass; + +#define GDK_TYPE_PIXBUF_GIF_ANIM (gdk_pixbuf_gif_anim_get_type ()) +#define GDK_PIXBUF_GIF_ANIM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnim)) +#define GDK_IS_PIXBUF_GIF_ANIM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_GIF_ANIM)) + +#define GDK_PIXBUF_GIF_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnimClass)) +#define GDK_IS_PIXBUF_GIF_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_GIF_ANIM)) +#define GDK_PIXBUF_GIF_ANIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnimClass)) + +/* Private part of the GdkPixbufGifAnim structure */ +struct _GdkPixbufGifAnim { + GdkPixbufAnimation parent_instance; + + /* Number of frames */ + int n_frames; + + /* Total length of animation */ + int total_time; + + /* List of GdkPixbufFrame structures */ + GList *frames; + + /* bounding box size */ + int width, height; + + guchar bg_red; + guchar bg_green; + guchar bg_blue; +}; + +struct _GdkPixbufGifAnimClass { + GdkPixbufAnimationClass parent_class; + +}; + +GType gdk_pixbuf_gif_anim_get_type (void) G_GNUC_CONST; + + + +typedef struct _GdkPixbufGifAnimIter GdkPixbufGifAnimIter; +typedef struct _GdkPixbufGifAnimIterClass GdkPixbufGifAnimIterClass; + + +#define GDK_TYPE_PIXBUF_GIF_ANIM_ITER (gdk_pixbuf_gif_anim_iter_get_type ()) +#define GDK_PIXBUF_GIF_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIter)) +#define GDK_IS_PIXBUF_GIF_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_GIF_ANIM_ITER)) + +#define GDK_PIXBUF_GIF_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIterClass)) +#define GDK_IS_PIXBUF_GIF_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_GIF_ANIM_ITER)) +#define GDK_PIXBUF_GIF_ANIM_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIterClass)) + +struct _GdkPixbufGifAnimIter { + GdkPixbufAnimationIter parent_instance; + + GdkPixbufGifAnim *gif_anim; + + GTimeVal start_time; + GTimeVal current_time; + + /* Time in milliseconds into this run of the animation */ + gint position; + + GList *current_frame; +}; + +struct _GdkPixbufGifAnimIterClass { + GdkPixbufAnimationIterClass parent_class; + +}; + +GType gdk_pixbuf_gif_anim_iter_get_type (void) G_GNUC_CONST; + + + +struct _GdkPixbufFrame { + /* The pixbuf with this frame's image data */ + GdkPixbuf *pixbuf; + + /* Offsets for overlaying onto the GIF graphic area */ + int x_offset; + int y_offset; + + /* Frame duration in ms */ + int delay_time; + + /* Sum of preceding delay times */ + int elapsed; + + /* Overlay mode */ + GdkPixbufFrameAction action; + + /* TRUE if the pixbuf has been modified since + * the last frame composite operation + */ + gboolean need_recomposite; + + /* TRUE if the background for this frame is transparent */ + gboolean bg_transparent; + + /* The below reflects the "use hell of a lot of RAM" + * philosophy of coding + */ + + /* Cached composite image (the image you actually display + * for this frame) + */ + GdkPixbuf *composited; + + /* Cached revert image (the contents of the area + * covered by the frame prior to compositing; + * same size as pixbuf, not as the composite image; only + * used for FRAME_REVERT frames) + */ + GdkPixbuf *revert; +}; + +void gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, + GdkPixbufFrame *frame); + +#endif diff --git a/gdk-pixbuf/io-gif.c b/gdk-pixbuf/io-gif.c index 5be68023fc..c3c1b8728c 100644 --- a/gdk-pixbuf/io-gif.c +++ b/gdk-pixbuf/io-gif.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - GIF image loader * * Copyright (C) 1999 Mark Crichton @@ -30,10 +31,6 @@ * in it got a bit messy. Basicly, every function is written to expect a failed * read_gif, and lets you call it again assuming that the bytes are there. * - * A note on Animations: - * Currently, it doesn't correctly read the different colormap per frame. This - * needs implementing sometime. - * * Return vals. * Unless otherwise specified, these are the return vals for most functions: * @@ -59,11 +56,15 @@ #include #include #include +#include #include "gdk-pixbuf-private.h" #include "gdk-pixbuf-io.h" +#include "io-gif-animation.h" +#undef DUMP_IMAGE_DETAILS + #define MAXCOLORMAPSIZE 256 #define MAX_LZW_BITS 12 @@ -88,7 +89,7 @@ enum { GIF_LZW_FILL_BUFFER, GIF_LZW_CLEAR_CODE, GIF_GET_LZW, - GIF_DONE, + GIF_DONE }; @@ -107,19 +108,27 @@ struct _GifContext int state; /* really only relevant for progressive loading */ unsigned int width; unsigned int height; - CMap color_map; - CMap frame_color_map; - unsigned int bit_pixel; - unsigned int color_resolution; - unsigned int background; + + gboolean has_global_cmap; + + CMap global_color_map; + gint global_colormap_size; + unsigned int global_bit_pixel; + unsigned int global_color_resolution; + unsigned int background_index; + + gboolean frame_cmap_active; + CMap frame_color_map; + gint frame_colormap_size; + unsigned int frame_bit_pixel; + unsigned int aspect_ratio; GdkPixbuf *pixbuf; - GdkPixbufAnimation *animation; + GdkPixbufGifAnim *animation; GdkPixbufFrame *frame; Gif89 gif89; - /* stuff per frame. As we only support the first one, not so - * relevant. But still needed */ + /* stuff per frame. */ int frame_len; int frame_height; int frame_interlace; @@ -132,18 +141,12 @@ struct _GifContext /* progressive read, only. */ ModulePreparedNotifyFunc prepare_func; ModuleUpdatedNotifyFunc update_func; - ModuleFrameDoneNotifyFunc frame_done_func; - ModuleAnimationDoneNotifyFunc anim_done_func; gpointer user_data; guchar *buf; guint ptr; guint size; guint amount_needed; - /* colormap context */ - gint colormap_index; - gint colormap_flag; - /* extension context */ guchar extension_label; guchar extension_flag; @@ -207,7 +210,14 @@ gif_read (GifContext *context, guchar *buffer, size_t len) count += len; g_print ("Fsize :%d\tcount :%d\t", len, count); #endif - retval = (fread(buffer, len, 1, context->file) != 0); + retval = (fread(buffer, len, 1, context->file) != 0); + + if (!retval && ferror (context->file)) + g_set_error (context->error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + _("Failure reading GIF: %s"), strerror (errno)); + #ifdef IO_GIFDEBUG if (len < 100) { for (i = 0; i < len; i++) @@ -247,16 +257,14 @@ gif_read (GifContext *context, guchar *buffer, size_t len) static void gif_set_get_colormap (GifContext *context) { - context->colormap_flag = TRUE; - context->colormap_index = 0; + context->global_colormap_size = 0; context->state = GIF_GET_COLORMAP; } static void gif_set_get_colormap2 (GifContext *context) { - context->colormap_flag = TRUE; - context->colormap_index = 0; + context->frame_colormap_size = 0; context->state = GIF_GET_COLORMAP2; } @@ -265,18 +273,43 @@ gif_get_colormap (GifContext *context) { unsigned char rgb[3]; - while (context->colormap_index < context->bit_pixel) { + while (context->global_colormap_size < context->global_bit_pixel) { if (!gif_read (context, rgb, sizeof (rgb))) { - /*g_message (_("GIF: bad colormap\n"));*/ return -1; } - context->color_map[0][context->colormap_index] = rgb[0]; - context->color_map[1][context->colormap_index] = rgb[1]; - context->color_map[2][context->colormap_index] = rgb[2]; + context->global_color_map[0][context->global_colormap_size] = rgb[0]; + context->global_color_map[1][context->global_colormap_size] = rgb[1]; + context->global_color_map[2][context->global_colormap_size] = rgb[2]; - context->colormap_flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]); - context->colormap_index ++; + if (context->global_colormap_size == context->background_index) { + context->animation->bg_red = rgb[0]; + context->animation->bg_green = rgb[1]; + context->animation->bg_blue = rgb[2]; + } + + context->global_colormap_size ++; + } + + return 0; +} + + +static gint +gif_get_colormap2 (GifContext *context) +{ + unsigned char rgb[3]; + + while (context->frame_colormap_size < context->frame_bit_pixel) { + if (!gif_read (context, rgb, sizeof (rgb))) { + return -1; + } + + context->frame_color_map[0][context->frame_colormap_size] = rgb[0]; + context->frame_color_map[1][context->frame_colormap_size] = rgb[1]; + context->frame_color_map[2][context->frame_colormap_size] = rgb[2]; + + context->frame_colormap_size ++; } return 0; @@ -348,9 +381,11 @@ gif_get_extension (GifContext *context) retval = get_data_block (context, (unsigned char *) context->block_buf, NULL); if (retval != 0) return retval; - if (context->pixbuf == NULL) { + + if (context->frame == NULL) { /* I only want to set the transparency if I haven't - * created the pixbuf yet. */ + * created the frame yet. + */ context->gif89.disposal = (context->block_buf[0] >> 2) & 0x7; context->gif89.input_flag = (context->block_buf[0] >> 1) & 0x1; context->gif89.delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]); @@ -623,18 +658,24 @@ static void gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v) { guchar *pixel = NULL; + guchar (*cmap)[MAXCOLORMAPSIZE]; + if (context->frame_cmap_active) + cmap = context->frame_color_map; + else + cmap = context->global_color_map; + if (context->gif89.transparent != -1) { - pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4; - *pixel = context->color_map [0][(guchar) v]; - *(pixel+1) = context->color_map [1][(guchar) v]; - *(pixel+2) = context->color_map [2][(guchar) v]; - *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535); + pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4; + *pixel = cmap [0][(guchar) v]; + *(pixel+1) = cmap [1][(guchar) v]; + *(pixel+2) = cmap [2][(guchar) v]; + *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255); } else { - pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3; - *pixel = context->color_map [0][(guchar) v]; - *(pixel+1) = context->color_map [1][(guchar) v]; - *(pixel+2) = context->color_map [2][(guchar) v]; + pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 3; + *pixel = cmap [0][(guchar) v]; + *(pixel+1) = cmap [1][(guchar) v]; + *(pixel+2) = cmap [2][(guchar) v]; } } @@ -682,80 +723,123 @@ gif_get_lzw (GifContext *context) gint first_pass; /* bounds for emitting the area_updated signal */ gint v; - if (context->pixbuf == NULL) { - context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - context->gif89.transparent != -1, - 8, - context->frame_len, - context->frame_height); + if (context->frame == NULL) { + context->frame = g_new (GdkPixbufFrame, 1); - if (context->prepare_func) - (* context->prepare_func) (context->pixbuf, context->user_data); - if (context->animation || context->frame_done_func || context->anim_done_func) { - context->frame = g_new (GdkPixbufFrame, 1); - context->frame->x_offset = context->x_offset; - context->frame->y_offset = context->y_offset;; - context->frame->delay_time = context->gif89.delay_time; - switch (context->gif89.disposal) { - case 0: - case 1: - context->frame->action = GDK_PIXBUF_FRAME_RETAIN; - break; - case 2: - context->frame->action = GDK_PIXBUF_FRAME_DISPOSE; - break; - case 3: - context->frame->action = GDK_PIXBUF_FRAME_REVERT; - break; - default: - context->frame->action = GDK_PIXBUF_FRAME_RETAIN; - break; - } - context->frame->pixbuf = context->pixbuf; - if (context->animation) { - int w,h; - context->animation->n_frames ++; - context->animation->frames = g_list_append (context->animation->frames, context->frame); - w = gdk_pixbuf_get_width (context->pixbuf); - h = gdk_pixbuf_get_height (context->pixbuf); - if (w > context->animation->width) - context->animation->width = w; - if (h > context->animation->height) - context->animation->height = h; - } - } - } - dest = gdk_pixbuf_get_pixels (context->pixbuf); + context->frame->composited = NULL; + context->frame->revert = NULL; + + context->frame->pixbuf = + gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, + context->frame_len, + context->frame_height); + + context->frame->x_offset = context->x_offset; + context->frame->y_offset = context->y_offset; + context->frame->need_recomposite = TRUE; + + /* GIF delay is in hundredths, we want thousandths */ + context->frame->delay_time = context->gif89.delay_time * 10; + context->frame->elapsed = context->animation->total_time; + context->animation->total_time += context->frame->delay_time; + + switch (context->gif89.disposal) { + case 0: + case 1: + context->frame->action = GDK_PIXBUF_FRAME_RETAIN; + break; + case 2: + context->frame->action = GDK_PIXBUF_FRAME_DISPOSE; + break; + case 3: + context->frame->action = GDK_PIXBUF_FRAME_REVERT; + break; + default: + context->frame->action = GDK_PIXBUF_FRAME_RETAIN; + break; + } + + context->frame->bg_transparent = (context->gif89.transparent == context->background_index); + + { + /* Update animation size */ + int w, h; + + context->animation->n_frames ++; + context->animation->frames = g_list_append (context->animation->frames, context->frame); + + w = context->frame->x_offset + + gdk_pixbuf_get_width (context->frame->pixbuf); + h = context->frame->y_offset + + gdk_pixbuf_get_height (context->frame->pixbuf); + if (w > context->animation->width) + context->animation->width = w; + if (h > context->animation->height) + context->animation->height = h; + } + + /* Only call prepare_func for the first frame */ + if (context->animation->frames->next == NULL) { + if (context->prepare_func) + (* context->prepare_func) (context->frame->pixbuf, + GDK_PIXBUF_ANIMATION (context->animation), + context->user_data); + } else { + /* Otherwise init frame with last frame */ + GList *link; + GdkPixbufFrame *prev_frame; + + link = g_list_find (context->animation->frames, context->frame); + + prev_frame = link->prev->data; + + gdk_pixbuf_gif_anim_frame_composite (context->animation, prev_frame); + + gdk_pixbuf_copy_area (prev_frame->composited, + context->frame->x_offset, + context->frame->y_offset, + gdk_pixbuf_get_width (context->frame->pixbuf), + gdk_pixbuf_get_height (context->frame->pixbuf), + context->frame->pixbuf, + 0, 0); + } + } + + dest = gdk_pixbuf_get_pixels (context->frame->pixbuf); bound_flag = FALSE; lower_bound = upper_bound = context->draw_ypos; first_pass = context->draw_pass; while (TRUE) { + guchar (*cmap)[MAXCOLORMAPSIZE]; + + if (context->frame_cmap_active) + cmap = context->frame_color_map; + else + cmap = context->global_color_map; + v = lzw_read_byte (context); if (v < 0) { goto finished_data; } bound_flag = TRUE; - if (context->gif89.transparent != -1) { - temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4; - *temp = context->color_map [0][(guchar) v]; - *(temp+1) = context->color_map [1][(guchar) v]; - *(temp+2) = context->color_map [2][(guchar) v]; - *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : -1); - } else { - temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3; - *temp = context->color_map [0][(guchar) v]; - *(temp+1) = context->color_map [1][(guchar) v]; - *(temp+2) = context->color_map [2][(guchar) v]; - } + g_assert (gdk_pixbuf_get_has_alpha (context->frame->pixbuf)); + + temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4; + *temp = cmap [0][(guchar) v]; + *(temp+1) = cmap [1][(guchar) v]; + *(temp+2) = cmap [2][(guchar) v]; + *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255); if (context->prepare_func && context->frame_interlace) gif_fill_in_lines (context, dest, v); context->draw_xpos++; - + if (context->draw_xpos == context->frame_len) { context->draw_xpos = 0; if (context->frame_interlace) { @@ -796,7 +880,7 @@ gif_get_lzw (GifContext *context) lower_bound = 0; upper_bound = context->frame_height; } else { - + } } else upper_bound = context->draw_ypos; @@ -804,56 +888,60 @@ gif_get_lzw (GifContext *context) if (context->draw_ypos >= context->frame_height) break; } + done: - /* we got enough data. there may be more (ie, newer layers) but we can quit now */ - if (context->animation || context->frame_done_func || context->anim_done_func) { - context->state = GIF_GET_NEXT_STEP; - } else - context->state = GIF_DONE; - v = 0; + + context->state = GIF_GET_NEXT_STEP; + + v = 0; + finished_data: + + if (bound_flag) + context->frame->need_recomposite = TRUE; + if (bound_flag && context->update_func) { if (lower_bound <= upper_bound && first_pass == context->draw_pass) { (* context->update_func) - (context->pixbuf, + (context->frame->pixbuf, 0, lower_bound, - gdk_pixbuf_get_width (context->pixbuf), + gdk_pixbuf_get_width (context->frame->pixbuf), upper_bound - lower_bound, context->user_data); } else { if (lower_bound <= upper_bound) { (* context->update_func) - (context->pixbuf, - 0, 0, - gdk_pixbuf_get_width (context->pixbuf), - gdk_pixbuf_get_height (context->pixbuf), + (context->frame->pixbuf, + context->frame->x_offset, + context->frame->y_offset, + gdk_pixbuf_get_width (context->frame->pixbuf), + gdk_pixbuf_get_height (context->frame->pixbuf), context->user_data); } else { (* context->update_func) - (context->pixbuf, - 0, 0, - gdk_pixbuf_get_width (context->pixbuf), + (context->frame->pixbuf, + context->frame->x_offset, + context->frame->y_offset, + gdk_pixbuf_get_width (context->frame->pixbuf), upper_bound, context->user_data); (* context->update_func) - (context->pixbuf, - 0, lower_bound, - gdk_pixbuf_get_width (context->pixbuf), - gdk_pixbuf_get_height (context->pixbuf), + (context->frame->pixbuf, + context->frame->x_offset, + lower_bound + context->frame->y_offset, + gdk_pixbuf_get_width (context->frame->pixbuf), + gdk_pixbuf_get_height (context->frame->pixbuf), context->user_data); } } } - if ((context->animation || context->frame_done_func || context->anim_done_func) - && context->state == GIF_GET_NEXT_STEP) { - if (context->frame_done_func) - (* context->frame_done_func) (context->frame, - context->user_data); - if (context->frame_done_func) - gdk_pixbuf_unref (context->pixbuf); - context->pixbuf = NULL; + if (context->state == GIF_GET_NEXT_STEP) { + /* Will be freed with context->animation, we are just + * marking that we're done with it (no current frame) + */ context->frame = NULL; + context->frame_cmap_active = FALSE; } return v; @@ -943,16 +1031,36 @@ gif_init (GifContext *context) context->width = LM_to_uint (buf[0], buf[1]); context->height = LM_to_uint (buf[2], buf[3]); - context->bit_pixel = 2 << (buf[4] & 0x07); - context->color_resolution = (((buf[4] & 0x70) >> 3) + 1); - context->background = buf[5]; + /* The 4th byte is + * high bit: whether to use the background index + * next 3: color resolution + * next: whether colormap is sorted by priority of allocation + * last 3: size of colormap + */ + context->global_bit_pixel = 2 << (buf[4] & 0x07); + context->global_color_resolution = (((buf[4] & 0x70) >> 3) + 1); + context->has_global_cmap = (buf[4] & 0x80) != 0; + context->background_index = buf[5]; context->aspect_ratio = buf[6]; - if (BitSet (buf[4], LOCALCOLORMAP)) { + /* Use background of transparent black as default, though if + * one isn't set explicitly no one should ever use it. + */ + context->animation->bg_red = 0; + context->animation->bg_green = 0; + context->animation->bg_blue = 0; + + if (context->has_global_cmap) { gif_set_get_colormap (context); } else { context->state = GIF_GET_NEXT_STEP; } + +#ifdef DUMP_IMAGE_DETAILS + g_print (">Image width: %d height: %d global_cmap: %d background: %d\n", + context->width, context->height, context->has_global_cmap, context->background_index); +#endif + return 0; } @@ -966,38 +1074,81 @@ static gint gif_get_frame_info (GifContext *context) { unsigned char buf[9]; + if (!gif_read (context, buf, 9)) { return -1; } + /* Okay, we got all the info we need. Lets record it */ context->frame_len = LM_to_uint (buf[4], buf[5]); context->frame_height = LM_to_uint (buf[6], buf[7]); context->x_offset = LM_to_uint (buf[0], buf[1]); context->y_offset = LM_to_uint (buf[2], buf[3]); - if (context->frame_height > context->height) { - /* we don't want to resize things. So we exit */ + if (((context->frame_height + context->y_offset) > context->height) || + ((context->frame_len + context->x_offset) > context->width)) { + /* All frames must fit in the image bounds */ context->state = GIF_DONE; g_set_error (context->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, - _("GIF animation contained a frame with an incorrect size")); + _("GIF image contained a frame appearing outside the image bounds.")); + + return -2; + } + + if (context->animation->frames == NULL && + context->gif89.disposal == 3) { + /* First frame can't have "revert to previous" as its + * dispose mode. + */ + + context->state = GIF_DONE; + + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("First frame of GIF image had 'revert to previous' as its disposal mode.")); return -2; } +#ifdef DUMP_IMAGE_DETAILS + g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d\n", + context->frame_len, context->frame_height, context->x_offset, context->y_offset, + context->gif89.disposal, context->gif89.delay_time, context->gif89.transparent); +#endif + context->frame_interlace = BitSet (buf[8], INTERLACE); if (BitSet (buf[8], LOCALCOLORMAP)) { + +#ifdef DUMP_IMAGE_DETAILS + g_print (">has local colormap\n"); +#endif + /* Does this frame have it's own colormap. */ /* really only relevant when looking at the first frame * of an animated gif. */ /* if it does, we need to re-read in the colormap, * the gray_scale, and the bit_pixel */ - context->bit_pixel = 1 << ((buf[8] & 0x07) + 1); + context->frame_cmap_active = TRUE; + context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1); gif_set_get_colormap2 (context); return 0; } + + if (!context->has_global_cmap) { + context->state = GIF_DONE; + + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF image has no global colormap, and a frame inside it has no local colormap.")); + + return -2; + } + gif_set_prepare_lzw (context); return 0; @@ -1037,6 +1188,8 @@ gif_get_next_step (GifContext *context) } +#define LOG(x) + static gint gif_main_loop (GifContext *context) { @@ -1045,52 +1198,63 @@ gif_main_loop (GifContext *context) do { switch (context->state) { case GIF_START: + LOG("start\n"); retval = gif_init (context); break; case GIF_GET_COLORMAP: + LOG("get_colormap\n"); retval = gif_get_colormap (context); if (retval == 0) context->state = GIF_GET_NEXT_STEP; break; case GIF_GET_NEXT_STEP: + LOG("next_step\n"); retval = gif_get_next_step (context); break; case GIF_GET_FRAME_INFO: + LOG("frame_info\n"); retval = gif_get_frame_info (context); break; case GIF_GET_EXTENTION: + LOG("get_extension\n"); retval = gif_get_extension (context); if (retval == 0) context->state = GIF_GET_NEXT_STEP; break; case GIF_GET_COLORMAP2: - retval = gif_get_colormap (context); + LOG("get_colormap2\n"); + retval = gif_get_colormap2 (context); if (retval == 0) gif_set_prepare_lzw (context); break; case GIF_PREPARE_LZW: + LOG("prepare_lzw\n"); retval = gif_prepare_lzw (context); break; case GIF_LZW_FILL_BUFFER: + LOG("fill_buffer\n"); retval = gif_lzw_fill_buffer (context); break; case GIF_LZW_CLEAR_CODE: + LOG("clear_code\n"); retval = gif_lzw_clear_code (context); break; case GIF_GET_LZW: + LOG("get_lzw\n"); retval = gif_get_lzw (context); break; case GIF_DONE: + LOG("done\n"); default: retval = 0; goto done; @@ -1107,13 +1271,12 @@ new_context (void) context = g_new0 (GifContext, 1); - context->pixbuf = NULL; + context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL); + context->frame = NULL; context->file = NULL; context->state = GIF_START; context->prepare_func = NULL; context->update_func = NULL; - context->frame_done_func = NULL; - context->anim_done_func = NULL; context->user_data = NULL; context->buf = NULL; context->amount_needed = 0; @@ -1137,9 +1300,22 @@ gdk_pixbuf__gif_image_load (FILE *file, GError **error) context->file = file; context->error = error; - gif_main_loop (context); + if (gif_main_loop (context) == -1 || context->animation->frames == NULL) { + if (context->error && *(context->error) == NULL) + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF file was missing some data (perhaps it was truncated somehow?)")); + } + + pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation)); - pixbuf = context->pixbuf; + if (pixbuf) + g_object_ref (G_OBJECT (pixbuf)); + + g_object_unref (G_OBJECT (context->animation)); + + g_free (context->buf); g_free (context); return pixbuf; @@ -1148,8 +1324,6 @@ gdk_pixbuf__gif_image_load (FILE *file, GError **error) static gpointer gdk_pixbuf__gif_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -1162,8 +1336,6 @@ gdk_pixbuf__gif_image_begin_load (ModulePreparedNotifyFunc prepare_func, context->error = error; context->prepare_func = prepare_func; context->update_func = update_func; - context->frame_done_func = frame_done_func; - context->anim_done_func = anim_done_func; context->user_data = user_data; return (gpointer) context; @@ -1173,21 +1345,23 @@ static gboolean gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error) { GifContext *context = (GifContext *) data; + gboolean retval = TRUE; + + if (context->state != GIF_DONE) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF image was truncated or incomplete.")); - /* FIXME: free the animation data */ + retval = FALSE; + } - /* FIXME this thing needs to report errors if - * we have unused image data - */ - - if (context->pixbuf) - gdk_pixbuf_unref (context->pixbuf); - if (context->animation) - gdk_pixbuf_animation_unref (context->animation); -/* g_free (context->buf);*/ + g_object_unref (G_OBJECT (context->animation)); + + g_free (context->buf); g_free (context); - return TRUE; + return retval; } static gboolean @@ -1264,18 +1438,28 @@ gdk_pixbuf__gif_image_load_animation (FILE *file, context = new_context (); context->error = error; - - context->animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - context->animation->n_frames = 0; - context->animation->frames = NULL; - context->animation->width = 0; - context->animation->height = 0; context->file = file; - gif_main_loop (context); + if (gif_main_loop (context) == -1 || context->animation->frames == NULL) { + if (context->error && *(context->error) == NULL) + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF file was missing some data (perhaps it was truncated somehow?)")); - animation = context->animation; + g_object_unref (G_OBJECT (context->animation)); + context->animation = NULL; + } + + if (context->animation) + animation = GDK_PIXBUF_ANIMATION (context->animation); + else + animation = NULL; + + if (context->error && *(context->error)) + g_print ("%s\n", (*(context->error))->message); + + g_free (context->buf); g_free (context); return animation; } diff --git a/gdk-pixbuf/io-ico.c b/gdk-pixbuf/io-ico.c index dc6dadb2f8..6a2ca48840 100644 --- a/gdk-pixbuf/io-ico.c +++ b/gdk-pixbuf/io-ico.c @@ -156,8 +156,6 @@ struct ico_progressive_state { static gpointer gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error); @@ -178,8 +176,8 @@ gdk_pixbuf__ico_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, - NULL, NULL, error); + State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, error); + if (State == NULL) return NULL; @@ -363,6 +361,7 @@ static void DecodeHeader(guchar *Data, gint Bytes, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ (*State->prepared_func) (State->pixbuf, + NULL, State->user_data); } @@ -378,8 +377,6 @@ static void DecodeHeader(guchar *Data, gint Bytes, static gpointer gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-jpeg.c b/gdk-pixbuf/io-jpeg.c index 2c295131d9..c707d347af 100644 --- a/gdk-pixbuf/io-jpeg.c +++ b/gdk-pixbuf/io-jpeg.c @@ -96,8 +96,6 @@ typedef struct { static GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f, GError **error); static gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc func, ModuleUpdatedNotifyFunc func2, - ModuleFrameDoneNotifyFunc func3, - ModuleAnimationDoneNotifyFunc func4, gpointer user_data, GError **error); static gboolean gdk_pixbuf__jpeg_image_stop_load (gpointer context, GError **error); @@ -321,8 +319,6 @@ skip_input_data (j_decompress_ptr cinfo, long num_bytes) gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -539,6 +535,7 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data, /* Notify the client that we are ready to go */ (* context->prepared_func) (context->pixbuf, + NULL, context->user_data); } else if (!context->did_prescan) { diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c index 9ece35d40d..21b1d4b296 100644 --- a/gdk-pixbuf/io-png.c +++ b/gdk-pixbuf/io-png.c @@ -324,8 +324,6 @@ struct _LoadContext { static gpointer gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -540,7 +538,7 @@ png_info_callback (png_structp png_read_ptr, /* Notify the client that we are ready to go */ if (lc->prepare_func) - (* lc->prepare_func) (lc->pixbuf, lc->notify_user_data); + (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data); return; } diff --git a/gdk-pixbuf/io-pnm.c b/gdk-pixbuf/io-pnm.c index 87c392db5a..cd7dd9d9bd 100644 --- a/gdk-pixbuf/io-pnm.c +++ b/gdk-pixbuf/io-pnm.c @@ -84,8 +84,6 @@ typedef struct { static GdkPixbuf *gdk_pixbuf__pnm_image_load (FILE *f, GError **error); static gpointer gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc func, ModuleUpdatedNotifyFunc func2, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__pnm_image_stop_load (gpointer context, GError **error); @@ -763,8 +761,6 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -923,6 +919,7 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data, /* Notify the client that we are ready to go */ (* context->prepared_func) (context->pixbuf, + NULL, context->user_data); } diff --git a/gdk-pixbuf/io-ras.c b/gdk-pixbuf/io-ras.c index 25a3c0b7a5..84b7c27f92 100644 --- a/gdk-pixbuf/io-ras.c +++ b/gdk-pixbuf/io-ras.c @@ -96,8 +96,6 @@ struct ras_progressive_state { static gpointer gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error); @@ -116,8 +114,7 @@ static GdkPixbuf *gdk_pixbuf__ras_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__ras_image_begin_load(NULL, NULL, NULL, - NULL, NULL, error); + State = gdk_pixbuf__ras_image_begin_load(NULL, NULL, NULL, error); membuf = g_malloc(4096); @@ -195,6 +192,7 @@ static void RAS2State(struct rasterfile *RAS, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ (*State->prepared_func) (State->pixbuf, + NULL, State->user_data); } @@ -219,8 +217,6 @@ static void RAS2State(struct rasterfile *RAS, static gpointer gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-tiff.c b/gdk-pixbuf/io-tiff.c index 9b23f5d719..51040dcdad 100644 --- a/gdk-pixbuf/io-tiff.c +++ b/gdk-pixbuf/io-tiff.c @@ -92,7 +92,7 @@ gdk_pixbuf__tiff_image_load_real (FILE *f, TiffData *context, GError **error) } if (context) - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, NULL, context->user_data); /* Yes, it needs to be _TIFFMalloc... */ rast = (uint32 *) _TIFFmalloc (num_pixs * sizeof (uint32)); @@ -163,8 +163,6 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__tiff_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-wbmp.c b/gdk-pixbuf/io-wbmp.c index 1b7784b13d..1537d83d62 100644 --- a/gdk-pixbuf/io-wbmp.c +++ b/gdk-pixbuf/io-wbmp.c @@ -65,9 +65,7 @@ struct wbmp_progressive_state { static gpointer gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error); static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data, GError **error); @@ -87,7 +85,7 @@ static GdkPixbuf *gdk_pixbuf__wbmp_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__wbmp_image_begin_load(NULL, NULL, NULL, NULL, NULL, + State = gdk_pixbuf__wbmp_image_begin_load(NULL, NULL, NULL, error); if (State == NULL) @@ -120,9 +118,7 @@ static GdkPixbuf *gdk_pixbuf__wbmp_image_load(FILE * f, GError **error) static gpointer gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error) { struct wbmp_progressive_state *context; @@ -285,7 +281,7 @@ static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data, g_assert(context->pixbuf); if(context->prepared_func) - context->prepared_func(context->pixbuf, context->user_data); + context->prepared_func(context->pixbuf, NULL, context->user_data); } } else if(context->needmore) diff --git a/gdk-pixbuf/io-xbm.c b/gdk-pixbuf/io-xbm.c index 05d8f994a4..0041488eb8 100644 --- a/gdk-pixbuf/io-xbm.c +++ b/gdk-pixbuf/io-xbm.c @@ -300,7 +300,7 @@ gdk_pixbuf__xbm_image_load_real (FILE *f, XBMData *context, GError **error) row_stride = gdk_pixbuf_get_rowstride (pixbuf); if (context) - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, NULL, context->user_data); /* Initialize PIXBUF */ @@ -355,8 +355,6 @@ gdk_pixbuf__xbm_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__xbm_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-xpm.c b/gdk-pixbuf/io-xpm.c index 0ddc3c234e..1f99088c3d 100644 --- a/gdk-pixbuf/io-xpm.c +++ b/gdk-pixbuf/io-xpm.c @@ -1424,8 +1424,6 @@ struct _XPMContext static gpointer gdk_pixbuf__xpm_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -1471,7 +1469,9 @@ gdk_pixbuf__xpm_image_stop_load (gpointer data, pixbuf = gdk_pixbuf__xpm_image_load (context->file, error); if (pixbuf != NULL) { - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, + NULL, + context->user_data); (* context->update_func) (pixbuf, 0, 0, pixbuf->width, pixbuf->height, context->user_data); gdk_pixbuf_unref (pixbuf); diff --git a/gdk-pixbuf/pixops/pixops.c b/gdk-pixbuf/pixops/pixops.c index ea19607153..7a13e043ba 100644 --- a/gdk-pixbuf/pixops/pixops.c +++ b/gdk-pixbuf/pixops/pixops.c @@ -1,4 +1,5 @@ #include +#include #include "config.h" #include "pixops.h" @@ -93,6 +94,7 @@ pixops_scale_nearest (guchar *dest_buf, for (i = 0; i < (render_y1 - render_y0); i++) { const guchar *src = src_buf + (((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride; + /* FIXME Owen needs to look at this */ guchar *dest = dest_buf + i * dest_rowstride; x = render_x0 * x_step + x_step / 2; @@ -160,7 +162,6 @@ pixops_composite_nearest (guchar *dest_buf, for (i = 0; i < (render_y1 - render_y0); i++) { const guchar *src = src_buf + (((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride; - /* FIXME Owen needs to look at this */ guchar *dest = dest_buf + i * dest_rowstride; x = render_x0 * x_step + x_step / 2; @@ -183,9 +184,9 @@ pixops_composite_nearest (guchar *dest_buf, if (w != 0) { - dest[0] = (w0 * src[0] + w1 * dest[0]) / w; - dest[1] = (w0 * src[1] + w1 * dest[1]) / w; - dest[2] = (w0 * src[2] + w1 * dest[2]) / w; + dest[0] = (w0 * p[0] + w1 * dest[0]) / w; + dest[1] = (w0 * p[1] + w1 * dest[1]) / w; + dest[2] = (w0 * p[2] + w1 * dest[2]) / w; dest[3] = w / 0xff; } else @@ -274,25 +275,39 @@ pixops_composite_color_nearest (guchar *dest_buf, for (j=0 ; j < (render_x1 - render_x0); j++) { const guchar *p = src + (x >> SCALE_SHIFT) * src_channels; - unsigned int a0; + int a0; + int tmp; if (src_has_alpha) a0 = (p[3] * overall_alpha + 0xff) >> 8; else a0 = overall_alpha; - if (((j + check_x) >> check_shift) & 1) + if (a0 == 255) { - dest[0] = r2 + ((a0 * ((int)p[0] - r2) + 0xff) >> 8); - dest[1] = g2 + ((a0 * ((int)p[1] - g2) + 0xff) >> 8); - dest[2] = b2 + ((a0 * ((int)p[2] - b2) + 0xff) >> 8); + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; } else - { - dest[0] = r1 + ((a0 * ((int)p[0] - r1) + 0xff) >> 8); - dest[1] = g1 + ((a0 * ((int)p[1] - g1) + 0xff) >> 8); - dest[2] = b1 + ((a0 * ((int)p[2] - b1) + 0xff) >> 8); - } + if (((j + check_x) >> check_shift) & 1) + { + tmp = ((int) p[0] - r2) * a0; + dest[0] = r2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[1] - g2) * a0; + dest[1] = g2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[2] - b2) * a0; + dest[2] = b2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + } + else + { + tmp = ((int) p[0] - r1) * a0; + dest[0] = r1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[1] - g1) * a0; + dest[1] = g1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[2] - b1) * a0; + dest[2] = b1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + } if (dest_channels == 4) dest[3] = 0xff; @@ -1003,7 +1018,7 @@ pixops_process (guchar *dest_buf, dest_x += (new_outbuf - outbuf) / dest_channels; - x = dest_x * x_step + scaled_x_offset; + x = (dest_x - check_x + render_x0) * x_step + scaled_x_offset; outbuf = new_outbuf; while (outbuf < outbuf_end) diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c index 9a6f65cbfd..706fffd2c3 100644 --- a/gtk/gtkcellrenderertext.c +++ b/gtk/gtkcellrenderertext.c @@ -70,6 +70,7 @@ enum { PROP_STRETCH, PROP_SIZE, PROP_SIZE_POINTS, + PROP_SCALE, PROP_EDITABLE, PROP_STRIKETHROUGH, PROP_UNDERLINE, @@ -84,6 +85,7 @@ enum { PROP_WEIGHT_SET, PROP_STRETCH_SET, PROP_SIZE_SET, + PROP_SCALE_SET, PROP_EDITABLE_SET, PROP_STRIKETHROUGH_SET, PROP_UNDERLINE_SET, @@ -282,6 +284,16 @@ gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class) G_MAXDOUBLE, 0.0, G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_SCALE, + g_param_spec_double ("scale", + _("Font scale"), + _("Font scaling factor"), + 0.0, + G_MAXDOUBLE, + 1.0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property (object_class, PROP_RISE, @@ -351,6 +363,10 @@ gtk_cell_renderer_text_class_init (GtkCellRendererTextClass *class) _("Font size set"), _("Whether this tag affects the font size")); + ADD_SET_PROP ("scale_set", PROP_SCALE_SET, + _("Font scale set"), + _("Whether this tag scales the font size by a factor")); + ADD_SET_PROP ("rise_set", PROP_RISE_SET, _("Rise set"), _("Whether this tag affects the rise")); @@ -462,6 +478,10 @@ gtk_cell_renderer_text_get_property (GObject *object, g_value_set_double (value, ((double)celltext->font.size) / (double)PANGO_SCALE); break; + case PROP_SCALE: + g_value_set_double (value, celltext->font_scale); + break; + case PROP_EDITABLE: g_value_set_boolean (value, celltext->editable); break; @@ -510,6 +530,10 @@ gtk_cell_renderer_text_get_property (GObject *object, g_value_set_boolean (value, celltext->size_set); break; + case PROP_SCALE_SET: + g_value_set_boolean (value, celltext->scale_set); + break; + case PROP_EDITABLE_SET: g_value_set_boolean (value, celltext->editable_set); break; @@ -824,6 +848,11 @@ gtk_cell_renderer_text_set_property (GObject *object, g_object_notify (G_OBJECT (celltext), "font"); break; + case PROP_SCALE: + celltext->font_scale = g_value_get_double (value); + celltext->scale_set = TRUE; + break; + case PROP_SIZE_POINTS: celltext->font.size = g_value_get_double (value) * PANGO_SCALE; @@ -849,6 +878,7 @@ gtk_cell_renderer_text_set_property (GObject *object, celltext->underline_style = g_value_get_enum (value); celltext->underline_set = TRUE; g_object_notify (G_OBJECT (celltext), "underline_set"); + break; case PROP_RISE: @@ -859,62 +889,54 @@ gtk_cell_renderer_text_set_property (GObject *object, case PROP_BACKGROUND_SET: celltext->background_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "background_set"); break; case PROP_FOREGROUND_SET: celltext->foreground_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "foreground_set"); break; case PROP_FAMILY_SET: celltext->family_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "family_set"); break; case PROP_STYLE_SET: celltext->style_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "style_set"); break; case PROP_VARIANT_SET: celltext->variant_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "variant_set"); break; case PROP_WEIGHT_SET: celltext->weight_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "weight_set"); break; case PROP_STRETCH_SET: celltext->stretch_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "stretch_set"); break; case PROP_SIZE_SET: celltext->size_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "size_set"); break; + case PROP_SCALE_SET: + celltext->scale_set = g_value_get_boolean (value); + break; + case PROP_EDITABLE_SET: celltext->editable_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "editable_set"); break; case PROP_STRIKETHROUGH_SET: celltext->strikethrough_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "strikethrough_set"); break; case PROP_UNDERLINE_SET: celltext->underline_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "underline_set"); break; case PROP_RISE_SET: celltext->rise_set = g_value_get_boolean (value); - g_object_notify(G_OBJECT(object), "rise_set"); break; default: @@ -1012,6 +1034,10 @@ get_layout (GtkCellRendererText *celltext, celltext->font.size >= 0) add_attr (attr_list, pango_attr_size_new (celltext->font.size)); + if (celltext->scale_set && + celltext->font_scale != 1.0) + add_attr (attr_list, pango_attr_scale_new (celltext->font_scale)); + if (celltext->underline_set) uline = celltext->underline_style; else diff --git a/gtk/gtkcellrenderertext.h b/gtk/gtkcellrenderertext.h index fd906a3350..3ec1852288 100644 --- a/gtk/gtkcellrenderertext.h +++ b/gtk/gtkcellrenderertext.h @@ -44,6 +44,7 @@ struct _GtkCellRendererText /*< private >*/ gchar *text; PangoFontDescription font; + gdouble font_scale; PangoColor foreground; PangoColor background; @@ -66,6 +67,8 @@ struct _GtkCellRendererText guint stretch_set : 1; guint size_set : 1; + guint scale_set : 1; + guint foreground_set : 1; guint background_set : 1; diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c index 802b85e017..f888e65113 100644 --- a/gtk/gtkcheckbutton.c +++ b/gtk/gtkcheckbutton.c @@ -246,8 +246,8 @@ gtk_check_button_size_request (GtkWidget *widget, gint indicator_spacing; gint border_width = GTK_CONTAINER (widget)->border_width; - requisition->width = border_width + 2; - requisition->height = border_width + 2; + requisition->width = border_width * 2 + 2; + requisition->height = border_width * 2 + 2; child = GTK_BIN (widget)->child; if (child && GTK_WIDGET_VISIBLE (child)) @@ -255,7 +255,7 @@ gtk_check_button_size_request (GtkWidget *widget, GtkRequisition child_requisition; gtk_widget_size_request (child, &child_requisition); - + requisition->width += child_requisition.width; requisition->height += child_requisition.height; } diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c index a894e9fc5c..4f1e61ac4f 100644 --- a/gtk/gtkcolorsel.c +++ b/gtk/gtkcolorsel.c @@ -1848,7 +1848,7 @@ gtk_color_selection_destroy (GtkObject *object) if (priv->tooltips) { - gtk_object_destroy (priv->tooltips); + gtk_object_destroy (GTK_OBJECT (priv->tooltips)); priv->tooltips = NULL; } diff --git a/gtk/gtkhscale.c b/gtk/gtkhscale.c index 0fa644b68a..29f6321fbf 100644 --- a/gtk/gtkhscale.c +++ b/gtk/gtkhscale.c @@ -523,7 +523,6 @@ gtk_hscale_draw_value (GtkScale *scale) { GtkStateType state_type; GtkWidget *widget; - gchar buffer[32]; gint width, height; gint x, y; @@ -536,9 +535,14 @@ gtk_hscale_draw_value (GtkScale *scale) { PangoLayout *layout; PangoRectangle logical_rect; + gchar *txt; + + txt = _gtk_scale_format_value (scale, + GTK_RANGE (scale)->adjustment->value); + + layout = gtk_widget_create_pango_layout (widget, txt); + g_free (txt); - sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value); - layout = gtk_widget_create_pango_layout (widget, buffer); pango_layout_get_pixel_extents (layout, NULL, &logical_rect); switch (scale->value_pos) diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c index 81f0debc18..e5890a16c4 100644 --- a/gtk/gtkimage.c +++ b/gtk/gtkimage.c @@ -28,11 +28,13 @@ #include "gtkimage.h" #include "gtkiconfactory.h" #include "gtkstock.h" +#include static void gtk_image_class_init (GtkImageClass *klass); static void gtk_image_init (GtkImage *image); static gint gtk_image_expose (GtkWidget *widget, GdkEventExpose *event); +static void gtk_image_unmap (GtkWidget *widget); static void gtk_image_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_image_destroy (GtkObject *object); @@ -85,6 +87,7 @@ gtk_image_class_init (GtkImageClass *class) widget_class->expose_event = gtk_image_expose; widget_class->size_request = gtk_image_size_request; + widget_class->unmap = gtk_image_unmap; } static void @@ -163,11 +166,22 @@ gtk_image_new_from_image (GdkImage *gdk_image, * gtk_image_new_from_file: * @filename: a filename * - * Creates a new #GtkImage displaying the file @filename. If the - * file isn't found or can't be loaded, the #GtkImage will display - * a "broken image" icon. If you need to detect failures to load - * the file, use gdk_pixbuf_new_from_file() to load the file yourself, - * then create the #GtkImage from the pixbuf. + * Creates a new #GtkImage displaying the file @filename. If the file + * isn't found or can't be loaded, the resulting #GtkImage will + * display a "broken image" icon. This function never returns %NULL, + * it always returns a valid #GtkImage widget. + * + * If the file contains an animation, the image will contain an + * animation. + * + * If you need to detect failures to load the file, use + * gdk_pixbuf_new_from_file() to load the file yourself, then create + * the #GtkImage from the pixbuf. (Or for animations, use + * gdk_pixbuf_animation_new_from_file()). + * + * The storage type (gtk_image_get_storage_type()) of the returned + * image is not defined, it will be whatever is appropriate for + * displaying the file. * * Return value: a new #GtkImage **/ @@ -194,7 +208,7 @@ gtk_image_new_from_file (const gchar *filename) * * Note that this function just creates an #GtkImage from the pixbuf. The * #GtkImage created will not react to state changes. Should you want that, you - * should use @gtk_image_new_from_icon_set. + * should use gtk_image_new_from_icon_set(). * * Return value: a new #GtkImage **/ @@ -268,6 +282,31 @@ gtk_image_new_from_icon_set (GtkIconSet *icon_set, return GTK_WIDGET (image); } +/** + * gtk_image_new_from_animation: + * @animation: an animation + * + * Creates a #GtkImage displaying the given animation. + * The #GtkImage does not assume a reference to the + * animation; you still need to unref it if you own references. + * #GtkImage will add its own reference rather than adopting yours. + * + * Return value: a new #GtkImage widget + **/ +GtkWidget* +gtk_image_new_from_animation (GdkPixbufAnimation *animation) +{ + GtkImage *image; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL); + + image = gtk_type_new (GTK_TYPE_IMAGE); + + gtk_image_set_from_animation (image, animation); + + return GTK_WIDGET (image); +} + /** * gtk_image_set_from_pixmap: * @image: a #GtkImage @@ -376,7 +415,7 @@ void gtk_image_set_from_file (GtkImage *image, const gchar *filename) { - GdkPixbuf *pixbuf; + GdkPixbufAnimation *anim; g_return_if_fail (GTK_IS_IMAGE (image)); g_return_if_fail (filename != NULL); @@ -386,9 +425,9 @@ gtk_image_set_from_file (GtkImage *image, if (filename == NULL) return; - pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + anim = gdk_pixbuf_animation_new_from_file (filename, NULL); - if (pixbuf == NULL) + if (anim == NULL) { gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, @@ -396,9 +435,22 @@ gtk_image_set_from_file (GtkImage *image, return; } - gtk_image_set_from_pixbuf (image, pixbuf); + /* We could just unconditionally set_from_animation, + * but it's nicer for memory if we toss the animation + * if it's just a single pixbuf + */ - g_object_unref (G_OBJECT (pixbuf)); + if (gdk_pixbuf_animation_is_static_image (anim)) + { + gtk_image_set_from_pixbuf (image, + gdk_pixbuf_animation_get_static_image (anim)); + } + else + { + gtk_image_set_from_animation (image, anim); + } + + g_object_unref (G_OBJECT (anim)); } /** @@ -500,6 +552,41 @@ gtk_image_set_from_icon_set (GtkImage *image, } } +/** + * gtk_image_set_from_animation: + * @image: a #GtkImage + * @animation: the #GdkPixbufAnimation + * + * Causes the #GtkImage to display the given animation (or display + * nothing, if you set the animation to %NULL). + **/ +void +gtk_image_set_from_animation (GtkImage *image, + GdkPixbufAnimation *animation) +{ + g_return_if_fail (GTK_IS_IMAGE (image)); + g_return_if_fail (animation == NULL || + GDK_IS_PIXBUF_ANIMATION (animation)); + + if (animation) + g_object_ref (G_OBJECT (animation)); + + gtk_image_reset (image); + + if (animation != NULL) + { + image->storage_type = GTK_IMAGE_ANIMATION; + + image->data.anim.anim = animation; + image->data.anim.frame_timeout = 0; + image->data.anim.iter = NULL; + + gtk_image_update_size (image, + gdk_pixbuf_animation_get_width (animation), + gdk_pixbuf_animation_get_height (animation)); + } +} + /** * gtk_image_get_storage_type: * @image: a #GtkImage @@ -660,6 +747,33 @@ gtk_image_get_icon_set (GtkImage *image, *size = image->data.icon_set.size; } +/** + * gtk_image_get_animation: + * @image: a #GtkImage + * + * + * Gets the #GdkPixbufAnimation being displayed by the #GtkImage. + * The storage type of the image must be %GTK_IMAGE_EMPTY or + * %GTK_IMAGE_ANIMATION (see gtk_image_get_storage_type()). + * The caller of this function does not own a reference to the + * returned animation. + * + * Return value: the displayed animation, or %NULL if the image is empty + **/ +GdkPixbufAnimation* +gtk_image_get_animation (GtkImage *image) +{ + g_return_val_if_fail (GTK_IS_IMAGE (image), NULL); + g_return_val_if_fail (image->storage_type == GTK_IMAGE_ANIMATION || + image->storage_type == GTK_IMAGE_EMPTY, + NULL); + + if (image->storage_type == GTK_IMAGE_EMPTY) + image->data.anim.anim = NULL; + + return image->data.anim.anim; +} + GtkWidget* gtk_image_new (GdkImage *val, GdkBitmap *mask) @@ -695,6 +809,55 @@ gtk_image_get (GtkImage *image, gtk_image_get_image (image, val, mask); } +static void +gtk_image_unmap (GtkWidget *widget) +{ + GtkImage *image; + + image = GTK_IMAGE (widget); + + if (image->storage_type == GTK_IMAGE_ANIMATION) + { + /* Reset the animation */ + + if (image->data.anim.frame_timeout) + { + g_source_remove (image->data.anim.frame_timeout); + image->data.anim.frame_timeout = 0; + } + + if (image->data.anim.iter) + { + g_object_unref (G_OBJECT (image->data.anim.iter)); + image->data.anim.iter = NULL; + } + } + + if (GTK_WIDGET_CLASS (parent_class)->unmap) + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +gint +animation_timeout (gpointer data) +{ + GtkImage *image; + + image = GTK_IMAGE (data); + + image->data.anim.frame_timeout = 0; + + gdk_pixbuf_animation_iter_advance (image->data.anim.iter, NULL); + + if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0) + image->data.anim.frame_timeout = + g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter), + animation_timeout, + image); + + gtk_widget_queue_draw (GTK_WIDGET (image)); + + return FALSE; +} static gint gtk_image_expose (GtkWidget *widget, @@ -777,6 +940,24 @@ gtk_image_expose (GtkWidget *widget, image_bound.height = gdk_pixbuf_get_height (stock_pixbuf); } break; + + case GTK_IMAGE_ANIMATION: + { + if (image->data.anim.iter == NULL) + { + image->data.anim.iter = gdk_pixbuf_animation_get_iter (image->data.anim.anim, NULL); + + if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0) + image->data.anim.frame_timeout = + g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter), + animation_timeout, + image); + } + + image_bound.width = gdk_pixbuf_animation_get_width (image->data.anim.anim); + image_bound.height = gdk_pixbuf_animation_get_height (image->data.anim.anim); + } + break; default: break; @@ -849,6 +1030,25 @@ gtk_image_expose (GtkWidget *widget, } break; + case GTK_IMAGE_ANIMATION: + /* don't advance the anim iter here, or we could get frame changes between two + * exposes of different areas. + */ + + gdk_pixbuf_render_to_drawable_alpha (gdk_pixbuf_animation_iter_get_pixbuf (image->data.anim.iter), + widget->window, + image_bound.x - x, + image_bound.y - y, + image_bound.x, + image_bound.y, + image_bound.width, + image_bound.height, + GDK_PIXBUF_ALPHA_FULL, + 128, + GDK_RGB_DITHER_NORMAL, + 0, 0); + break; + default: break; } @@ -876,9 +1076,6 @@ gtk_image_clear (GtkImage *image) if (image->data.pixmap.mask) g_object_unref (G_OBJECT (image->data.pixmap.mask)); - image->data.pixmap.pixmap = NULL; - image->data.pixmap.mask = NULL; - break; case GTK_IMAGE_IMAGE: @@ -889,9 +1086,6 @@ gtk_image_clear (GtkImage *image) if (image->data.image.mask) g_object_unref (G_OBJECT (image->data.image.mask)); - image->data.image.image = NULL; - image->data.image.mask = NULL; - break; case GTK_IMAGE_PIXBUF: @@ -899,27 +1093,27 @@ gtk_image_clear (GtkImage *image) if (image->data.pixbuf.pixbuf) g_object_unref (G_OBJECT (image->data.pixbuf.pixbuf)); - image->data.pixbuf.pixbuf = NULL; - break; case GTK_IMAGE_STOCK: g_free (image->data.stock.stock_id); - - image->data.stock.stock_id = NULL; - image->data.stock.size = 0; break; case GTK_IMAGE_ICON_SET: if (image->data.icon_set.icon_set) gtk_icon_set_unref (image->data.icon_set.icon_set); - - image->data.icon_set.size = 0; - image->data.icon_set.icon_set = NULL; break; + + case GTK_IMAGE_ANIMATION: + if (image->data.anim.frame_timeout) + g_source_remove (image->data.anim.frame_timeout); + + if (image->data.anim.anim) + g_object_unref (G_OBJECT (image->data.anim.anim)); + break; case GTK_IMAGE_EMPTY: default: @@ -928,6 +1122,8 @@ gtk_image_clear (GtkImage *image) } image->storage_type = GTK_IMAGE_EMPTY; + + memset (&image->data, '\0', sizeof (image->data)); } static void diff --git a/gtk/gtkimage.h b/gtk/gtkimage.h index bc1a0c0a60..71c80dfd0a 100644 --- a/gtk/gtkimage.h +++ b/gtk/gtkimage.h @@ -52,6 +52,7 @@ typedef struct _GtkImageImageData GtkImageImageData; typedef struct _GtkImagePixbufData GtkImagePixbufData; typedef struct _GtkImageStockData GtkImageStockData; typedef struct _GtkImageIconSetData GtkImageIconSetData; +typedef struct _GtkImageAnimationData GtkImageAnimationData; struct _GtkImagePixmapData { @@ -82,6 +83,13 @@ struct _GtkImageIconSetData GtkIconSize size; }; +struct _GtkImageAnimationData +{ + GdkPixbufAnimation *anim; + GdkPixbufAnimationIter *iter; + guint frame_timeout; +}; + typedef enum { GTK_IMAGE_EMPTY, @@ -89,7 +97,8 @@ typedef enum GTK_IMAGE_IMAGE, GTK_IMAGE_PIXBUF, GTK_IMAGE_STOCK, - GTK_IMAGE_ICON_SET + GTK_IMAGE_ICON_SET, + GTK_IMAGE_ANIMATION } GtkImageType; struct _GtkImage @@ -105,6 +114,7 @@ struct _GtkImage GtkImagePixbufData pixbuf; GtkImageStockData stock; GtkImageIconSetData icon_set; + GtkImageAnimationData anim; } data; }; @@ -115,33 +125,36 @@ struct _GtkImageClass GtkType gtk_image_get_type (void) G_GNUC_CONST; -GtkWidget* gtk_image_new_from_pixmap (GdkPixmap *pixmap, - GdkBitmap *mask); -GtkWidget* gtk_image_new_from_image (GdkImage *image, - GdkBitmap *mask); -GtkWidget* gtk_image_new_from_file (const gchar *filename); -GtkWidget* gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf); -GtkWidget* gtk_image_new_from_stock (const gchar *stock_id, - GtkIconSize size); -GtkWidget* gtk_image_new_from_icon_set (GtkIconSet *icon_set, - GtkIconSize size); +GtkWidget* gtk_image_new_from_pixmap (GdkPixmap *pixmap, + GdkBitmap *mask); +GtkWidget* gtk_image_new_from_image (GdkImage *image, + GdkBitmap *mask); +GtkWidget* gtk_image_new_from_file (const gchar *filename); +GtkWidget* gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf); +GtkWidget* gtk_image_new_from_stock (const gchar *stock_id, + GtkIconSize size); +GtkWidget* gtk_image_new_from_icon_set (GtkIconSet *icon_set, + GtkIconSize size); +GtkWidget* gtk_image_new_from_animation (GdkPixbufAnimation *animation); -void gtk_image_set_from_pixmap (GtkImage *image, - GdkPixmap *pixmap, - GdkBitmap *mask); -void gtk_image_set_from_image (GtkImage *image, - GdkImage *gdk_image, - GdkBitmap *mask); -void gtk_image_set_from_file (GtkImage *image, - const gchar *filename); -void gtk_image_set_from_pixbuf (GtkImage *image, - GdkPixbuf *pixbuf); -void gtk_image_set_from_stock (GtkImage *image, - const gchar *stock_id, - GtkIconSize size); -void gtk_image_set_from_icon_set (GtkImage *image, - GtkIconSet *icon_set, - GtkIconSize size); +void gtk_image_set_from_pixmap (GtkImage *image, + GdkPixmap *pixmap, + GdkBitmap *mask); +void gtk_image_set_from_image (GtkImage *image, + GdkImage *gdk_image, + GdkBitmap *mask); +void gtk_image_set_from_file (GtkImage *image, + const gchar *filename); +void gtk_image_set_from_pixbuf (GtkImage *image, + GdkPixbuf *pixbuf); +void gtk_image_set_from_stock (GtkImage *image, + const gchar *stock_id, + GtkIconSize size); +void gtk_image_set_from_icon_set (GtkImage *image, + GtkIconSet *icon_set, + GtkIconSize size); +void gtk_image_set_from_animation (GtkImage *image, + GdkPixbufAnimation *animation); GtkImageType gtk_image_get_storage_type (GtkImage *image); @@ -158,6 +171,7 @@ void gtk_image_get_stock (GtkImage *image, void gtk_image_get_icon_set (GtkImage *image, GtkIconSet **icon_set, GtkIconSize *size); +GdkPixbufAnimation* gtk_image_get_animation (GtkImage *image); #ifndef GTK_DISABLE_DEPRECATED diff --git a/gtk/gtkmarshal.list b/gtk/gtkmarshal.list index 12ec4108db..cb656a25cd 100644 --- a/gtk/gtkmarshal.list +++ b/gtk/gtkmarshal.list @@ -36,6 +36,7 @@ NONE:INT,INT NONE:NONE NONE:POINTER NONE:STRING,INT,POINTER +STRING:DOUBLE VOID:BOOLEAN VOID:BOXED VOID:BOXED,BOXED diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 12ec4108db..cb656a25cd 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -36,6 +36,7 @@ NONE:INT,INT NONE:NONE NONE:POINTER NONE:STRING,INT,POINTER +STRING:DOUBLE VOID:BOOLEAN VOID:BOXED VOID:BOXED,BOXED diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h index b9c27307b9..58fb89a386 100644 --- a/gtk/gtkrange.h +++ b/gtk/gtkrange.h @@ -91,13 +91,14 @@ struct _GtkRangeClass guint8 slider; guint8 step_forw; guint8 step_back; - + /* action signals for keybindings */ void (* move_slider) (GtkRange *range, GtkScrollType scroll, GtkTroughType trough); /* Completely broken virtual functions, please ignore */ + void (* draw_background) (GtkRange *range); void (* clear_background) (GtkRange *range); void (* draw_trough) (GtkRange *range); diff --git a/gtk/gtkscale.c b/gtk/gtkscale.c index a2769b2851..992cdfe41d 100644 --- a/gtk/gtkscale.c +++ b/gtk/gtkscale.c @@ -27,6 +27,7 @@ #include #include "gtkintl.h" #include "gtkscale.h" +#include "gtkmarshal.h" enum { ARG_0, @@ -35,6 +36,12 @@ enum { ARG_VALUE_POS }; +enum { + FORMAT_VALUE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; static void gtk_scale_class_init (GtkScaleClass *klass); static void gtk_scale_init (GtkScale *scale); @@ -78,6 +85,22 @@ gtk_scale_get_type (void) return scale_type; } +gboolean +single_string_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gchar *str; + + str = g_value_get_string (handler_return); + g_value_set_string (return_accu, str); + continue_emission = str == NULL; + + return continue_emission; +} + static void gtk_scale_class_init (GtkScaleClass *class) { @@ -104,6 +127,16 @@ gtk_scale_class_init (GtkScaleClass *class) GTK_ARG_READWRITE, ARG_VALUE_POS); + signals[FORMAT_VALUE] = + g_signal_newc ("format_value", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkScaleClass, format_value), + single_string_accumulator, NULL, + gtk_marshal_STRING__DOUBLE, + G_TYPE_STRING, 1, + G_TYPE_DOUBLE); + object_class->set_arg = gtk_scale_set_arg; object_class->get_arg = gtk_scale_get_arg; @@ -280,58 +313,27 @@ gtk_scale_get_value_size (GtkScale *scale, { PangoLayout *layout; PangoRectangle logical_rect; - gchar buffer[128]; - gdouble value; - gint digits; - gint i, j; + gchar *txt; range = GTK_RANGE (scale); layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL); - value = ABS (range->adjustment->lower); - if (value == 0) value = 1; - digits = log10 (value) + 1; - if (digits > 13) - digits = 13; - - i = 0; - if (range->adjustment->lower < 0) - buffer[i++] = '-'; - for (j = 0; j < digits; j++) - buffer[i++] = '0'; - if (GTK_RANGE (scale)->digits) - buffer[i++] = '.'; - for (j = 0; j < GTK_RANGE (scale)->digits; j++) - buffer[i++] = '0'; - buffer[i] = '\0'; - - pango_layout_set_text (layout, buffer, i); + txt = _gtk_scale_format_value (scale, range->adjustment->lower); + pango_layout_set_text (layout, txt, -1); + g_free (txt); + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); if (width) *width = logical_rect.width; if (height) - *height = logical_rect.width; + *height = logical_rect.height; + + txt = _gtk_scale_format_value (scale, range->adjustment->upper); + pango_layout_set_text (layout, txt, -1); + g_free (txt); - value = ABS (range->adjustment->upper); - if (value == 0) value = 1; - digits = log10 (value) + 1; - if (digits > 13) - digits = 13; - - i = 0; - if (range->adjustment->upper < 0) - buffer[i++] = '-'; - for (j = 0; j < digits; j++) - buffer[i++] = '0'; - if (GTK_RANGE (scale)->digits) - buffer[i++] = '.'; - for (j = 0; j < GTK_RANGE (scale)->digits; j++) - buffer[i++] = '0'; - buffer[i] = '\0'; - - pango_layout_set_text (layout, buffer, i); pango_layout_get_pixel_extents (layout, NULL, &logical_rect); if (width) @@ -383,3 +385,32 @@ gtk_scale_draw_background (GtkRange *range) gtk_scale_draw_value (GTK_SCALE (range)); } + +/** + * _gtk_scale_format_value: + * @scale: a #GtkScale + * @value: adjustment value + * + * Emits "format_value" signal to format the value, if no user + * signal handlers, falls back to a default format. + * + * Return value: formatted value + **/ +gchar* +_gtk_scale_format_value (GtkScale *scale, + gdouble value) +{ + gchar *fmt = NULL; + + g_signal_emit (G_OBJECT (scale), + signals[FORMAT_VALUE], + 0, + value, + &fmt); + + if (fmt) + return fmt; + else + return g_strdup_printf ("%0.*f", GTK_RANGE (scale)->digits, + value); +} diff --git a/gtk/gtkscale.h b/gtk/gtkscale.h index fd9e97446d..b6cd719547 100644 --- a/gtk/gtkscale.h +++ b/gtk/gtkscale.h @@ -61,6 +61,9 @@ struct _GtkScaleClass GtkRangeClass parent_class; gint value_spacing; + + gchar* (* format_value) (GtkRange *range, + gdouble value); void (* draw_value) (GtkScale *scale); }; @@ -79,6 +82,9 @@ void gtk_scale_get_value_size (GtkScale *scale, void gtk_scale_draw_value (GtkScale *scale); +gchar *_gtk_scale_format_value (GtkScale *scale, + gdouble value); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 1074ca0f9a..2e30d026ac 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -2106,6 +2106,127 @@ gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer, gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end); } +static gint +pointer_cmp (gconstpointer a, + gconstpointer b) +{ + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +/** + * gtk_text_buffer_remove_all_tags: + * @buffer: a #GtkTextBuffer + * @start: one bound of range to be untagged + * @end: other bound of range to be untagged + * + * Removes all tags in the range between @start and @end. Be careful + * with this function; it could remove tags added in code unrelated to + * the code you're currently writing. That is, using this function is + * probably a bad idea if you have two or more unrelated code sections + * that add tags. + **/ +void +gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, + const GtkTextIter *start, + const GtkTextIter *end) +{ + GtkTextIter first, second, tmp; + GSList *tags; + GSList *tmp_list; + GSList *prev; + GtkTextTag *tag; + + g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); + g_return_if_fail (start != NULL); + g_return_if_fail (end != NULL); + + first = *start; + second = *end; + + gtk_text_iter_reorder (&first, &second); + + /* Get all tags turned on at the start */ + tags = gtk_text_iter_get_tags (&first); + + /* Find any that are toggled on within the range */ + tmp = first; + while (gtk_text_iter_forward_to_tag_toggle (&tmp, NULL)) + { + GSList *toggled; + GSList *tmp_list2; + + if (gtk_text_iter_compare (&tmp, &second) >= 0) + break; /* past the end of the range */ + + toggled = gtk_text_iter_get_toggled_tags (&tmp, TRUE); + + /* We could end up with a really big-ass list here. + * Fix it someday. + */ + tmp_list2 = toggled; + while (tmp_list2 != NULL) + { + tags = g_slist_prepend (tags, tmp_list2->data); + + tmp_list2 = g_slist_next (tmp_list2); + } + + g_slist_free (toggled); + } + + /* Sort the list */ + tags = g_slist_sort (tags, pointer_cmp); + + /* Strip duplicates */ + tag = NULL; + prev = NULL; + tmp_list = tags; + while (tmp_list != NULL) + { + if (tag == tmp_list->data) + { + /* duplicate */ + if (prev) + prev->next = tmp_list->next; + + tmp_list->next = NULL; + + g_slist_free (tmp_list); + + tmp_list = prev->next; + /* prev is unchanged */ + } + else + { + /* not a duplicate */ + tag = GTK_TEXT_TAG (tmp_list->data); + prev = tmp_list; + tmp_list = tmp_list->next; + } + } + + g_list_foreach (tags, (GFunc) g_object_ref, NULL); + + tmp_list = tags; + while (tmp_list != NULL) + { + tag = GTK_TEXT_TAG (tmp_list->data); + + gtk_text_buffer_remove_tag (buffer, tag, &first, &second); + + tmp_list = tmp_list->next; + } + + g_list_foreach (tags, (GFunc) g_object_unref, NULL); + + g_slist_free (tags); +} + /* * Obtain various iterators diff --git a/gtk/gtktextbuffer.h b/gtk/gtktextbuffer.h index b6f801f119..4368d242aa 100644 --- a/gtk/gtktextbuffer.h +++ b/gtk/gtktextbuffer.h @@ -266,6 +266,9 @@ void gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer, const gchar *name, const GtkTextIter *start, const GtkTextIter *end); +void gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer, + const GtkTextIter *start, + const GtkTextIter *end); /* You can either ignore the return value, or use it to diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c index 241529d389..1d53032146 100644 --- a/gtk/gtktextiter.c +++ b/gtk/gtktextiter.c @@ -1511,7 +1511,7 @@ gtk_text_iter_is_end (const GtkTextIter *iter) } /** - * gtk_text_iter_is_first: + * gtk_text_iter_is_start: * @iter: an iterator * * Returns TRUE if @iter is the first iterator in the buffer, that is @@ -1520,7 +1520,7 @@ gtk_text_iter_is_end (const GtkTextIter *iter) * Return value: whether @iter is the first in the buffer **/ gboolean -gtk_text_iter_is_first (const GtkTextIter *iter) +gtk_text_iter_is_start (const GtkTextIter *iter) { return gtk_text_iter_get_offset (iter) == 0; } @@ -4253,7 +4253,7 @@ lines_window_init (LinesWindow *win, /* If we start on line 1, there are 2 lines to search (0 and 1), so * n_lines can be 2. */ - if (gtk_text_iter_is_first (start) || + if (gtk_text_iter_is_start (start) || gtk_text_iter_get_line (start) + 1 < win->n_lines) { /* Already at the end, or not enough lines to match */ diff --git a/gtk/gtktextiter.h b/gtk/gtktextiter.h index 00285e287b..8a883f9b23 100644 --- a/gtk/gtktextiter.h +++ b/gtk/gtktextiter.h @@ -108,9 +108,10 @@ GSList * gtk_text_iter_get_marks (const GtkTextIter *iter); GtkTextChildAnchor* gtk_text_iter_get_child_anchor (const GtkTextIter *iter); /* Return list of tags toggled at this point (toggled_on determines - whether the list is of on-toggles or off-toggles) */ + * whether the list is of on-toggles or off-toggles) + */ GSList *gtk_text_iter_get_toggled_tags (const GtkTextIter *iter, - gboolean toggled_on); + gboolean toggled_on); gboolean gtk_text_iter_begins_tag (const GtkTextIter *iter, GtkTextTag *tag); @@ -145,7 +146,7 @@ gboolean gtk_text_iter_get_attributes (const GtkTextIter *iter, GtkTextAttributes *values); gchar* gtk_text_iter_get_language (const GtkTextIter *iter); gboolean gtk_text_iter_is_end (const GtkTextIter *iter); -gboolean gtk_text_iter_is_first (const GtkTextIter *iter); +gboolean gtk_text_iter_is_start (const GtkTextIter *iter); /* * Moving around the buffer diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index 1355ef8fff..208fa8c0ea 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -1280,6 +1280,16 @@ add_text_attrs (GtkTextLayout *layout, attr->end_index = start + byte_count; pango_attr_list_insert (attrs, attr); + + if (style->font_scale != 1.0) + { + attr = pango_attr_scale_new (style->font_scale); + + attr->start_index = start; + attr->end_index = start + byte_count; + + pango_attr_list_insert (attrs, attr); + } } static void diff --git a/gtk/gtktexttag.c b/gtk/gtktexttag.c index a652432301..c149c4edad 100644 --- a/gtk/gtktexttag.c +++ b/gtk/gtktexttag.c @@ -85,6 +85,7 @@ enum { PROP_STRETCH, PROP_SIZE, PROP_SIZE_POINTS, + PROP_SCALE, PROP_PIXELS_ABOVE_LINES, PROP_PIXELS_BELOW_LINES, PROP_PIXELS_INSIDE_WRAP, @@ -114,6 +115,7 @@ enum { PROP_WEIGHT_SET, PROP_STRETCH_SET, PROP_SIZE_SET, + PROP_SCALE_SET, PROP_PIXELS_ABOVE_LINES_SET, PROP_PIXELS_BELOW_LINES_SET, PROP_PIXELS_INSIDE_WRAP_SET, @@ -349,6 +351,16 @@ gtk_text_tag_class_init (GtkTextTagClass *klass) 0, G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property (object_class, + PROP_SCALE, + g_param_spec_double ("scale", + _("Font scale"), + _("Font scale"), + 0.0, + G_MAXDOUBLE, + 1.0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property (object_class, PROP_SIZE_POINTS, g_param_spec_double ("size_points", @@ -543,6 +555,10 @@ gtk_text_tag_class_init (GtkTextTagClass *klass) _("Font size set"), _("Whether this tag affects the font size")); + ADD_SET_PROP ("scale_set", PROP_SCALE_SET, + _("Font scale set"), + _("Whether this tag scales the font size by a factor")); + ADD_SET_PROP ("justification_set", PROP_JUSTIFICATION_SET, _("Justification set"), _("Whether this tag affects paragraph justification")); @@ -963,6 +979,12 @@ gtk_text_tag_set_property (GObject *object, size_changed = TRUE; break; + case PROP_SCALE: + text_tag->values->font_scale = g_value_get_double (value); + text_tag->scale_set = TRUE; + size_changed = TRUE; + break; + case PROP_SIZE_POINTS: text_tag->values->font.size = g_value_get_double (value) * PANGO_SCALE; text_tag->size_set = TRUE; @@ -1151,6 +1173,11 @@ gtk_text_tag_set_property (GObject *object, text_tag->size_set = g_value_get_boolean (value); size_changed = TRUE; break; + + case PROP_SCALE_SET: + text_tag->scale_set = g_value_get_boolean (value); + size_changed = TRUE; + break; case PROP_PIXELS_ABOVE_LINES_SET: text_tag->pixels_above_lines_set = g_value_get_boolean (value); @@ -1332,10 +1359,14 @@ gtk_text_tag_get_property (GObject *object, case PROP_SIZE: g_value_set_int (value, tag->values->font.size); break; - + case PROP_SIZE_POINTS: g_value_set_double (value, ((double)tag->values->font.size) / (double)PANGO_SCALE); break; + + case PROP_SCALE: + g_value_set_double (value, tag->values->font_scale); + break; case PROP_PIXELS_ABOVE_LINES: g_value_set_int (value, tag->values->pixels_above_lines); @@ -1445,6 +1476,10 @@ gtk_text_tag_get_property (GObject *object, case PROP_SIZE_SET: g_value_set_boolean (value, tag->size_set); break; + + case PROP_SCALE_SET: + g_value_set_boolean (value, tag->scale_set); + break; case PROP_PIXELS_ABOVE_LINES_SET: g_value_set_boolean (value, tag->pixels_above_lines_set); @@ -1724,6 +1759,8 @@ gtk_text_attributes_new (void) values->language = gtk_get_default_language (); + values->font_scale = 1.0; + return values; } @@ -1965,6 +2002,10 @@ _gtk_text_attributes_fill_from_tags (GtkTextAttributes *dest, if (tag->size_set) dest->font.size = vals->font.size; + /* multiply all the scales together to get a composite */ + if (tag->scale_set) + dest->font_scale *= vals->font_scale; + if (tag->justification_set) dest->justification = vals->justification; @@ -2038,6 +2079,7 @@ _gtk_text_tag_affects_size (GtkTextTag *tag) tag->variant_set || tag->weight_set || tag->size_set || + tag->scale_set || tag->stretch_set || tag->justification_set || tag->left_margin_set || diff --git a/gtk/gtktexttag.h b/gtk/gtktexttag.h index ea30059b75..f75a1760b0 100644 --- a/gtk/gtktexttag.h +++ b/gtk/gtktexttag.h @@ -60,6 +60,7 @@ struct _GtkTextTag guint weight_set : 1; guint stretch_set : 1; guint size_set : 1; + guint scale_set : 1; guint fg_stipple_set : 1; guint justification_set : 1; guint left_margin_set : 1; @@ -148,6 +149,8 @@ struct _GtkTextAttributes /* Individual chunks of this can be set/unset as a group */ PangoFontDescription font; + gdouble font_scale; + gint left_margin; gint indent; diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 26aa7c6980..8bb7314b47 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -506,6 +506,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP); gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE); + gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_WRAP_MODE, GTK_ARG_READWRITE, ARG_WRAP_MODE); gtk_object_add_arg_type ("GtkTextView::justify", GTK_TYPE_JUSTIFICATION, diff --git a/gtk/gtkvscale.c b/gtk/gtkvscale.c index 72b8b209aa..be31ec3378 100644 --- a/gtk/gtkvscale.c +++ b/gtk/gtkvscale.c @@ -532,7 +532,6 @@ gtk_vscale_draw_value (GtkScale *scale) { GtkStateType state_type; GtkWidget *widget; - gchar buffer[32]; gint width, height; gint x, y; @@ -545,10 +544,14 @@ gtk_vscale_draw_value (GtkScale *scale) { PangoLayout *layout; PangoRectangle logical_rect; + gchar *txt; + + txt = _gtk_scale_format_value (scale, + GTK_RANGE (scale)->adjustment->value); + + layout = gtk_widget_create_pango_layout (widget, txt); + g_free (txt); - sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value); - - layout = gtk_widget_create_pango_layout (widget, buffer); pango_layout_get_pixel_extents (layout, NULL, &logical_rect); switch (scale->value_pos) diff --git a/tests/testgtk.c b/tests/testgtk.c index 5cd9117aa6..084ad2ae5c 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -6511,6 +6511,13 @@ create_event_watcher (void) * GtkRange */ +static gchar* +reformat_value (GtkScale *scale, + gdouble value) +{ + return g_strdup_printf ("-->%g<--", value); +} + static void create_range_controls (void) { @@ -6563,6 +6570,15 @@ create_range_controls (void) gtk_box_pack_start (GTK_BOX (box2), scrollbar, TRUE, TRUE, 0); gtk_widget_show (scrollbar); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment)); + gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE); + gtk_signal_connect (GTK_OBJECT (scale), + "format_value", + GTK_SIGNAL_FUNC (reformat_value), + NULL); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + hbox = gtk_hbox_new (FALSE, 0); scale = gtk_vscale_new (GTK_ADJUSTMENT (adjustment)); @@ -6579,10 +6595,20 @@ create_range_controls (void) gtk_range_set_inverted (GTK_RANGE (scale), TRUE); gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); gtk_widget_show (scale); + + scale = gtk_vscale_new (GTK_ADJUSTMENT (adjustment)); + gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE); + gtk_signal_connect (GTK_OBJECT (scale), + "format_value", + GTK_SIGNAL_FUNC (reformat_value), + NULL); + gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + gtk_box_pack_start (GTK_BOX (box2), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); - + separator = gtk_hseparator_new (); gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); gtk_widget_show (separator); @@ -8158,9 +8184,14 @@ configure_event_callback (GtkWidget *widget, GtkWidget *label = data; gchar *msg; gint x, y; - - x = widget->allocation.x; - y = widget->allocation.y; + +#if 0 + /* FIXME */ + gtk_window_get_location (GTK_WINDOW (widget), &x, &y); +#else + x = 0; + y = 0; +#endif msg = g_strdup_printf ("event: %d,%d %d x %d\n" "location: %d, %d", @@ -8235,6 +8266,26 @@ set_location_callback (GtkWidget *widget, gtk_widget_set_uposition (g_object_get_data (data, "target"), x, y); } +static void +set_geometry_callback (GtkWidget *entry, + gpointer data) +{ + gchar *text; + GtkWindow *target; + + target = GTK_WINDOW (g_object_get_data (G_OBJECT (data), "target")); + + text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); + +#if 0 + /* FIXME */ + if (!gtk_window_parse_geometry (target, text)) + g_print ("Bad geometry string '%s'\n", text); +#endif + + g_free (text); +} + static void allow_shrink_callback (GtkWidget *widget, gpointer data) @@ -8282,6 +8333,7 @@ window_controls (GtkWidget *window) GtkWidget *button; GtkWidget *spin; GtkAdjustment *adj; + GtkWidget *entry; GtkWidget *om; GtkWidget *menu; gint i; @@ -8327,6 +8379,13 @@ window_controls (GtkWidget *window) g_object_set_data (G_OBJECT (control_window), "spin2", spin); + entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); + + gtk_signal_connect (GTK_OBJECT (entry), "changed", + GTK_SIGNAL_FUNC (set_geometry_callback), + control_window); + button = gtk_button_new_with_label ("Queue resize"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", @@ -8386,6 +8445,20 @@ window_controls (GtkWidget *window) GTK_OBJECT (control_window)); gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic ("_Show"); + gtk_signal_connect_object (GTK_OBJECT (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_show), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_mnemonic ("_Hide"); + gtk_signal_connect_object (GTK_OBJECT (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + menu = gtk_menu_new (); i = 0; diff --git a/tests/testtext.c b/tests/testtext.c index a473837cda..175e4f1c4b 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -1063,6 +1063,23 @@ do_apply_colors (gpointer callback_data, } } +static void +do_remove_tags (gpointer callback_data, + guint callback_action, + GtkWidget *widget) +{ + View *view = view_from_widget (widget); + GtkTextIter start; + GtkTextIter end; + + if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer, + &start, &end)) + { + gtk_text_buffer_remove_all_tags (view->buffer->buffer, + &start, &end); + } +} + enum { RESPONSE_FORWARD, @@ -1235,6 +1252,7 @@ static GtkItemFactoryEntry menu_items[] = { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL }, { "/Attributes/Color cycles", NULL, do_apply_colors, TRUE, NULL }, { "/Attributes/No colors", NULL, do_apply_colors, FALSE, NULL }, + { "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL }, { "/_Test", NULL, 0, 0, "" }, { "/Test/_Example", NULL, do_example, 0, NULL }, };