diff --git a/demos/gtk-demo/listbox.c b/demos/gtk-demo/listbox.c index 750f5f480c..98a6a26611 100644 --- a/demos/gtk-demo/listbox.c +++ b/demos/gtk-demo/listbox.c @@ -8,7 +8,7 @@ #include #include -static GdkPixbuf *avatar_pixbuf_other; +static GdkTexture *avatar_texture_other; static GtkWidget *window = NULL; #define GTK_TYPE_MESSAGE (gtk_message_get_type ()) @@ -196,12 +196,9 @@ gtk_message_row_update (GtkMessageRow *row) gtk_button_set_label (GTK_BUTTON (priv->resent_by_button), priv->message->resent_by); if (strcmp (priv->message->sender_nick, "@GTKtoolkit") == 0) - { - gtk_image_set_from_icon_name (priv->avatar_image, "org.gtk.Demo4"); - gtk_image_set_icon_size (priv->avatar_image, GTK_ICON_SIZE_LARGE); - } + gtk_image_set_from_icon_name (priv->avatar_image, "org.gtk.Demo4"); else - gtk_image_set_from_pixbuf (priv->avatar_image, avatar_pixbuf_other); + gtk_image_set_from_paintable (priv->avatar_image, GDK_PAINTABLE (avatar_texture_other)); } @@ -344,7 +341,7 @@ do_listbox (GtkWidget *do_widget) if (!window) { - avatar_pixbuf_other = gdk_pixbuf_new_from_resource_at_scale ("/listbox/apple-red.png", 32, 32, FALSE, NULL); + avatar_texture_other = gdk_texture_new_from_resource ("/listbox/apple-red.png"); window = gtk_window_new (); gtk_window_set_display (GTK_WINDOW (window), diff --git a/demos/gtk-demo/listbox.ui b/demos/gtk-demo/listbox.ui index a237651d82..9b5199ef7d 100644 --- a/demos/gtk-demo/listbox.ui +++ b/demos/gtk-demo/listbox.ui @@ -25,6 +25,7 @@ 8 8 image-missing + large 0 0 diff --git a/examples/bp/bloatpad.c b/examples/bp/bloatpad.c index 42d7b45f15..e923b444c9 100644 --- a/examples/bp/bloatpad.c +++ b/examples/bp/bloatpad.c @@ -603,8 +603,8 @@ bloat_pad_startup (GApplication *application) g_object_unref (icon); g_bytes_unref (bytes); - icon = G_ICON (gdk_pixbuf_new_from_resource ("/org/gtk/libgtk/icons/16x16/actions/folder-new.png", NULL)); - item = g_menu_item_new ("Pixbuf", NULL); + icon = G_ICON (gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/actions/folder-new.png")); + item = g_menu_item_new ("Texture", NULL); g_menu_item_set_icon (item, icon); g_menu_append_item (menu, item); g_object_unref (item); diff --git a/gdk/gdkcontentdeserializer.c b/gdk/gdkcontentdeserializer.c index 4959b5af4d..56a62f7ae3 100644 --- a/gdk/gdkcontentdeserializer.c +++ b/gdk/gdkcontentdeserializer.c @@ -25,6 +25,8 @@ #include "filetransferportalprivate.h" #include "gdktexture.h" #include "gdkrgbaprivate.h" +#include "loaders/gdkpngprivate.h" +#include "loaders/gdktiffprivate.h" #include @@ -655,6 +657,56 @@ pixbuf_deserializer (GdkContentDeserializer *deserializer) deserializer); } +static void +texture_deserializer_finish (GObject *source, + GAsyncResult *result, + gpointer deserializer) +{ + GOutputStream *stream = G_OUTPUT_STREAM (source); + GBytes *bytes; + GError *error = NULL; + GdkTexture *texture = NULL; + gssize written; + + written = g_output_stream_splice_finish (stream, result, &error); + if (written < 0) + { + gdk_content_deserializer_return_error (deserializer, error); + return; + } + + bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream)); + + texture = gdk_texture_new_from_bytes (bytes, &error); + g_bytes_unref (bytes); + if (texture == NULL) + { + gdk_content_deserializer_return_error (deserializer, error); + return; + } + + g_value_take_object (gdk_content_deserializer_get_value (deserializer), texture); + gdk_content_deserializer_return_success (deserializer); +} + +static void +texture_deserializer (GdkContentDeserializer *deserializer) +{ + GOutputStream *output; + + output = g_memory_output_stream_new_resizable (); + + g_output_stream_splice_async (output, + gdk_content_deserializer_get_input_stream (deserializer), + G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE + | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, + gdk_content_deserializer_get_priority (deserializer), + gdk_content_deserializer_get_cancellable (deserializer), + texture_deserializer_finish, + deserializer); + g_object_unref (output); +} + static void string_deserializer_finish (GObject *source, GAsyncResult *result, @@ -863,48 +915,71 @@ init (void) initialized = TRUE; + gdk_content_register_deserializer ("image/png", + GDK_TYPE_TEXTURE, + texture_deserializer, + NULL, + NULL); + gdk_content_register_deserializer ("image/tiff", + GDK_TYPE_TEXTURE, + texture_deserializer, + NULL, + NULL); + gdk_content_register_deserializer ("image/jpeg", + GDK_TYPE_TEXTURE, + texture_deserializer, + NULL, + NULL); + + formats = gdk_pixbuf_get_formats (); /* Make sure png comes first */ for (f = formats; f; f = f->next) { GdkPixbufFormat *fmt = f->data; - char *name; - + char *name; + name = gdk_pixbuf_format_get_name (fmt); if (g_str_equal (name, "png")) - { - formats = g_slist_delete_link (formats, f); - formats = g_slist_prepend (formats, fmt); + { + formats = g_slist_delete_link (formats, f); + formats = g_slist_prepend (formats, fmt); - g_free (name); - - break; - } + g_free (name); + break; + } g_free (name); - } + } for (f = formats; f; f = f->next) { GdkPixbufFormat *fmt = f->data; char **mimes, **m; + char *name; + name = gdk_pixbuf_format_get_name (fmt); mimes = gdk_pixbuf_format_get_mime_types (fmt); for (m = mimes; *m; m++) - { - gdk_content_register_deserializer (*m, - GDK_TYPE_TEXTURE, - pixbuf_deserializer, - NULL, - NULL); + { + /* Turning pngs, jpegs and tiffs into textures is handled above */ + if (!g_str_equal (name, "png") && + !g_str_equal (name, "jpeg") && + !g_str_equal (name, "tiff")) + gdk_content_register_deserializer (*m, + GDK_TYPE_TEXTURE, + pixbuf_deserializer, + NULL, + NULL); gdk_content_register_deserializer (*m, GDK_TYPE_PIXBUF, pixbuf_deserializer, NULL, NULL); - } + } g_strfreev (mimes); + g_free (name); } g_slist_free (formats); diff --git a/gdk/gdkcontentserializer.c b/gdk/gdkcontentserializer.c index bc9deeb8d5..6faaa0121a 100644 --- a/gdk/gdkcontentserializer.c +++ b/gdk/gdkcontentserializer.c @@ -26,6 +26,10 @@ #include "filetransferportalprivate.h" #include "gdktextureprivate.h" #include "gdkrgba.h" +#include "loaders/gdkpngprivate.h" +#include "loaders/gdktiffprivate.h" +#include "loaders/gdkjpegprivate.h" +#include "gdkmemorytextureprivate.h" #include #include @@ -606,6 +610,7 @@ gdk_content_serialize_finish (GAsyncResult *result, /*** SERIALIZERS ***/ + static void pixbuf_serializer_finish (GObject *source, GAsyncResult *res, @@ -658,6 +663,77 @@ pixbuf_serializer (GdkContentSerializer *serializer) g_object_unref (pixbuf); } +static void +texture_serializer_finish (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GdkContentSerializer *serializer = GDK_CONTENT_SERIALIZER (source); + GError *error = NULL; + + if (!g_task_propagate_boolean (G_TASK (res), &error)) + gdk_content_serializer_return_error (serializer, error); + else + gdk_content_serializer_return_success (serializer); +} + +static void +serialize_texture_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GdkContentSerializer *serializer = source_object; + const GValue *value; + GdkTexture *texture; + GBytes *bytes = NULL; + GError *error = NULL; + gboolean result = FALSE; + GInputStream *input; + gssize spliced; + + value = gdk_content_serializer_get_value (serializer); + texture = g_value_get_object (value); + + if (strcmp (gdk_content_serializer_get_mime_type (serializer), "image/png") == 0) + bytes = gdk_save_png (texture); + else if (strcmp (gdk_content_serializer_get_mime_type (serializer), "image/tiff") == 0) + bytes = gdk_save_tiff (texture); + else if (strcmp (gdk_content_serializer_get_mime_type (serializer), "image/jpeg") == 0) + bytes = gdk_save_jpeg (texture); + else + g_assert_not_reached (); + + input = g_memory_input_stream_new_from_bytes (bytes); + spliced = g_output_stream_splice (gdk_content_serializer_get_output_stream (serializer), + input, + G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE, + gdk_content_serializer_get_cancellable (serializer), + &error); + g_object_unref (input); + g_bytes_unref (bytes); + + result = spliced != -1; + + if (result) + g_task_return_boolean (task, result); + else + g_task_return_error (task, error); +} + +static void +texture_serializer (GdkContentSerializer *serializer) +{ + GTask *task; + + task = g_task_new (serializer, + gdk_content_serializer_get_cancellable (serializer), + texture_serializer_finish, + NULL); + g_task_run_in_thread (task, serialize_texture_in_thread); + g_object_unref (task); +} + static void string_serializer_finish (GObject *source, GAsyncResult *result, @@ -877,51 +953,72 @@ init (void) initialized = TRUE; + gdk_content_register_serializer (GDK_TYPE_TEXTURE, + "image/png", + texture_serializer, + NULL, NULL); + + gdk_content_register_serializer (GDK_TYPE_TEXTURE, + "image/tiff", + texture_serializer, + NULL, NULL); + + gdk_content_register_serializer (GDK_TYPE_TEXTURE, + "image/jpeg", + texture_serializer, + NULL, NULL); + formats = gdk_pixbuf_get_formats (); /* Make sure png comes first */ for (f = formats; f; f = f->next) { GdkPixbufFormat *fmt = f->data; - char *name; - + char *name; + name = gdk_pixbuf_format_get_name (fmt); if (g_str_equal (name, "png")) - { - formats = g_slist_delete_link (formats, f); - formats = g_slist_prepend (formats, fmt); + { + formats = g_slist_delete_link (formats, f); + formats = g_slist_prepend (formats, fmt); - g_free (name); - - break; - } + g_free (name); + break; + } g_free (name); - } + } for (f = formats; f; f = f->next) { GdkPixbufFormat *fmt = f->data; char **mimes, **m; + char *name; if (!gdk_pixbuf_format_is_writable (fmt)) - continue; + continue; + name = gdk_pixbuf_format_get_name (fmt); mimes = gdk_pixbuf_format_get_mime_types (fmt); for (m = mimes; *m; m++) - { - gdk_content_register_serializer (GDK_TYPE_TEXTURE, - *m, - pixbuf_serializer, - gdk_pixbuf_format_get_name (fmt), - g_free); + { + /* Turning textures into pngs, tiffs or jpegs is handled above */ + if (!g_str_equal (name, "png") && + !g_str_equal (name, "tiff") && + !g_str_equal (name, "jpeg")) + gdk_content_register_serializer (GDK_TYPE_TEXTURE, + *m, + pixbuf_serializer, + gdk_pixbuf_format_get_name (fmt), + g_free); gdk_content_register_serializer (GDK_TYPE_PIXBUF, *m, pixbuf_serializer, gdk_pixbuf_format_get_name (fmt), g_free); - } + } g_strfreev (mimes); + g_free (name); } g_slist_free (formats); diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c index ec03c72370..3f602cc5cc 100644 --- a/gdk/gdktexture.c +++ b/gdk/gdktexture.c @@ -41,11 +41,17 @@ #include "gdktextureprivate.h" #include "gdkinternals.h" +#include "gdkintl.h" #include "gdkmemorytextureprivate.h" #include "gdkpaintable.h" #include "gdksnapshot.h" #include +#include "loaders/gdkpngprivate.h" +#include "loaders/gdktiffprivate.h" +#include "loaders/gdkjpegprivate.h" + +G_DEFINE_QUARK (gdk-texture-error-quark, gdk_texture_error) /* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */ void @@ -108,9 +114,110 @@ gdk_texture_paintable_init (GdkPaintableInterface *iface) iface->get_intrinsic_height = gdk_texture_paintable_get_intrinsic_height; } +static GVariant * +gdk_texture_icon_serialize (GIcon *icon) +{ + GVariant *result; + GBytes *bytes; + + bytes = gdk_texture_save_to_png_bytes (GDK_TEXTURE (icon)); + result = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE); + g_bytes_unref (bytes); + + return g_variant_new ("(sv)", "bytes", result); +} + +static void +gdk_texture_icon_init (GIconIface *iface) +{ + iface->hash = (guint (*) (GIcon *)) g_direct_hash; + iface->equal = (gboolean (*) (GIcon *, GIcon *)) g_direct_equal; + iface->serialize = gdk_texture_icon_serialize; +} + +static GInputStream * +gdk_texture_loadable_icon_load (GLoadableIcon *icon, + int size, + char **type, + GCancellable *cancellable, + GError **error) +{ + GInputStream *stream; + GBytes *bytes; + + bytes = gdk_texture_save_to_png_bytes (GDK_TEXTURE (icon)); + stream = g_memory_input_stream_new_from_bytes (bytes); + g_bytes_unref (bytes); + + if (type) + *type = NULL; + + return stream; +} + +static void +gdk_texture_loadable_icon_load_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GInputStream *stream; + GBytes *bytes; + + bytes = gdk_texture_save_to_png_bytes (source_object); + stream = g_memory_input_stream_new_from_bytes (bytes); + g_bytes_unref (bytes); + g_task_return_pointer (task, stream, g_object_unref); +} + +static void +gdk_texture_loadable_icon_load_async (GLoadableIcon *icon, + int size, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (icon, cancellable, callback, user_data); + g_task_run_in_thread (task, gdk_texture_loadable_icon_load_in_thread); + g_object_unref (task); +} + +static GInputStream * +gdk_texture_loadable_icon_load_finish (GLoadableIcon *icon, + GAsyncResult *res, + char **type, + GError **error) +{ + GInputStream *result; + + g_return_val_if_fail (g_task_is_valid (res, icon), NULL); + + result = g_task_propagate_pointer (G_TASK (res), error); + if (result == NULL) + return NULL; + + if (type) + *type = NULL; + + return result; +} + +static void +gdk_texture_loadable_icon_init (GLoadableIconIface *iface) +{ + iface->load = gdk_texture_loadable_icon_load; + iface->load_async = gdk_texture_loadable_icon_load_async; + iface->load_finish = gdk_texture_loadable_icon_load_finish; +} + G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, - gdk_texture_paintable_init)) + gdk_texture_paintable_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ICON, + gdk_texture_icon_init) + G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, gdk_texture_loadable_icon_init)) #define GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \ g_critical ("Texture of type '%s' does not implement GdkTexture::" # method, G_OBJECT_TYPE_NAME (obj)) @@ -349,18 +456,23 @@ gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf) GdkTexture * gdk_texture_new_from_resource (const char *resource_path) { - GError *error = NULL; + GBytes *bytes; GdkTexture *texture; - GdkPixbuf *pixbuf; + GError *error = NULL; g_return_val_if_fail (resource_path != NULL, NULL); - pixbuf = gdk_pixbuf_new_from_resource (resource_path, &error); - if (pixbuf == NULL) - g_error ("Resource path %s is not a valid image: %s", resource_path, error->message); + bytes = g_resources_lookup_data (resource_path, 0, &error); + if (bytes != NULL) + { + texture = gdk_texture_new_from_bytes (bytes, &error); + g_bytes_unref (bytes); + } + else + texture = NULL; - texture = gdk_texture_new_for_pixbuf (pixbuf); - g_object_unref (pixbuf); + if (texture == NULL) + g_error ("Resource path %s s not a valid image: %s", resource_path, error->message); return texture; } @@ -400,6 +512,59 @@ gdk_texture_new_from_file (GFile *file, return texture; } +gboolean +gdk_texture_can_load (GBytes *bytes) +{ + return gdk_is_png (bytes) || + gdk_is_jpeg (bytes) || + gdk_is_tiff (bytes); +} + +static GdkTexture * +gdk_texture_new_from_bytes_internal (GBytes *bytes, + GError **error) +{ + if (gdk_is_png (bytes)) + { + return gdk_load_png (bytes, error); + } + else if (gdk_is_jpeg (bytes)) + { + return gdk_load_jpeg (bytes, error); + } + else if (gdk_is_tiff (bytes)) + { + return gdk_load_tiff (bytes, error); + } + else + { + g_set_error_literal (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT, + _("Unknown image format.")); + return NULL; + } +} + +static GdkTexture * +gdk_texture_new_from_bytes_pixbuf (GBytes *bytes, + GError **error) +{ + GInputStream *stream; + GdkPixbuf *pixbuf; + GdkTexture *texture; + + stream = g_memory_input_stream_new_from_bytes (bytes); + pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error); + g_object_unref (stream); + if (pixbuf == NULL) + return NULL; + + texture = gdk_texture_new_for_pixbuf (pixbuf); + g_object_unref (pixbuf); + + return texture; +} + /** * gdk_texture_new_from_bytes: @@ -421,24 +586,26 @@ GdkTexture * gdk_texture_new_from_bytes (GBytes *bytes, GError **error) { - GInputStream *stream; - GdkPixbuf *pixbuf; GdkTexture *texture; + GError *internal_error = NULL; g_return_val_if_fail (bytes != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - stream = g_memory_input_stream_new_from_bytes (bytes); - pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error); - g_object_unref (stream); + texture = gdk_texture_new_from_bytes_internal (bytes, &internal_error); + if (texture) + return texture; - if (pixbuf == NULL) - return NULL; + if (!g_error_matches (internal_error, GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT) && + !g_error_matches (internal_error, GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT)) + { + g_propagate_error (error, internal_error); + return NULL; + } - texture = gdk_texture_new_for_pixbuf (pixbuf); - g_object_unref (pixbuf); + g_clear_error (&internal_error); - return texture; + return gdk_texture_new_from_bytes_pixbuf (bytes, error); } /** @@ -671,7 +838,8 @@ gdk_texture_get_render_data (GdkTexture *self, * This is a utility function intended for debugging and testing. * If you want more control over formats, proper error handling or * want to store to a `GFile` or other location, you might want to - * look into using the gdk-pixbuf library. + * use [method@Gdk.Texture.save_to_png_bytes] or look into the + * gdk-pixbuf library. * * Returns: %TRUE if saving succeeded, %FALSE on failure. */ @@ -679,30 +847,110 @@ gboolean gdk_texture_save_to_png (GdkTexture *texture, const char *filename) { - cairo_surface_t *surface; - cairo_status_t status; + GBytes *bytes; gboolean result; g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE); g_return_val_if_fail (filename != NULL, FALSE); - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - gdk_texture_get_width (texture), - gdk_texture_get_height (texture)); - gdk_texture_download (texture, - cairo_image_surface_get_data (surface), - cairo_image_surface_get_stride (surface)); - cairo_surface_mark_dirty (surface); - - status = cairo_surface_write_to_png (surface, filename); - - if (status != CAIRO_STATUS_SUCCESS || - cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) - result = FALSE; - else - result = TRUE; - - cairo_surface_destroy (surface); + bytes = gdk_save_png (texture); + result = g_file_set_contents (filename, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + NULL); + g_bytes_unref (bytes); return result; } + +/** + * gdk_texture_save_to_png_bytes: + * @texture: a `GdkTexture` + * + * Store the given @texture in memory as a PNG file. + * Use [ctor@Gdk.Texture.new_from_bytes] to read it back. + * + * If you want to serialize a texture, this is a convenient and + * portable way to do that. + * + * If you need more control over the generated image, such as + * attaching metadata, you should look into an image handling + * library such as the gdk-pixbuf library. + * + * If you are dealing with high dynamic range float data, you + * might also want to consider [method@Gdk.Texture.save_to_tiff_bytes] + * instead. + * + * Returns: a newly allocated `GBytes` containing PNG data + * + * Since: 4.6 + */ +GBytes * +gdk_texture_save_to_png_bytes (GdkTexture *texture) +{ + g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL); + + return gdk_save_png (texture); +} + +/** + * gdk_texture_save_to_tiff: + * @texture: a `GdkTexture` + * @filename: (type filename): the filename to store to + * + * Store the given @texture to the @filename as a TIFF file. + * + * GTK will attempt to store data without loss. + * Returns: %TRUE if saving succeeded, %FALSE on failure. + * + * Since: 4.6 + */ +gboolean +gdk_texture_save_to_tiff (GdkTexture *texture, + const char *filename) +{ + GBytes *bytes; + gboolean result; + + g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + bytes = gdk_save_tiff (texture); + result = g_file_set_contents (filename, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + NULL); + g_bytes_unref (bytes); + + return result; +} + +/** + * gdk_texture_save_to_tiff_bytes: + * @texture: a `GdkTexture` + * + * Store the given @texture in memory as a TIFF file. + * + * Use [ctor@Gdk.Texture.new_from_bytes] to read it back. + * + * This function is intended to store a representation of the + * texture's data that is as accurate as possible. This is + * particularly relevant when working with high dynamic range + * images and floating-point texture data. + * + * If that is not your concern and you are interested in a + * smaller size and a more portable format, you might want to + * use [method@Gdk.Texture.save_to_png_bytes]. + * + * Returns: a newly allocated `GBytes` containing TIFF data + * + * Since: 4.6 + */ +GBytes * +gdk_texture_save_to_tiff_bytes (GdkTexture *texture) +{ + g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL); + + return gdk_save_tiff (texture); +} + diff --git a/gdk/gdktexture.h b/gdk/gdktexture.h index e080679ca1..6a62c9fe50 100644 --- a/gdk/gdktexture.h +++ b/gdk/gdktexture.h @@ -38,6 +38,30 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkTexture, g_object_unref) typedef struct _GdkTextureClass GdkTextureClass; +#define GDK_TEXTURE_ERROR (gdk_texture_error_quark ()) + +GDK_AVAILABLE_IN_4_6 +GQuark gdk_texture_error_quark (void); + +/** + * GdkTextureError: + * @GDK_TEXTURE_ERROR_TOO_LARGE: Not enough memory to handle this image + * @GDK_TEXTURE_ERROR_CORRUPT_IMAGE: The image data appears corrupted + * @GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT: The image contains features + * that cannot be loaded + * @GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT: The image format is not supported + * + * Possible errors that can be returned by `GdkTexture` constructors. + * + * Since: 4.6 + */ +typedef enum +{ + GDK_TEXTURE_ERROR_TOO_LARGE, + GDK_TEXTURE_ERROR_CORRUPT_IMAGE, + GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT, + GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT, +} GdkTextureError; GDK_AVAILABLE_IN_ALL GType gdk_texture_get_type (void) G_GNUC_CONST; @@ -72,6 +96,13 @@ void gdk_texture_download_float (GdkTexture GDK_AVAILABLE_IN_ALL gboolean gdk_texture_save_to_png (GdkTexture *texture, const char *filename); +GDK_AVAILABLE_IN_4_6 +GBytes * gdk_texture_save_to_png_bytes (GdkTexture *texture); +GDK_AVAILABLE_IN_4_6 +gboolean gdk_texture_save_to_tiff (GdkTexture *texture, + const char *filename); +GDK_AVAILABLE_IN_4_6 +GBytes * gdk_texture_save_to_tiff_bytes (GdkTexture *texture); G_END_DECLS diff --git a/gdk/gdktextureprivate.h b/gdk/gdktextureprivate.h index 29fca0a9ba..814ed5d92c 100644 --- a/gdk/gdktextureprivate.h +++ b/gdk/gdktextureprivate.h @@ -35,9 +35,8 @@ struct _GdkTextureClass { gsize stride); }; -gpointer gdk_texture_new (const GdkTextureClass *klass, - int width, - int height); +gboolean gdk_texture_can_load (GBytes *bytes); + GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface); cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture); /* NB: GdkMemoryTexture */ diff --git a/gdk/loaders/gdkjpeg.c b/gdk/loaders/gdkjpeg.c new file mode 100644 index 0000000000..c64a0ea99d --- /dev/null +++ b/gdk/loaders/gdkjpeg.c @@ -0,0 +1,329 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#include "config.h" + +#include "gdkjpegprivate.h" + +#include "gdkintl.h" +#include "gdktexture.h" +#include "gdkmemorytextureprivate.h" + +#include +#include +#include + +/* {{{ Error handling */ + +/* No sigsetjmp on Windows */ +#ifndef HAVE_SIGSETJMP +#define sigjmp_buf jmp_buf +#define sigsetjmp(jb, x) setjmp(jb) +#define siglongjmp longjmp +#endif + +struct error_handler_data { + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; + GError **error; +}; + +static void +fatal_error_handler (j_common_ptr cinfo) +{ + struct error_handler_data *errmgr; + char buffer[JMSG_LENGTH_MAX]; + + errmgr = (struct error_handler_data *) cinfo->err; + + cinfo->err->format_message (cinfo, buffer); + + if (errmgr->error && *errmgr->error == NULL) + g_set_error (errmgr->error, + GDK_TEXTURE_ERROR, + GDK_TEXTURE_ERROR_CORRUPT_IMAGE, + _("Error interpreting JPEG image file (%s)"), buffer); + + siglongjmp (errmgr->setjmp_buffer, 1); + + g_assert_not_reached (); +} + +static void +output_message_handler (j_common_ptr cinfo) +{ + /* do nothing */ +} + +/* }}} */ +/* {{{ Format conversion */ + +static void +convert_rgba_to_rgb (guchar *data, + int width, + int height, + int stride) +{ + gsize x, y; + guchar *src, *dest; + + for (y = 0; y < height; y++) + { + src = data; + dest = data; + + for (x = 0; x < width; x++) + { + guint32 pixel; + + memcpy (&pixel, src, sizeof (guint32)); + + dest[0] = (pixel & 0x00ff0000) >> 16; + dest[1] = (pixel & 0x0000ff00) >> 8; + dest[2] = (pixel & 0x000000ff) >> 0; + + dest += 3; + src += 4; + } + + data += stride; + } +} + +static void +convert_grayscale_to_rgb (guchar *data, + int width, + int height, + int stride) +{ + gsize x, y; + guchar *dest, *src; + + for (y = 0; y < height; y++) + { + src = data + width; + dest = data + 3 * width; + for (x = 0; x < width; x++) + { + dest -= 3; + src -= 1; + dest[0] = *src; + dest[1] = *src; + dest[2] = *src; + } + data += stride; + } +} + +static void +convert_cmyk_to_rgba (guchar *data, + int width, + int height, + int stride) +{ + gsize x, r; + guchar *dest; + + for (r = 0; r < height; r++) + { + dest = data; + for (x = 0; x < width; x++) + { + int c, m, y, k; + + c = dest[0]; + m = dest[1]; + y = dest[2]; + k = dest[3]; + dest[0] = k * c / 255; + dest[1] = k * m / 255; + dest[2] = k * y / 255; + dest[3] = 255; + dest += 4; + } + data += stride; + } +} + + /* }}} */ +/* {{{ Public API */ + +GdkTexture * +gdk_load_jpeg (GBytes *input_bytes, + GError **error) +{ + struct jpeg_decompress_struct info; + struct error_handler_data jerr; + guint width, height, stride; + unsigned char *data; + unsigned char *row[1]; + GBytes *bytes; + GdkTexture *texture; + GdkMemoryFormat format; + + info.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + jerr.error = error; + + if (sigsetjmp (jerr.setjmp_buffer, 1)) + { + jpeg_destroy_decompress (&info); + return NULL; + } + + jpeg_create_decompress (&info); + + jpeg_mem_src (&info, + g_bytes_get_data (input_bytes, NULL), + g_bytes_get_size (input_bytes)); + + jpeg_read_header (&info, TRUE); + jpeg_start_decompress (&info); + + width = info.output_width; + height = info.output_height; + + switch ((int)info.out_color_space) + { + case JCS_GRAYSCALE: + case JCS_RGB: + stride = 3 * width; + data = g_try_malloc_n (stride, height); + format = GDK_MEMORY_R8G8B8; + break; + case JCS_CMYK: + stride = 4 * width; + data = g_try_malloc_n (stride, height); + format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; + break; + default: + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT, + _("Unsupported JPEG colorspace (%d)"), info.out_color_space); + jpeg_destroy_decompress (&info); + return NULL; + } + + if (!data) + { + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_TOO_LARGE, + _("Not enough memory for image size %ux%u"), width, height); + jpeg_destroy_decompress (&info); + return NULL; + } + + while (info.output_scanline < info.output_height) + { + row[0] = (unsigned char *)(&data[stride * info.output_scanline]); + jpeg_read_scanlines (&info, row, 1); + } + + switch ((int)info.out_color_space) + { + case JCS_GRAYSCALE: + convert_grayscale_to_rgb (data, width, height, stride); + format = GDK_MEMORY_R8G8B8; + break; + case JCS_RGB: + break; + case JCS_CMYK: + convert_cmyk_to_rgba (data, width, height, stride); + break; + default: + g_assert_not_reached (); + } + + jpeg_finish_decompress (&info); + jpeg_destroy_decompress (&info); + + bytes = g_bytes_new_take (data, stride * height); + + texture = gdk_memory_texture_new (width, height, + format, + bytes, stride); + + g_bytes_unref (bytes); + + return texture; +} + +GBytes * +gdk_save_jpeg (GdkTexture *texture) +{ + struct jpeg_compress_struct info; + struct error_handler_data jerr; + struct jpeg_error_mgr err; + guchar *data = NULL; + gulong size = 0; + guchar *input = NULL; + guchar *row; + int width, height, stride; + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + info.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + jerr.error = NULL; + + if (sigsetjmp (jerr.setjmp_buffer, 1)) + { + free (data); + g_free (input); + jpeg_destroy_compress (&info); + return NULL; + } + + info.err = jpeg_std_error (&err); + jpeg_create_compress (&info); + info.image_width = width; + info.image_height = height; + info.input_components = 3; + info.in_color_space = JCS_RGB; + + jpeg_set_defaults (&info); + jpeg_set_quality (&info, 75, TRUE); + + jpeg_mem_dest (&info, &data, &size); + + stride = width * 4; + input = g_malloc (stride * height); + gdk_texture_download (texture, input, stride); + convert_rgba_to_rgb (data, width, height, stride); + + jpeg_start_compress (&info, TRUE); + + while (info.next_scanline < info.image_height) + { + row = &input[info.next_scanline * stride]; + jpeg_write_scanlines (&info, &row, 1); + } + + jpeg_finish_compress (&info); + + g_free (input); + jpeg_destroy_compress (&info); + + return g_bytes_new_with_free_func (data, size, (GDestroyNotify) free, NULL); +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/loaders/gdkjpegprivate.h b/gdk/loaders/gdkjpegprivate.h new file mode 100644 index 0000000000..28e7b90f28 --- /dev/null +++ b/gdk/loaders/gdkjpegprivate.h @@ -0,0 +1,43 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#ifndef __GDK_JPEG_PRIVATE_H__ +#define __GDK_JPEG_PRIVATE_H__ + +#include "gdkmemorytexture.h" +#include + +#define JPEG_SIGNATURE "\xff\xd8" + +GdkTexture *gdk_load_jpeg (GBytes *bytes, + GError **error); + +GBytes *gdk_save_jpeg (GdkTexture *texture); + +static inline gboolean +gdk_is_jpeg (GBytes *bytes) +{ + const char *data; + gsize size; + + data = g_bytes_get_data (bytes, &size); + + return size > strlen (JPEG_SIGNATURE) && + memcmp (data, JPEG_SIGNATURE, strlen (JPEG_SIGNATURE)) == 0; +} + +#endif diff --git a/gdk/loaders/gdkpng.c b/gdk/loaders/gdkpng.c new file mode 100644 index 0000000000..80fca2460b --- /dev/null +++ b/gdk/loaders/gdkpng.c @@ -0,0 +1,568 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#include "config.h" + +#include "gdkpngprivate.h" + +#include "gdkintl.h" +#include "gdkmemorytextureprivate.h" +#include "gdktexture.h" +#include "gdktextureprivate.h" +#include "gsk/ngl/fp16private.h" +#include +#include + +/* The main difference between the png load/save code here and + * gdk-pixbuf is that we can support loading 16-bit data in the + * future, and we can extract gamma and colorspace information + * to produce linear, color-corrected data. + */ + +/* {{{ Callbacks */ + +/* No sigsetjmp on Windows */ +#ifndef HAVE_SIGSETJMP +#define sigjmp_buf jmp_buf +#define sigsetjmp(jb, x) setjmp(jb) +#define siglongjmp longjmp +#endif + +typedef struct +{ + guchar *data; + gsize size; + gsize position; +} png_io; + + +static void +png_read_func (png_structp png, + png_bytep data, + png_size_t size) +{ + png_io *io; + + io = png_get_io_ptr (png); + + if (io->position + size > io->size) + png_error (png, "Read past EOF"); + + memcpy (data, io->data + io->position, size); + io->position += size; +} + +static void +png_write_func (png_structp png, + png_bytep data, + png_size_t size) +{ + png_io *io; + + io = png_get_io_ptr (png); + + if (io->position > io->size || + io->size - io->position < size) + { + io->size = io->position + size; + io->data = g_realloc (io->data, io->size); + } + + memcpy (io->data + io->position, data, size); + io->position += size; +} + +static void +png_flush_func (png_structp png) +{ +} + +static png_voidp +png_malloc_callback (png_structp o, + png_size_t size) +{ + return g_try_malloc (size); +} + +static void +png_free_callback (png_structp o, + png_voidp x) +{ + g_free (x); +} + +static void +png_simple_error_callback (png_structp png, + png_const_charp error_msg) +{ + GError **error = png_get_error_ptr (png); + + if (error && !*error) + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE, + _("Error reading png (%s)"), error_msg); + + longjmp (png_jmpbuf (png), 1); +} + +static void +png_simple_warning_callback (png_structp png, + png_const_charp error_msg) +{ +} + +/* }}} */ +/* {{{ Format conversion */ + +static void +unpremultiply (guchar *data, + int width, + int height) +{ + gsize x, y; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + guchar *b = &data[x * 4]; + guint32 pixel; + guchar alpha; + + memcpy (&pixel, b, sizeof (guint32)); + alpha = (pixel & 0xff000000) >> 24; + if (alpha == 0) + { + b[0] = 0; + b[1] = 0; + b[2] = 0; + b[3] = 0; + } + else + { + b[0] = (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha; + b[1] = (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } + } + data += width * 4; + } +} + +static void +unpremultiply_float_to_16bit (guchar *data, + int width, + int height) +{ + gsize x, y; + float *src = (float *)data;; + guint16 *dest = (guint16 *)data; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + float r, g, b, a; + + r = src[0]; + g = src[1]; + b = src[2]; + a = src[3]; + if (a == 0) + { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + } + else + { + dest[0] = (guint16) CLAMP (65536.f * r / a, 0.f, 65535.f); + dest[1] = (guint16) CLAMP (65536.f * g / a, 0.f, 65535.f); + dest[2] = (guint16) CLAMP (65536.f * b / a, 0.f, 65535.f); + dest[3] = (guint16) CLAMP (65536.f * a, 0.f, 65535.f); + } + + dest += 4; + src += 4; + } + } +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +static void +premultiply_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) + { + uint8_t *base = &data[i]; + uint8_t alpha = base[3]; + uint32_t p; + + if (alpha == 0) + { + p = 0; + } + else + { + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + + if (alpha != 0xff) + { + red = multiply_alpha (alpha, red); + green = multiply_alpha (alpha, green); + blue = multiply_alpha (alpha, blue); + } + p = ((uint32_t)alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + memcpy (base, &p, sizeof (uint32_t)); + } +} + +static void +convert_bytes_to_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) + { + uint8_t *base = &data[i]; + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + uint32_t pixel; + + pixel = (0xffu << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &pixel, sizeof (uint32_t)); + } +} + +static void +premultiply_16bit (guchar *data, + int width, + int height, + int stride) +{ + gsize x, y; + guint16 *src; + + for (y = 0; y < height; y++) + { + src = (guint16 *)data; + for (x = 0; x < width; x++) + { + float alpha = src[x * 4 + 3] / 65535.f; + src[x * 4 ] = (guint16) CLAMP (src[x * 4 ] * alpha, 0.f, 65535.f); + src[x * 4 + 1] = (guint16) CLAMP (src[x * 4 + 1] * alpha, 0.f, 65535.f); + src[x * 4 + 2] = (guint16) CLAMP (src[x * 4 + 2] * alpha, 0.f, 65535.f); + } + + data += stride; + } +} + +/* }}} */ +/* {{{ Public API */ + +GdkTexture * +gdk_load_png (GBytes *bytes, + GError **error) +{ + png_io io; + png_struct *png = NULL; + png_info *info; + guint width, height; + int depth, color_type; + int interlace, stride; + GdkMemoryFormat format; + guchar *buffer = NULL; + guchar **row_pointers = NULL; + GBytes *out_bytes; + GdkTexture *texture; + int bpp; + + io.data = (guchar *)g_bytes_get_data (bytes, &io.size); + io.position = 0; + + png = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING, + error, + png_simple_error_callback, + png_simple_warning_callback, + NULL, + png_malloc_callback, + png_free_callback); + if (png == NULL) + g_error ("Out of memory"); + + info = png_create_info_struct (png); + if (info == NULL) + g_error ("Out of memory"); + + png_set_read_fn (png, &io, png_read_func); + + if (sigsetjmp (png_jmpbuf (png), 1)) + { + g_free (buffer); + g_free (row_pointers); + png_destroy_read_struct (&png, &info, NULL); + return NULL; + } + + png_read_info (png, info); + + png_get_IHDR (png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + if (color_type == PNG_COLOR_TYPE_GRAY) + png_set_expand_gray_1_2_4_to_8 (png); + + if (png_get_valid (png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 8) + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + if (depth < 8) + png_set_packing (png); + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_read_update_info (png, info); + png_get_IHDR (png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + if ((depth != 8 && depth != 16) || + !(color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA)) + { + png_destroy_read_struct (&png, &info, NULL); + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT, + _("Failed to parse png image")); + return NULL; + } + + switch (color_type) + { + case PNG_COLOR_TYPE_RGB_ALPHA: + if (depth == 8) + { + format = GDK_MEMORY_DEFAULT; + png_set_read_user_transform_fn (png, premultiply_data); + } + else + { + format = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + png_set_swap (png); +#endif + } + break; + case PNG_COLOR_TYPE_RGB: + if (depth == 8) + { + format = GDK_MEMORY_DEFAULT; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + } + else + { + format = GDK_MEMORY_R16G16B16; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + png_set_swap (png); +#endif + } + break; + default: + g_assert_not_reached (); + } + + bpp = gdk_memory_format_bytes_per_pixel (format); + stride = width * bpp; + if (stride % 8) + stride += 8 - stride % 8; + + buffer = g_try_malloc_n (height, stride); + row_pointers = g_try_malloc_n (height, sizeof (char *)); + + if (!buffer || !row_pointers) + { + g_free (buffer); + g_free (row_pointers); + png_destroy_read_struct (&png, &info, NULL); + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_TOO_LARGE, + _("Not enough memory for image size %ux%u"), width, height); + return NULL; + } + + for (int i = 0; i < height; i++) + row_pointers[i] = &buffer[i * stride]; + + png_read_image (png, row_pointers); + png_read_end (png, info); + + if (format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED) + premultiply_16bit (buffer, width, height, stride); + + out_bytes = g_bytes_new_take (buffer, height * stride); + texture = gdk_memory_texture_new (width, height, format, out_bytes, stride); + g_bytes_unref (out_bytes); + + g_free (row_pointers); + png_destroy_read_struct (&png, &info, NULL); + + return texture; +} + +GBytes * +gdk_save_png (GdkTexture *texture) +{ + png_struct *png = NULL; + png_info *info; + png_io io = { NULL, 0, 0 }; + guint width, height, stride; + guchar *data = NULL; + guchar *row; + int y; + GdkTexture *mtexture; + GdkMemoryFormat format; + int png_format; + int depth; + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + mtexture = gdk_texture_download_texture (texture); + format = gdk_memory_texture_get_format (GDK_MEMORY_TEXTURE (mtexture)); + + switch (format) + { + case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: + case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: + case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: + case GDK_MEMORY_B8G8R8A8: + case GDK_MEMORY_A8R8G8B8: + case GDK_MEMORY_R8G8B8A8: + case GDK_MEMORY_A8B8G8R8: + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: + stride = width * 4; + data = g_malloc_n (stride, height); + gdk_texture_download (mtexture, data, stride); + unpremultiply (data, width, height); + + png_format = PNG_COLOR_TYPE_RGB_ALPHA; + depth = 8; + break; + + case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16_FLOAT: + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + case GDK_MEMORY_R32G32B32_FLOAT: + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + data = g_malloc_n (width * 16, height); + gdk_texture_download_float (mtexture, (float *)data, width * 4); + unpremultiply_float_to_16bit (data, width, height); + + png_format = PNG_COLOR_TYPE_RGB_ALPHA; + stride = width * 8; + depth = 16; + break; + + case GDK_MEMORY_N_FORMATS: + default: + g_assert_not_reached (); + } + + png = png_create_write_struct_2 (PNG_LIBPNG_VER_STRING, NULL, + png_simple_error_callback, + png_simple_warning_callback, + NULL, + png_malloc_callback, + png_free_callback); + if (!png) + return NULL; + + info = png_create_info_struct (png); + if (!info) + { + png_destroy_read_struct (&png, NULL, NULL); + return NULL; + } + + if (sigsetjmp (png_jmpbuf (png), 1)) + { + g_free (data); + g_free (io.data); + png_destroy_read_struct (&png, &info, NULL); + return NULL; + } + + png_set_write_fn (png, &io, png_write_func, png_flush_func); + + png_set_IHDR (png, info, width, height, depth, + png_format, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info (png, info); + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + png_set_swap (png); +#endif + + for (y = 0, row = data; y < height; y++, row += stride) + png_write_rows (png, &row, 1); + + png_write_end (png, info); + + png_destroy_write_struct (&png, &info); + + g_free (data); + + return g_bytes_new_take (io.data, io.size); +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/loaders/gdkpngprivate.h b/gdk/loaders/gdkpngprivate.h new file mode 100644 index 0000000000..cbe073b315 --- /dev/null +++ b/gdk/loaders/gdkpngprivate.h @@ -0,0 +1,43 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#ifndef __GDK_PNG_PRIVATE_H__ +#define __GDK_PNG_PRIVATE_H__ + +#include "gdktexture.h" +#include + +#define PNG_SIGNATURE "\x89PNG" + +GdkTexture *gdk_load_png (GBytes *bytes, + GError **error); + +GBytes *gdk_save_png (GdkTexture *texture); + +static inline gboolean +gdk_is_png (GBytes *bytes) +{ + const char *data; + gsize size; + + data = g_bytes_get_data (bytes, &size); + + return size > strlen (PNG_SIGNATURE) && + memcmp (data, PNG_SIGNATURE, strlen (PNG_SIGNATURE)) == 0; +} + +#endif diff --git a/gdk/loaders/gdktiff.c b/gdk/loaders/gdktiff.c new file mode 100644 index 0000000000..62d2194f0c --- /dev/null +++ b/gdk/loaders/gdktiff.c @@ -0,0 +1,513 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#include "config.h" + +#include "gdktiffprivate.h" + +#include "gdkintl.h" +#include "gdkmemorytextureprivate.h" +#include "gdktexture.h" +#include "gdktextureprivate.h" + +#include + +/* Our main interest in tiff as an image format is that it is + * flexible enough to save all our texture formats without + * lossy conversions. + * + * The loader isn't meant to be a very versatile. It just aims + * to load the subset that we're saving ourselves. For anything + * else, we fall back to TIFFRGBAImage, which is the same api + * that gdk-pixbuf uses. + */ + +/* {{{ IO handling */ + +typedef struct +{ + GBytes **out_bytes; + gchar *data; + gsize size; + gsize position; +} TiffIO; + +static void +tiff_io_warning (const char *module, + const char *fmt, + va_list ap) G_GNUC_PRINTF(2, 0); +static void +tiff_io_warning (const char *module, + const char *fmt, + va_list ap) +{ + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap); +} + +static void +tiff_io_error (const char *module, + const char *fmt, + va_list ap) G_GNUC_PRINTF(2, 0); +static void +tiff_io_error (const char *module, + const char *fmt, + va_list ap) +{ + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap); +} + +static tsize_t +tiff_io_no_read (thandle_t handle, + tdata_t buffer, + tsize_t size) +{ + return (tsize_t) -1; +} + +static tsize_t +tiff_io_read (thandle_t handle, + tdata_t buffer, + tsize_t size) +{ + TiffIO *io = (TiffIO *) handle; + gsize read; + + if (io->position >= io->size) + return 0; + + read = MIN (size, io->size - io->position); + + memcpy (buffer, io->data + io->position, read); + io->position += read; + + return (tsize_t) read; +} + +static tsize_t +tiff_io_no_write (thandle_t handle, + tdata_t buffer, + tsize_t size) +{ + return (tsize_t) -1; +} + +static tsize_t +tiff_io_write (thandle_t handle, + tdata_t buffer, + tsize_t size) +{ + TiffIO *io = (TiffIO *) handle; + + if (io->position > io->size || + io->size - io->position < size) + { + io->size = io->position + size; + io->data = g_realloc (io->data, io->size); + } + + memcpy (io->data + io->position, buffer, size); + io->position += size; + + return (tsize_t) size; +} + +static toff_t +tiff_io_seek (thandle_t handle, + toff_t offset, + int whence) +{ + TiffIO *io = (TiffIO *) handle; + + switch (whence) + { + default: + return -1; + case SEEK_SET: + break; + case SEEK_CUR: + offset += io->position; + break; + case SEEK_END: + offset += io->size; + break; + } + if (offset < 0) + return -1; + + io->position = offset; + + return offset; +} + +static int +tiff_io_close (thandle_t handle) +{ + TiffIO *io = (TiffIO *) handle; + + if (io->out_bytes) + *io->out_bytes = g_bytes_new_take (io->data, io->size); + + g_free (io); + + return 0; +} + +static toff_t +tiff_io_get_file_size (thandle_t handle) +{ + TiffIO *io = (TiffIO *) handle; + + return io->size; +} + +static TIFF * +tiff_open_read (GBytes *bytes) +{ + TiffIO *io; + + TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning); + TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error); + + io = g_new0 (TiffIO, 1); + + io->data = (char *) g_bytes_get_data (bytes, &io->size); + + return TIFFClientOpen ("GTK-read", "r", + (thandle_t) io, + tiff_io_read, + tiff_io_no_write, + tiff_io_seek, + tiff_io_close, + tiff_io_get_file_size, + NULL, NULL); +} + +static TIFF * +tiff_open_write (GBytes **result) +{ + TiffIO *io; + + TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning); + TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error); + + io = g_new0 (TiffIO, 1); + + io->out_bytes = result; + + return TIFFClientOpen ("GTK-write", "w", + (thandle_t) io, + tiff_io_no_read, + tiff_io_write, + tiff_io_seek, + tiff_io_close, + tiff_io_get_file_size, + NULL, NULL); +} + +/* }}} */ +/* {{{ Format conversion */ + +static void +flip_02 (guchar *data, + int width, + int height, + int stride) +{ + gsize x, y; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + guchar tmp; + tmp = data[x * 4]; + data[x * 4] = data[x * 4 + 2]; + data[x * 4 + 2] = tmp; + } + data += stride; + } +} + +/* }}} */ +/* {{{ Public API */ + +static struct { + GdkMemoryFormat format; + guint16 bits_per_sample; + guint16 samples_per_pixel; + guint16 sample_format; +} format_data[] = { + { GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, 8, 4, SAMPLEFORMAT_UINT }, + { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT }, + { GDK_MEMORY_R16G16B16, 16, 3, SAMPLEFORMAT_UINT }, + { GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_UINT }, + { GDK_MEMORY_R16G16B16_FLOAT, 16, 3, SAMPLEFORMAT_IEEEFP }, + { GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_IEEEFP }, + { GDK_MEMORY_R32G32B32_FLOAT, 32, 3, SAMPLEFORMAT_IEEEFP }, + { GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, 32, 4, SAMPLEFORMAT_IEEEFP }, +}; + +GBytes * +gdk_save_tiff (GdkTexture *texture) +{ + TIFF *tif; + int width, height, stride; + guint16 bits_per_sample = 0; + guint16 samples_per_pixel = 0; + guint16 sample_format = 0; + const guchar *line; + const guchar *data; + guchar *new_data = NULL; + GBytes *result = NULL; + GdkTexture *memory_texture; + GdkMemoryFormat format; + + tif = tiff_open_write (&result); + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + memory_texture = gdk_texture_download_texture (texture); + format = gdk_memory_texture_get_format (GDK_MEMORY_TEXTURE (memory_texture)); + + for (int i = 0; i < G_N_ELEMENTS (format_data); i++) + { + if (format == format_data[i].format) + { + data = gdk_memory_texture_get_data (GDK_MEMORY_TEXTURE (memory_texture)); + stride = gdk_memory_texture_get_stride (GDK_MEMORY_TEXTURE (memory_texture)); + bits_per_sample = format_data[i].bits_per_sample; + samples_per_pixel = format_data[i].samples_per_pixel; + sample_format = format_data[i].sample_format; + break; + } + } + + if (bits_per_sample == 0) + { + /* An 8-bit format we don't have in the table, handle + * it by converting to R8G8B8A8_PREMULTIPLIED + */ + stride = width * 4; + new_data = g_malloc (stride * height); + gdk_texture_download (memory_texture, new_data, stride); +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + flip_02 (new_data, width, height, stride); +#endif + data = new_data; + bits_per_sample = 8; + samples_per_pixel = 4; + sample_format = SAMPLEFORMAT_UINT; + } + + TIFFSetField (tif, TIFFTAG_SOFTWARE, "GTK"); + TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample); + TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel); + TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sample_format); + TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + // TODO: save gamma / colorspace + + if (samples_per_pixel > 3) + { + guint16 extra_samples[] = { EXTRASAMPLE_ASSOCALPHA }; + TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples); + } + + TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + + line = (const guchar *)data; + for (int y = 0; y < height; y++) + { + if (TIFFWriteScanline (tif, (void *)line, y, 0) == -1) + { + TIFFClose (tif); + g_free (new_data); + g_object_unref (memory_texture); + return NULL; + } + + line += stride; + } + + TIFFFlushData (tif); + TIFFClose (tif); + + g_assert (result); + + g_free (new_data); + g_object_unref (memory_texture); + + return result; +} + +static GdkTexture * +load_fallback (TIFF *tif, + GError **error) +{ + int width, height; + guchar *data; + GBytes *bytes; + GdkTexture *texture; + + TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height); + + data = g_malloc (width * height * 4); + + if (!TIFFReadRGBAImageOriented (tif, width, height, (uint32 *)data, ORIENTATION_TOPLEFT, 1)) + { + g_set_error_literal (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE, + _("Failed to load RGB data from TIFF file")); + g_free (data); + return NULL; + } + + bytes = g_bytes_new_take (data, width * height * 4); + + texture = gdk_memory_texture_new (width, height, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, + bytes, + width * 4); + + g_bytes_unref (bytes); + + return texture; +} + +GdkTexture * +gdk_load_tiff (GBytes *input_bytes, + GError **error) +{ + TIFF *tif; + guint16 samples_per_pixel; + guint16 bits_per_sample; + guint16 photometric; + guint16 planarconfig; + guint16 sample_format; + guint16 orientation; + guint32 width, height; + GdkMemoryFormat format; + guchar *data, *line; + gsize stride; + int bpp; + GBytes *bytes; + GdkTexture *texture; + + tif = tiff_open_read (input_bytes); + + TIFFSetDirectory (tif, 0); + + TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); + TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sample_format); + TIFFGetFieldDefaulted (tif, TIFFTAG_PHOTOMETRIC, &photometric); + TIFFGetFieldDefaulted (tif, TIFFTAG_PLANARCONFIG, &planarconfig); + TIFFGetFieldDefaulted (tif, TIFFTAG_ORIENTATION, &orientation); + TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGELENGTH, &height); + + if (samples_per_pixel == 4) + { + guint16 extra; + guint16 *extra_types; + + if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) + extra = 0; + + if (extra == 0 || extra_types[0] != EXTRASAMPLE_ASSOCALPHA) + { + texture = load_fallback (tif, error); + TIFFClose (tif); + return texture; + } + } + + format = 0; + + for (int i = 0; i < G_N_ELEMENTS (format_data); i++) + { + if (format_data[i].sample_format == sample_format && + format_data[i].bits_per_sample == bits_per_sample && + format_data[i].samples_per_pixel == samples_per_pixel) + { + format = format_data[i].format; + break; + } + } + + if (format == 0 || + photometric != PHOTOMETRIC_RGB || + planarconfig != PLANARCONFIG_CONTIG || + TIFFIsTiled (tif) || + orientation != ORIENTATION_TOPLEFT) + { + texture = load_fallback (tif, error); + TIFFClose (tif); + return texture; + } + + stride = width * gdk_memory_format_bytes_per_pixel (format); + + g_assert (TIFFScanlineSize (tif) == stride); + + data = g_try_malloc_n (height, stride); + if (!data) + { + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_TOO_LARGE, + _("Not enough memory for image size %ux%u"), width, height); + TIFFClose (tif); + return NULL; + } + + line = data; + for (int y = 0; y < height; y++) + { + if (TIFFReadScanline (tif, line, y, 0) == -1) + { + g_set_error (error, + GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE, + _("Reading data failed at row %d"), y); + TIFFClose (tif); + g_free (data); + return NULL; + } + + line += stride; + } + + bpp = gdk_memory_format_bytes_per_pixel (format); + bytes = g_bytes_new_take (data, width * height * bpp); + + texture = gdk_memory_texture_new (width, height, + format, + bytes, width * bpp); + g_bytes_unref (bytes); + + TIFFClose (tif); + + return texture; +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/loaders/gdktiffprivate.h b/gdk/loaders/gdktiffprivate.h new file mode 100644 index 0000000000..839e8855f0 --- /dev/null +++ b/gdk/loaders/gdktiffprivate.h @@ -0,0 +1,46 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * 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, see . + */ + +#ifndef __GDK_TIFF_PRIVATE_H__ +#define __GDK_TIFF_PRIVATE_H__ + +#include "gdktexture.h" +#include + +#define TIFF_SIGNATURE1 "MM\x00\x2a" +#define TIFF_SIGNATURE2 "II\x2a\x00" + +GdkTexture *gdk_load_tiff (GBytes *bytes, + GError **error); + +GBytes * gdk_save_tiff (GdkTexture *texture); + +static inline gboolean +gdk_is_tiff (GBytes *bytes) +{ + const char *data; + gsize size; + + data = g_bytes_get_data (bytes, &size); + + return (size > strlen (TIFF_SIGNATURE1) && + memcmp (data, TIFF_SIGNATURE1, strlen (TIFF_SIGNATURE1)) == 0) || + (size > strlen (TIFF_SIGNATURE2) && + memcmp (data, TIFF_SIGNATURE2, strlen (TIFF_SIGNATURE2)) == 0); +} + +#endif diff --git a/gdk/meson.build b/gdk/meson.build index ccc1738eab..06905233f8 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -51,6 +51,9 @@ gdk_public_sources = files([ 'gdktoplevelsize.c', 'gdktoplevel.c', 'gdkdragsurface.c', + 'loaders/gdkpng.c', + 'loaders/gdktiff.c', + 'loaders/gdkjpeg.c', ]) gdk_public_headers = files([ @@ -201,6 +204,9 @@ gdk_deps = [ platform_gio_dep, pangocairo_dep, vulkan_dep, + png_dep, + tiff_dep, + jpeg_dep, ] if profiler_enabled @@ -257,7 +263,7 @@ endif libgdk = static_library('gdk', sources: [gdk_sources, gdk_backends_gen_headers, gdkconfig], dependencies: gdk_deps + [libgtk_css_dep], - link_with: [libgtk_css, ], + link_with: [libgtk_css], include_directories: [confinc, gdkx11_inc, wlinc], c_args: libgdk_c_args + common_cflags, link_whole: gdk_backends, diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index c4c721e2e3..ed08bd4431 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -85,24 +85,17 @@ parse_texture (GtkCssParser *parser, scheme = g_uri_parse_scheme (url); if (scheme && g_ascii_strcasecmp (scheme, "data") == 0) { - GInputStream *stream; - GdkPixbuf *pixbuf; GBytes *bytes; - texture = NULL; - bytes = gtk_css_data_url_parse (url, NULL, &error); if (bytes) { - stream = g_memory_input_stream_new_from_bytes (bytes); + texture = gdk_texture_new_from_bytes (bytes, &error); g_bytes_unref (bytes); - pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error); - g_object_unref (stream); - if (pixbuf != NULL) - { - texture = gdk_texture_new_for_pixbuf (pixbuf); - g_object_unref (pixbuf); - } + } + else + { + texture = NULL; } } else @@ -2699,26 +2692,23 @@ render_node_print (Printer *p, case GSK_TEXTURE_NODE: { GdkTexture *texture = gsk_texture_node_get_texture (node); - cairo_surface_t *surface; - GByteArray *array; + GBytes *bytes; start_node (p, "texture"); append_rect_param (p, "bounds", &node->bounds); - surface = gdk_texture_download_surface (texture); - array = g_byte_array_new (); - cairo_surface_write_to_png_stream (surface, cairo_write_array, array); + bytes = gdk_texture_save_to_png_bytes (texture); _indent (p); g_string_append (p->str, "texture: url(\"data:image/png;base64,"); - b64 = base64_encode_with_linebreaks (array->data, array->len); + b64 = base64_encode_with_linebreaks (g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes)); append_escaping_newlines (p->str, b64); g_free (b64); g_string_append (p->str, "\");\n"); end_node (p); - g_byte_array_free (array, TRUE); - cairo_surface_destroy (surface); + g_bytes_unref (bytes); } break; diff --git a/gtk/gdkpixbufutils.c b/gtk/gdkpixbufutils.c index 2b48d76943..df8c542deb 100644 --- a/gtk/gdkpixbufutils.c +++ b/gtk/gdkpixbufutils.c @@ -20,6 +20,8 @@ #include "gdkpixbufutilsprivate.h" #include "gtkscalerprivate.h" +#include "gdk/gdktextureprivate.h" + static GdkPixbuf * load_from_stream (GdkPixbufLoader *loader, GInputStream *stream, @@ -91,7 +93,6 @@ size_prepared_cb (GdkPixbufLoader *loader, */ GdkPixbuf * _gdk_pixbuf_new_from_stream_scaled (GInputStream *stream, - const char *format, double scale, GCancellable *cancellable, GError **error) @@ -99,14 +100,7 @@ _gdk_pixbuf_new_from_stream_scaled (GInputStream *stream, GdkPixbufLoader *loader; GdkPixbuf *pixbuf; - if (format) - { - loader = gdk_pixbuf_loader_new_with_type (format, error); - if (!loader) - return NULL; - } - else - loader = gdk_pixbuf_loader_new (); + loader = gdk_pixbuf_loader_new (); if (scale != 0) g_signal_connect (loader, "size-prepared", @@ -153,7 +147,6 @@ size_prepared_cb2 (GdkPixbufLoader *loader, GdkPixbuf * _gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream, - const char *format, int width, int height, gboolean aspect, @@ -164,14 +157,7 @@ _gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream, GdkPixbuf *pixbuf; int scales[3]; - if (format) - { - loader = gdk_pixbuf_loader_new_with_type (format, error); - if (!loader) - return NULL; - } - else - loader = gdk_pixbuf_loader_new (); + loader = gdk_pixbuf_loader_new (); scales[0] = width; scales[1] = height; @@ -188,11 +174,10 @@ _gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream, GdkPixbuf * _gdk_pixbuf_new_from_stream (GInputStream *stream, - const char *format, GCancellable *cancellable, GError **error) { - return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error); + return _gdk_pixbuf_new_from_stream_scaled (stream, 0, cancellable, error); } /* Like gdk_pixbuf_new_from_resource_at_scale, but @@ -201,7 +186,6 @@ _gdk_pixbuf_new_from_stream (GInputStream *stream, */ GdkPixbuf * _gdk_pixbuf_new_from_resource_scaled (const char *resource_path, - const char *format, double scale, GError **error) { @@ -212,7 +196,7 @@ _gdk_pixbuf_new_from_resource_scaled (const char *resource_path, if (stream == NULL) return NULL; - pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error); + pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, scale, NULL, error); g_object_unref (stream); return pixbuf; @@ -220,15 +204,13 @@ _gdk_pixbuf_new_from_resource_scaled (const char *resource_path, GdkPixbuf * _gdk_pixbuf_new_from_resource (const char *resource_path, - const char *format, GError **error) { - return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error); + return _gdk_pixbuf_new_from_resource_scaled (resource_path, 0, error); } GdkPixbuf * _gdk_pixbuf_new_from_resource_at_scale (const char *resource_path, - const char *format, int width, int height, gboolean preserve_aspect, @@ -241,7 +223,7 @@ _gdk_pixbuf_new_from_resource_at_scale (const char *resource_path, if (stream == NULL) return NULL; - pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error); + pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, width, height, preserve_aspect, NULL, error); g_object_unref (stream); return pixbuf; @@ -539,7 +521,7 @@ gtk_load_symbolic_texture_from_file (GFile *file) if (stream == NULL) return NULL; - pixbuf = _gdk_pixbuf_new_from_stream (stream, "png", NULL, NULL); + pixbuf = _gdk_pixbuf_new_from_stream (stream, NULL, NULL); g_object_unref (stream); if (pixbuf == NULL) return NULL; @@ -597,42 +579,44 @@ GdkPaintable * gdk_paintable_new_from_bytes_scaled (GBytes *bytes, int scale_factor) { - GdkPixbufLoader *loader; - GdkPixbuf *pixbuf = NULL; LoaderData loader_data; GdkTexture *texture; GdkPaintable *paintable; loader_data.scale_factor = scale_factor; - loader = gdk_pixbuf_loader_new (); - g_signal_connect (loader, "size-prepared", - G_CALLBACK (on_loader_size_prepared), &loader_data); + if (gdk_texture_can_load (bytes)) + { + /* We know these formats can't be scaled */ + texture = gdk_texture_new_from_bytes (bytes, NULL); + if (texture == NULL) + return NULL; + } + else + { + GdkPixbufLoader *loader; + gboolean success; - if (!gdk_pixbuf_loader_write_bytes (loader, bytes, NULL)) - goto out; + loader = gdk_pixbuf_loader_new (); + g_signal_connect (loader, "size-prepared", + G_CALLBACK (on_loader_size_prepared), &loader_data); - if (!gdk_pixbuf_loader_close (loader, NULL)) - goto out; + success = gdk_pixbuf_loader_write_bytes (loader, bytes, NULL); + /* close even when writing failed */ + success &= gdk_pixbuf_loader_close (loader, NULL); - pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); - if (pixbuf != NULL) - g_object_ref (pixbuf); + if (!success) + return NULL; - out: - gdk_pixbuf_loader_close (loader, NULL); - g_object_unref (loader); + texture = gdk_texture_new_for_pixbuf (gdk_pixbuf_loader_get_pixbuf (loader)); + g_object_unref (loader); + } - if (!pixbuf) - return NULL; - - texture = gdk_texture_new_for_pixbuf (pixbuf); if (loader_data.scale_factor != 1) paintable = gtk_scaler_new (GDK_PAINTABLE (texture), loader_data.scale_factor); else paintable = g_object_ref ((GdkPaintable *)texture); - g_object_unref (pixbuf); g_object_unref (texture); return paintable; diff --git a/gtk/gdkpixbufutilsprivate.h b/gtk/gdkpixbufutilsprivate.h index 62efe3699e..2c4ab5b305 100644 --- a/gtk/gdkpixbufutilsprivate.h +++ b/gtk/gdkpixbufutilsprivate.h @@ -23,32 +23,26 @@ G_BEGIN_DECLS GdkPixbuf *_gdk_pixbuf_new_from_stream (GInputStream *stream, - const char *format, GCancellable *cancellable, GError **error); GdkPixbuf *_gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream, - const char *format, int width, int height, gboolean aspect, GCancellable *cancellable, GError **error); GdkPixbuf *_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream, - const char *format, double scale, GCancellable *cancellable, GError **error); GdkPixbuf *_gdk_pixbuf_new_from_resource (const char *resource_path, - const char *format, GError **error); GdkPixbuf *_gdk_pixbuf_new_from_resource_at_scale (const char *resource_path, - const char *format, int width, int height, gboolean preserve_aspect, GError **error); GdkPixbuf *_gdk_pixbuf_new_from_resource_scaled (const char *resource_path, - const char *format, double scale, GError **error); diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 9d85e6f2ed..697246790b 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -210,7 +210,6 @@ #include "gtkbuilderprivate.h" -#include "gdkpixbufutilsprivate.h" #include "gtkbuildableprivate.h" #include "gtkbuilderlistitemfactory.h" #include "gtkbuilderscopeprivate.h" @@ -226,7 +225,6 @@ #include "gtktypebuiltins.h" #include "gtkicontheme.h" #include "gtkiconthemeprivate.h" -#include "gdkpixbufutilsprivate.h" #include "gtkdebug.h" @@ -2296,9 +2294,63 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, break; case G_TYPE_OBJECT: case G_TYPE_INTERFACE: - if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF) || - G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE) || + if (G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE) || G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE)) + { + GObject *object = g_hash_table_lookup (priv->objects, string); + char *filename; + GError *tmp_error = NULL; + GdkTexture *texture = NULL; + + if (object) + { + if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value))) + { + g_value_set_object (value, object); + return TRUE; + } + else + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_VALUE, + "Could not load image '%s': " + " '%s' is already used as object id for a %s", + string, string, G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + } + + filename = _gtk_builder_get_resource_path (builder, string); + if (filename != NULL) + { + texture = gdk_texture_new_from_resource (filename); + } + else + { + GFile *file; + + filename = _gtk_builder_get_absolute_filename (builder, string); + file = g_file_new_for_path (filename); + texture = gdk_texture_new_from_file (file, &tmp_error); + g_object_unref (file); + } + + g_free (filename); + + if (!texture) + { + g_warning ("Could not load image '%s': %s", string, tmp_error->message); + g_error_free (tmp_error); + + texture = gdk_texture_new_from_resource (IMAGE_MISSING_RESOURCE_PATH); + } + + g_value_take_object (value, texture); + + ret = TRUE; + } + else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF)) { char *filename; GError *tmp_error = NULL; @@ -2344,28 +2396,13 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, if (pixbuf == NULL) { - g_warning ("Could not load image '%s': %s", - string, tmp_error->message); + g_warning ("Could not load image '%s': %s", string, tmp_error->message); g_error_free (tmp_error); - pixbuf = _gdk_pixbuf_new_from_resource (IMAGE_MISSING_RESOURCE_PATH, "png", NULL); + pixbuf = gdk_pixbuf_new_from_resource (IMAGE_MISSING_RESOURCE_PATH, NULL); } - if (pixbuf) - { - if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE) || - G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE)) - { - GdkTexture *texture = gdk_texture_new_for_pixbuf (pixbuf); - g_value_set_object (value, texture); - g_object_unref (texture); - } - else - { - g_value_set_object (value, pixbuf); - } - g_object_unref (G_OBJECT (pixbuf)); - } + g_value_take_object (value, pixbuf); g_free (filename); diff --git a/gtk/gtkcssimageurl.c b/gtk/gtkcssimageurl.c index d7271eda18..15d56ac21e 100644 --- a/gtk/gtkcssimageurl.c +++ b/gtk/gtkcssimageurl.c @@ -26,10 +26,9 @@ #include "gtkcssimageinvalidprivate.h" #include "gtkcssimagepaintableprivate.h" #include "gtkstyleproviderprivate.h" -#include "gdkpixbufutilsprivate.h" - #include "gtk/css/gtkcssdataurlprivate.h" + G_DEFINE_TYPE (GtkCssImageUrl, _gtk_css_image_url, GTK_TYPE_CSS_IMAGE) static GtkCssImage * @@ -181,28 +180,28 @@ gtk_css_image_url_parse (GtkCssImage *image, if (scheme && g_ascii_strcasecmp (scheme, "data") == 0) { GBytes *bytes; - GdkPaintable *paintable; GError *error = NULL; bytes = gtk_css_data_url_parse (url, NULL, &error); if (bytes) { - paintable = gdk_paintable_new_from_bytes_scaled (bytes, 1); + GdkTexture *texture; + + texture = gdk_texture_new_from_bytes (bytes, &error); g_bytes_unref (bytes); - if (paintable == NULL) + if (texture) + { + GdkPaintable *paintable = GDK_PAINTABLE (texture); + self->loaded_image = gtk_css_image_paintable_new (paintable, paintable); + } + else { - error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to load image from '%s'", url); gtk_css_parser_emit_error (parser, gtk_css_parser_get_start_location (parser), gtk_css_parser_get_end_location (parser), error); g_clear_error (&error); } - else - { - self->loaded_image = gtk_css_image_paintable_new (paintable, paintable); - } } else { diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 91ea519a26..1c2d83125c 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -3742,31 +3742,6 @@ gtk_icon_paintable_is_symbolic (GtkIconPaintable *icon) return icon->is_symbolic; } -static GLoadableIcon * -icon_get_loadable (GtkIconPaintable *icon) -{ - GFile *file; - GLoadableIcon *loadable; - - if (icon->loadable) - return g_object_ref (icon->loadable); - - if (icon->is_resource) - { - char *uri = g_strconcat ("resource://", icon->filename, NULL); - file = g_file_new_for_uri (uri); - g_free (uri); - } - else - file = g_file_new_for_path (icon->filename); - - loadable = G_LOADABLE_ICON (g_file_icon_new (file)); - - g_object_unref (file); - - return loadable; -} - /* This function contains the complicated logic for deciding * on the size at which to load the icon and loading it at * that size. @@ -3775,7 +3750,6 @@ static void icon_ensure_texture__locked (GtkIconPaintable *icon, gboolean in_thread) { - GdkPixbuf *source_pixbuf; gint64 before; int pixel_size; GError *load_error = NULL; @@ -3795,11 +3769,10 @@ icon_ensure_texture__locked (GtkIconPaintable *icon, /* At this point, we need to actually get the icon; either from the * builtin image or by loading the file */ - source_pixbuf = NULL; #ifdef G_OS_WIN32 if (icon->win32_icon) { - source_pixbuf = g_object_ref (icon->win32_icon); + icon->texture = gdk_texture_new_for_pixbuf (icon->win32_icon); } else #endif @@ -3807,6 +3780,8 @@ icon_ensure_texture__locked (GtkIconPaintable *icon, { if (icon->is_svg) { + GdkPixbuf *source_pixbuf; + if (gtk_icon_paintable_is_symbolic (icon)) source_pixbuf = gtk_make_symbolic_pixbuf_from_resource (icon->filename, pixel_size, pixel_size, @@ -3814,33 +3789,67 @@ icon_ensure_texture__locked (GtkIconPaintable *icon, &load_error); else source_pixbuf = _gdk_pixbuf_new_from_resource_at_scale (icon->filename, - "svg", pixel_size, pixel_size, TRUE, &load_error); + if (source_pixbuf) + { + icon->texture = gdk_texture_new_for_pixbuf (source_pixbuf); + g_object_unref (source_pixbuf); + } } else - source_pixbuf = _gdk_pixbuf_new_from_resource (icon->filename, - g_str_has_suffix (icon->filename, ".xpm") ? "xpm" : "png", - &load_error); - - if (source_pixbuf == NULL) + icon->texture = gdk_texture_new_from_resource (icon->filename); + } + else if (icon->filename) + { + if (icon->is_svg) { - g_warning ("Failed to load icon %s: %s", icon->filename, load_error->message); - g_clear_error (&load_error); + GdkPixbuf *source_pixbuf; + + if (gtk_icon_paintable_is_symbolic (icon)) + source_pixbuf = gtk_make_symbolic_pixbuf_from_path (icon->filename, + pixel_size, pixel_size, + icon->desired_scale, + &load_error); + else + { + GFile *file = g_file_new_for_path (icon->filename); + GInputStream *stream = G_INPUT_STREAM (g_file_read (file, NULL, &load_error)); + + g_object_unref (file); + if (stream) + { + source_pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, + pixel_size, pixel_size, + TRUE, NULL, + &load_error); + g_object_unref (stream); + } + else + source_pixbuf = NULL; + } + if (source_pixbuf) + { + icon->texture = gdk_texture_new_for_pixbuf (source_pixbuf); + g_object_unref (source_pixbuf); + } + } + else + { + icon->texture = gdk_texture_new_from_filename (icon->filename, &load_error); } } else { - GLoadableIcon *loadable; GInputStream *stream; + GdkPixbuf *source_pixbuf; - loadable = icon_get_loadable (icon); - stream = g_loadable_icon_load (loadable, + g_assert (icon->loadable); + + stream = g_loadable_icon_load (icon->loadable, pixel_size, NULL, NULL, &load_error); - g_object_unref (loadable); - if (stream) { /* SVG icons are a special case - we just immediately scale them @@ -3848,46 +3857,32 @@ icon_ensure_texture__locked (GtkIconPaintable *icon, */ if (icon->is_svg) { - if (gtk_icon_paintable_is_symbolic (icon)) - source_pixbuf = gtk_make_symbolic_pixbuf_from_path (icon->filename, + source_pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, pixel_size, pixel_size, - icon->desired_scale, + TRUE, NULL, &load_error); - else - source_pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, - "svg", - pixel_size, pixel_size, - TRUE, NULL, - &load_error); } else source_pixbuf = _gdk_pixbuf_new_from_stream (stream, - g_str_has_suffix (icon->filename, ".xpm") ? "xpm" : "png", NULL, &load_error); g_object_unref (stream); - } - - if (source_pixbuf == NULL) - { - g_warning ("Failed to load icon %s: %s", icon->filename, load_error->message); - g_clear_error (&load_error); + if (source_pixbuf) + { + icon->texture = gdk_texture_new_for_pixbuf (source_pixbuf); + g_object_unref (source_pixbuf); + } } } - if (!source_pixbuf) + if (!icon->texture) { - source_pixbuf = _gdk_pixbuf_new_from_resource (IMAGE_MISSING_RESOURCE_PATH, "png", NULL); + g_warning ("Failed to load icon %s: %s", icon->filename, load_error->message); + g_clear_error (&load_error); + icon->texture = gdk_texture_new_from_resource (IMAGE_MISSING_RESOURCE_PATH); icon->icon_name = g_strdup ("image-missing"); icon->is_symbolic = FALSE; - g_assert (source_pixbuf != NULL); } - /* Actual scaling is done during rendering, so just keep the source pixbuf as a texture */ - icon->texture = gdk_texture_new_for_pixbuf (source_pixbuf); - g_object_unref (source_pixbuf); - - g_assert (icon->texture != NULL); - if (GDK_PROFILER_IS_RUNNING) { gint64 end = GDK_PROFILER_CURRENT_TIME; @@ -4089,28 +4084,6 @@ gtk_icon_paintable_new_for_file (GFile *file, return icon; } -static GtkIconPaintable * -gtk_icon_paintable_new_for_pixbuf (GtkIconTheme *icon_theme, - GdkPixbuf *pixbuf, - int size, - int scale) -{ - GtkIconPaintable *icon; - int width, height; - - if (size <= 0) - { - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - size = MAX (width, height); - } - - icon = icon_paintable_new (NULL, size, scale); - icon->texture = gdk_texture_new_for_pixbuf (pixbuf); - - return icon; -} - /** * gtk_icon_theme_lookup_by_gicon: * @self: a `GtkIconTheme` @@ -4140,17 +4113,23 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme *self, g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL); g_return_val_if_fail (G_IS_ICON (gicon), NULL); + g_return_val_if_fail (size > 0, NULL); + g_return_val_if_fail (scale > 0, NULL); /* We can't render emblemed icons atm, but at least render the base */ while (G_IS_EMBLEMED_ICON (gicon)) gicon = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (gicon)); g_assert (gicon); /* shut up gcc -Wnull-dereference */ - if (GDK_IS_PIXBUF (gicon)) + if (GDK_IS_TEXTURE (gicon)) { - GdkPixbuf *pixbuf = GDK_PIXBUF (gicon); - - icon = gtk_icon_paintable_new_for_pixbuf (self, pixbuf, size, scale); + icon = icon_paintable_new (NULL, size, scale); + icon->texture = g_object_ref (GDK_TEXTURE (icon)); + } + else if (GDK_IS_PIXBUF (gicon)) + { + icon = icon_paintable_new (NULL, size, scale); + icon->texture = gdk_texture_new_for_pixbuf (GDK_PIXBUF (icon)); } else if (G_IS_FILE_ICON (gicon)) { diff --git a/meson.build b/meson.build index 764755bf33..e85daf88f5 100644 --- a/meson.build +++ b/meson.build @@ -207,6 +207,17 @@ foreach func : check_functions endif endforeach +# We use links() because sigsetjmp() is often a macro hidden behind other macros +cdata.set('HAVE_SIGSETJMP', + cc.links('''#define _POSIX_SOURCE + #include + int main (void) { + sigjmp_buf env; + sigsetjmp (env, 0); + return 0; + }''', name: 'sigsetjmp'), +) + # Check for __uint128_t (gcc) by checking for 128-bit division uint128_t_src = '''int main() { static __uint128_t v1 = 100; @@ -389,6 +400,16 @@ pangocairo_dep = dependency('pangocairo', version: pango_req, pixbuf_dep = dependency('gdk-pixbuf-2.0', version: gdk_pixbuf_req, fallback : ['gdk-pixbuf', 'gdkpixbuf_dep'], default_options: ['png=enabled', 'jpeg=enabled', 'builtin_loaders=png,jpeg', 'man=false']) +png_dep = dependency('libpng', + fallback: ['libpng', 'libpng_dep'], + required: true) +tiff_dep = dependency('libtiff-4', + fallback: ['libtiff', 'libtiff4_dep'], + required: true) +jpeg_dep = dependency('libjpeg', + fallback: ['libjpeg-turbo', 'jpeg_dep'], + required: true) + epoxy_dep = dependency('epoxy', version: epoxy_req, fallback: ['libepoxy', 'libepoxy_dep']) harfbuzz_dep = dependency('harfbuzz', version: '>= 2.1.0', required: false, diff --git a/subprojects/libpng.wrap b/subprojects/libpng.wrap new file mode 100644 index 0000000000..9d6c6b3078 --- /dev/null +++ b/subprojects/libpng.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = libpng-1.6.37 +source_url = https://github.com/glennrp/libpng/archive/v1.6.37.tar.gz +source_filename = libpng-1.6.37.tar.gz +source_hash = ca74a0dace179a8422187671aee97dd3892b53e168627145271cad5b5ac81307 +patch_url = https://wrapdb.mesonbuild.com/v2/libpng_1.6.37-3/get_patch +patch_filename = libpng-1.6.37-3-wrap.zip +patch_hash = 6c9f32fd9150b3a96ab89be52af664e32207e10aa9f5fb9aa015989ee2dd7100 + +[provide] +libpng = libpng_dep + diff --git a/subprojects/libtiff.wrap b/subprojects/libtiff.wrap new file mode 100644 index 0000000000..7ad7f5e1f5 --- /dev/null +++ b/subprojects/libtiff.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = tiff-4.1.0 +source_url = http://download.osgeo.org/libtiff/tiff-4.1.0.zip +source_filename = tiff-4.1.0.zip +source_hash = 6f3dbed9d2ecfed33c7192b5c01884078970657fa21b4ad28e3cdf3438eb2419 +patch_filename = libtiff_4.1.0-4_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/libtiff_4.1.0-4/get_patch +patch_hash = c0fe078d06e5a7f2480a96c3897a7b3b9fa9a42c08fb76ae5f1dd59e0519a14e + +[provide] +libtiff-4 = libtiff4_dep + diff --git a/testsuite/gdk/contentserializer.c b/testsuite/gdk/contentserializer.c index d20a60d99e..a807951707 100644 --- a/testsuite/gdk/contentserializer.c +++ b/testsuite/gdk/contentserializer.c @@ -30,6 +30,49 @@ compare_rgba_values (GValue *v1, GValue *v2) (GdkRGBA *)g_value_get_boxed (v2)); } +static gboolean +textures_equal (GdkTexture *t1, GdkTexture *t2) +{ + guchar *d1, *d2; + int width, height; + gboolean ret; + + width = gdk_texture_get_width (t1); + height = gdk_texture_get_height (t1); + + if (width != gdk_texture_get_width (t2)) + return FALSE; + if (height != gdk_texture_get_height (t2)) + return FALSE; + + d1 = g_malloc (width * height * 4); + d2 = g_malloc (width * height * 4); + + gdk_texture_download (t1, d1, width * 4); + gdk_texture_download (t2, d2, width * 4); + + ret = memcmp (d1, d2, width * height * 4) == 0; + + if (!ret) + { + gdk_texture_save_to_png (t1, "texture1.png"); + gdk_texture_save_to_png (t2, "texture2.png"); + } + g_free (d1); + g_free (d2); + + return ret; +} + +static gboolean +compare_texture_values (GValue *v1, GValue *v2) +{ + return G_VALUE_TYPE (v1) == GDK_TYPE_TEXTURE && + G_VALUE_TYPE (v2) == GDK_TYPE_TEXTURE && + textures_equal ((GdkTexture *)g_value_get_object (v1), + (GdkTexture *)g_value_get_object (v2)); +} + static gboolean compare_file_values (GValue *v1, GValue *v2) { @@ -125,7 +168,7 @@ test_content_roundtrip (const GValue *value, TestData data = { 0, }; data.ostream = g_memory_output_stream_new_resizable (); - data.mime_type = g_strdup (mime_type); + data.mime_type = mime_type; g_value_init (&data.value, G_VALUE_TYPE (value)); g_value_copy (value, &data.value); data.compare = compare; @@ -182,6 +225,30 @@ test_content_color (void) g_value_unset (&value); } +static void +test_content_texture (gconstpointer data) +{ + const char *mimetype = data; + GValue value = G_VALUE_INIT; + char *path; + GFile *file; + GdkTexture *texture; + GError *error = NULL; + + path = g_test_build_filename (G_TEST_DIST, "image-data", "image.png", NULL); + file = g_file_new_for_path (path); + texture = gdk_texture_new_from_file (file, &error); + g_assert_no_error (error); + g_object_unref (file); + g_free (path); + + g_value_init (&value, GDK_TYPE_TEXTURE); + g_value_set_object (&value, texture); + test_content_roundtrip (&value, mimetype, compare_texture_values); + g_value_unset (&value); + g_object_unref (texture); +} + static void test_content_file (void) { @@ -406,6 +473,8 @@ main (int argc, char *argv[]) g_test_add_func ("/content/text_plain_utf8", test_content_text_plain_utf8); g_test_add_func ("/content/text_plain", test_content_text_plain); g_test_add_func ("/content/color", test_content_color); + g_test_add_data_func ("/content/texture/png", "image/png", test_content_texture); + g_test_add_data_func ("/content/texture/tiff", "image/tiff", test_content_texture); g_test_add_func ("/content/file", test_content_file); g_test_add_func ("/content/files", test_content_files); g_test_add_func ("/content/custom", test_custom_format); diff --git a/testsuite/gdk/image-data/image.jpeg b/testsuite/gdk/image-data/image.jpeg new file mode 100644 index 0000000000..53ebb349bc Binary files /dev/null and b/testsuite/gdk/image-data/image.jpeg differ diff --git a/testsuite/gdk/image-data/image.png b/testsuite/gdk/image-data/image.png new file mode 100644 index 0000000000..49ee0d459e Binary files /dev/null and b/testsuite/gdk/image-data/image.png differ diff --git a/testsuite/gdk/image-data/image.tiff b/testsuite/gdk/image-data/image.tiff new file mode 100644 index 0000000000..ddd0d03609 Binary files /dev/null and b/testsuite/gdk/image-data/image.tiff differ diff --git a/testsuite/gdk/image.c b/testsuite/gdk/image.c new file mode 100644 index 0000000000..57a0c112c2 --- /dev/null +++ b/testsuite/gdk/image.c @@ -0,0 +1,129 @@ +#include +#include "gdk/loaders/gdkpngprivate.h" +#include "gdk/loaders/gdktiffprivate.h" +#include "gdk/loaders/gdkjpegprivate.h" + +static void +assert_texture_equal (GdkTexture *t1, + GdkTexture *t2) +{ + int width; + int height; + int stride; + guchar *d1; + guchar *d2; + + width = gdk_texture_get_width (t1); + height = gdk_texture_get_height (t1); + stride = 4 * width; + + g_assert_cmpint (width, ==, gdk_texture_get_width (t2)); + g_assert_cmpint (height, ==, gdk_texture_get_height (t2)); + + d1 = g_malloc (stride * height); + d2 = g_malloc (stride * height); + + gdk_texture_download (t1, d1, stride); + gdk_texture_download (t2, d2, stride); + + g_assert_cmpmem (d1, stride * height, d2, stride * height); + + g_free (d1); + g_free (d2); +} + +static void +test_load_image (gconstpointer data) +{ + const char *filename = data; + GdkTexture *texture; + char *path; + GFile *file; + GBytes *bytes; + GError *error = NULL; + + path = g_test_build_filename (G_TEST_DIST, "image-data", filename, NULL); + file = g_file_new_for_path (path); + bytes = g_file_load_bytes (file, NULL, NULL, &error); + g_assert_no_error (error); + + if (g_str_has_suffix (filename, ".png")) + texture = gdk_load_png (bytes, &error); + else if (g_str_has_suffix (filename, ".tiff")) + texture = gdk_load_tiff (bytes, &error); + else if (g_str_has_suffix (filename, ".jpeg")) + texture = gdk_load_jpeg (bytes, &error); + else + g_assert_not_reached (); + + g_assert_no_error (error); + g_assert_true (GDK_IS_TEXTURE (texture)); + g_assert_cmpint (gdk_texture_get_width (texture), ==, 32); + g_assert_cmpint (gdk_texture_get_height (texture), ==, 32); + + g_object_unref (texture); + g_bytes_unref (bytes); + g_object_unref (file); + g_free (path); +} + +static void +test_save_image (gconstpointer test_data) +{ + const char *filename = test_data; + char *path; + GFile *file; + GdkTexture *texture; + GFile *file2; + GdkTexture *texture2; + GError *error = NULL; + GBytes *bytes = NULL; + GIOStream *stream; + + path = g_test_build_filename (G_TEST_DIST, "image-data", filename, NULL); + file = g_file_new_for_path (path); + texture = gdk_texture_new_from_file (file, &error); + g_assert_no_error (error); + + if (g_str_has_suffix (filename, ".png")) + bytes = gdk_save_png (texture); + else if (g_str_has_suffix (filename, ".tiff")) + bytes = gdk_save_tiff (texture); + else + g_assert_not_reached (); + + file2 = g_file_new_tmp ("imageXXXXXX", (GFileIOStream **)&stream, NULL); + g_object_unref (stream); + g_file_replace_contents (file2, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + NULL, FALSE, 0, + NULL, NULL, &error); + g_assert_no_error (error); + + texture2 = gdk_texture_new_from_file (file2, &error); + g_assert_no_error (error); + + assert_texture_equal (texture, texture2); + + g_bytes_unref (bytes); + g_object_unref (texture2); + g_object_unref (file2); + g_object_unref (texture); + g_object_unref (file); + g_free (path); +} + +int +main (int argc, char *argv[]) +{ + (g_test_init) (&argc, &argv, NULL); + + g_test_add_data_func ("/image/load/png", "image.png", test_load_image); + g_test_add_data_func ("/image/load/tiff", "image.tiff", test_load_image); + g_test_add_data_func ("/image/load/jpeg", "image.jpeg", test_load_image); + g_test_add_data_func ("/image/save/png", "image.png", test_save_image); + g_test_add_data_func ("/image/save/tiff", "image.tiff", test_save_image); + + return g_test_run (); +} diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c index 021a7ad11b..020d6927e4 100644 --- a/testsuite/gdk/memorytexture.c +++ b/testsuite/gdk/memorytexture.c @@ -12,6 +12,10 @@ typedef enum { TEXTURE_METHOD_LOCAL, TEXTURE_METHOD_GL, TEXTURE_METHOD_GL_RELEASED, + TEXTURE_METHOD_PNG, + TEXTURE_METHOD_PNG_PIXBUF, + TEXTURE_METHOD_TIFF, + TEXTURE_METHOD_TIFF_PIXBUF, N_TEXTURE_METHODS } TextureMethod; @@ -448,6 +452,68 @@ create_texture (GdkMemoryFormat format, gdk_gl_texture_release (GDK_GL_TEXTURE (texture)); break; + case TEXTURE_METHOD_PNG: + { + GBytes *bytes = gdk_texture_save_to_png_bytes (texture); + g_assert (bytes); + g_object_unref (texture); + texture = gdk_texture_new_from_bytes (bytes, NULL); + g_assert (texture); + g_bytes_unref (bytes); + } + break; + + case TEXTURE_METHOD_PNG_PIXBUF: + { + GInputStream *stream; + GdkPixbuf *pixbuf; + GBytes *bytes; + + bytes = gdk_texture_save_to_png_bytes (texture); + g_assert (bytes); + g_object_unref (texture); + stream = g_memory_input_stream_new_from_bytes (bytes); + pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); + g_object_unref (stream); + g_assert (pixbuf); + texture = gdk_texture_new_for_pixbuf (pixbuf); + g_assert (texture); + g_object_unref (pixbuf); + g_bytes_unref (bytes); + } + break; + + case TEXTURE_METHOD_TIFF: + { + GBytes *bytes = gdk_texture_save_to_tiff_bytes (texture); + g_assert (bytes); + g_object_unref (texture); + texture = gdk_texture_new_from_bytes (bytes, NULL); + g_assert (texture); + g_bytes_unref (bytes); + } + break; + + case TEXTURE_METHOD_TIFF_PIXBUF: + { + GInputStream *stream; + GdkPixbuf *pixbuf; + GBytes *bytes; + + bytes = gdk_texture_save_to_png_bytes (texture); + g_assert (bytes); + g_object_unref (texture); + stream = g_memory_input_stream_new_from_bytes (bytes); + pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); + g_object_unref (stream); + g_assert (pixbuf); + texture = gdk_texture_new_for_pixbuf (pixbuf); + g_assert (texture); + g_object_unref (pixbuf); + g_bytes_unref (bytes); + } + break; + case N_TEXTURE_METHODS: default: g_assert_not_reached (); @@ -584,7 +650,7 @@ add_test (const char *name, { for (method = 0; method < N_TEXTURE_METHODS; method++) { - const char *method_names[N_TEXTURE_METHODS] = { "local", "gl", "gl-released" }; + const char *method_names[N_TEXTURE_METHODS] = { "local", "gl", "gl-released", "png", "png-pixbuf", "tiff", "tiff-pixbuf" }; char *test_name = g_strdup_printf ("%s/%s/%s", name, g_enum_get_value (enum_class, format)->value_nick, diff --git a/testsuite/gdk/meson.build b/testsuite/gdk/meson.build index 3eff25868d..c259be1f06 100644 --- a/testsuite/gdk/meson.build +++ b/testsuite/gdk/meson.build @@ -49,6 +49,31 @@ foreach t : tests ) endforeach +internal_tests = [ + 'image' +] + +foreach t : internal_tests + test_exe = executable(t, '@0@.c'.format(t), + c_args: common_cflags, + dependencies: libgtk_static_dep, + install: get_option('install-tests'), + install_dir: testexecdir, + ) + + test(t, test_exe, + args: [ '--tap', '-k' ], + protocol: 'tap', + env: [ + 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), + 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), + 'DBUS_SESSION_BUS_ADDRESS=', + ], + suite: 'gdk', + ) +endforeach + + if get_option('install-tests') foreach t : tests test_cdata = configuration_data() @@ -63,4 +88,5 @@ if get_option('install-tests') endforeach install_subdir('clipboard-data', install_dir: testexecdir) + install_subdir('image-data', install_dir: testexecdir) endif diff --git a/testsuite/gsk/nodeparser/empty-texture.ref.node b/testsuite/gsk/nodeparser/empty-texture.ref.node index 6b2c5ead8a..b8027cb332 100644 --- a/testsuite/gsk/nodeparser/empty-texture.ref.node +++ b/testsuite/gsk/nodeparser/empty-texture.ref.node @@ -1,7 +1,6 @@ texture { bounds: 0 0 50 50; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAAKUlE\ -QVQYlWP8z3DmPwMaYGQwYUQXY0IXwAUGUCGGoxkYGBiweXAoeAYAz44F3e3U1xUAAAAASUVORK5C\ -YII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAKUlEQVQYlWP8z3DmPwMaYGQwYUQX\ +Y0IXwAUGUCGGoxkYGBiweXAoeAYAz44F3e3U1xUAAAAASUVORK5CYII=\ "); } diff --git a/testsuite/gsk/nodeparser/widgetfactory.ref.node b/testsuite/gsk/nodeparser/widgetfactory.ref.node index 3c7e8d6415..ac96d76476 100644 --- a/testsuite/gsk/nodeparser/widgetfactory.ref.node +++ b/testsuite/gsk/nodeparser/widgetfactory.ref.node @@ -39,129 +39,129 @@ transform { offset: 0.180392 0.203922 0.211765 0; child: texture { bounds: 1068.6 0 256 256; - texture: url("data:image/png;base64,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\ + texture: url("data:image/png;base64,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\ +Wk3S1gAAAABJRU5ErkJggg==\ "); } } @@ -277,8 +277,8 @@ GIZhGIZhGIZhGIYh/D/GS6zNWk3S1gAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 4 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAH0lE\ -QVQ4jWNgGPKAEY3/n1R9TNRzy0CB0TAYDQOqAADB/QMKPEUKLAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAH0lEQVQ4jWNgGPKAEY3/n1R9TNRz\ +y0CB0TAYDQOqAADB/QMKPEUKLAAAAABJRU5ErkJggg==\ "); } } @@ -294,10 +294,10 @@ QVQ4jWNgGPKAEY3/n1R9TNRzy0CB0TAYDQOqAADB/QMKPEUKLAAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAfUlE\ -QVQ4je3RMQoCMRBG4Q+voEIKC/H+d7Cwd1GULSzEUyg2azOyQ1gxB9gHgcnknxdCmJliwAHr1Fti\ -H2d/uUTwHJIVuuj1LYINbklyivqObYuglgx4YDcVXPwQvPFM+1esJgquxjf3qS4tgmM1UJKkaxF8\ -g/m2YvyJmYoPk2IlW2Wofj0AAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfUlEQVQ4je3RMQoCMRBG4Q+voEIK\ +C/H+d7Cwd1GULSzEUyg2azOyQ1gxB9gHgcnknxdCmJliwAHr1FtiH2d/uUTwHJIVuuj1LYINbkly\ +ivqObYuglgx4YDcVXPwQvPFM+1esJgquxjf3qS4tgmM1UJKkaxF8g/m2YvyJmYoPk2IlW2Wofj0A\ +AAAASUVORK5CYII=\ "); } } @@ -373,9 +373,9 @@ transform { offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -430,9 +430,9 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -473,11 +473,11 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.654902 0.666667 0.666667 0; child: texture { bounds: 0 8 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAyklE\ -QVQ4jc3S3UpCURAF4K8w8ypQA1EfpkcI8SmEEh9STYzQQNCE3sELiQiiLhy1I/ucbl0wDHt+Fmv2\ -DOeGCh4xxic2eIrYVdSM8JNqbmMeyZS9oPnnnUEZr5FY4h63aKCDdeSe8wgGEXxDNaGujvcTRRns\ -mTuJ5v3Mp5bBRwQbCYK8PwGl8N/hvxIEF4nYAZfhF+HvioqL8BCyVqgl8jVM5ewfrjGLgjW6uAnr\ -Oq5xWqSi5XgLeYfU+m+UMvqYYGt3ykP0QuUZ4hei9keg1mtPrwAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAyklEQVQ4jc3S3UpCURAF4K8w8ypQ\ +A1EfpkcI8SmEEh9STYzQQNCE3sELiQiiLhy1I/ucbl0wDHt+Fmv2DOeGCh4xxic2eIrYVdSM8JNq\ +bmMeyZS9oPnnnUEZr5FY4h63aKCDdeSe8wgGEXxDNaGujvcTRRnsmTuJ5v3Mp5bBRwQbCYK8PwGl\ +8N/hvxIEF4nYAZfhF+HvioqL8BCyVqgl8jVM5ewfrjGLgjW6uAnrOq5xWqSi5XgLeYfU+m+UMvqY\ +YGt3ykP0QuUZ4hei9keg1mtPrwAAAABJRU5ErkJggg==\ "); } } @@ -557,10 +557,10 @@ Oq5xWqSi5XgLeYfU+m+UMvqYYGt3ykP0QuUZ4hei9keg1mtPrwAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 4 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAfUlE\ -QVQ4je3RMQoCMRBG4Q+voEIKC/H+d7Cwd1GULSzEUyg2azOyQ1gxB9gHgcnknxdCmJliwAHr1Fti\ -H2d/uUTwHJIVuuj1LYINbklyivqObYuglgx4YDcVXPwQvPFM+1esJgquxjf3qS4tgmM1UJKkaxF8\ -g/m2YvyJmYoPk2IlW2Wofj0AAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfUlEQVQ4je3RMQoCMRBG4Q+voEIK\ +C/H+d7Cwd1GULSzEUyg2azOyQ1gxB9gHgcnknxdCmJliwAHr1FtiH2d/uUTwHJIVuuj1LYINbkly\ +ivqObYuglgx4YDcVXPwQvPFM+1esJgquxjf3qS4tgmM1UJKkaxF8g/m2YvyJmYoPk2IlW2Wofj0A\ +AAAASUVORK5CYII=\ "); } } @@ -600,9 +600,9 @@ g/m2YvyJmYoPk2IlW2Wofj0AAAAASUVORK5CYII=\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -638,9 +638,9 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -679,9 +679,9 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -747,8 +747,8 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.615686 0.623529 0.623529 0; child: texture { bounds: 4 8 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAHElE\ -QVQ4jWNgGAXDADCi8f+Tqo+Jem4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAHElEQVQ4jWNgGAXDADCi8f+Tqo+J\ +em4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ "); } } @@ -767,9 +767,8 @@ QVQ4jWNgGAXDADCi8f+Tqo+Jem4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ offset: 0.615686 0.623529 0.623529 0; child: texture { bounds: 4 8 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAKElE\ -QVQ4jWNgGPbgPxTjBEyU2jDwBjCi8fH6F5s+il1ACIyEWBgGAADypgUMy1PhdwAAAABJRU5ErkJg\ -gg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKElEQVQ4jWNgGPbgPxTjBEyU2jDw\ +BjCi8fH6F5s+il1ACIyEWBgGAADypgUMy1PhdwAAAABJRU5ErkJggg==\ "); } } @@ -819,8 +818,8 @@ gg==\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 4 8 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAHElE\ -QVQ4jWNgGAXDADCi8f+Tqo+Jem4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAHElEQVQ4jWNgGAXDADCi8f+Tqo+J\ +em4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ "); } } @@ -846,9 +845,8 @@ QVQ4jWNgGAXDADCi8f+Tqo+Jem4ZBUMYAABDXwEEvj+CVwAAAABJRU5ErkJggg==\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 4 8 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAKElE\ -QVQ4jWNgGPbgPxTjBEyU2jDwBjCi8fH6F5s+il1ACIyEWBgGAADypgUMy1PhdwAAAABJRU5ErkJg\ -gg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKElEQVQ4jWNgGPbgPxTjBEyU2jDw\ +BjCi8fH6F5s+il1ACIyEWBgGAADypgUMy1PhdwAAAABJRU5ErkJggg==\ "); } } @@ -881,10 +879,10 @@ gg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAeElE\ -QVQokb3QXwpAQBDH8e9uzscRXEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUp\ -YBXUB+gEC19cAmyAQaAmRJloOiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAe\ -i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAeElEQVQokb3QXwpAQBDH8e9uzscR\ +XEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUpYBXUB+gEC19cAmyAQaAmRJlo\ +OiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAei77LDp72K7jAZx9tAAAAAElF\ +TkSuQmCC\ "); } } @@ -947,9 +945,9 @@ i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAOUlE\ -QVQokWNgGAVEAW8GBoYnDAwM/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4\ -GmjkhFgCAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAOUlEQVQokWNgGAVEAW8GBoYnDAwM\ +/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4GmjkhFgCAAAAAElFTkSuQmCC\ +\ "); } } @@ -985,10 +983,10 @@ GmjkhFgCAAAAAElFTkSuQmCC\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAeElE\ -QVQokb3QXwpAQBDH8e9uzscRXEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUp\ -YBXUB+gEC19cAmyAQaAmRJloOiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAe\ -i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAeElEQVQokb3QXwpAQBDH8e9uzscR\ +XEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUpYBXUB+gEC19cAmyAQaAmRJlo\ +OiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAei77LDp72K7jAZx9tAAAAAElF\ +TkSuQmCC\ "); } } @@ -1051,9 +1049,9 @@ i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAOUlE\ -QVQokWNgGAVEAW8GBoYnDAwM/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4\ -GmjkhFgCAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAOUlEQVQokWNgGAVEAW8GBoYnDAwM\ +/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4GmjkhFgCAAAAAElFTkSuQmCC\ +\ "); } } @@ -1152,9 +1150,9 @@ GmjkhFgCAAAAAElFTkSuQmCC\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAOUlE\ -QVQokWNgGAVEAW8GBoYnDAwM/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4\ -GmjkhFgCAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAOUlEQVQokWNgGAVEAW8GBoYnDAwM\ +/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4GmjkhFgCAAAAAElFTkSuQmCC\ +\ "); } } @@ -1193,9 +1191,9 @@ GmjkhFgCAAAAAElFTkSuQmCC\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAASklE\ -QVQokWNgGJmAjYGBoZWBgeERFLcwMDCwEqOxlYGB4T8abiZG42MsGh+iK2LCovE/MWLYNC4iUgwD\ -sDJA/PQQihsZiAyc4QYASeYTs7b/ALUAAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAASklEQVQokWNgGJmAjYGBoZWBgeER\ +FLcwMDCwEqOxlYGB4T8abiZG42MsGh+iK2LCovE/MWLYNC4iUgwDsDJA/PQQihsZiAyc4QYASeYT\ +s7b/ALUAAAAASUVORK5CYII=\ "); } } @@ -1264,9 +1262,9 @@ sDJA/PQQihsZiAyc4QYASeYTs7b/ALUAAAAASUVORK5CYII=\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 0 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAOUlE\ -QVQokWNgGAVEAW8GBoYnDAwM/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4\ -GmjkhFgCAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAOUlEQVQokWNgGAVEAW8GBoYnDAwM\ +/3HgxwwMDF7YND7GowmGH8EUM1HDqV4EbH3EwMDgSQ2Lhj0AABu4GmjkhFgCAAAAAElFTkSuQmCC\ +\ "); } } @@ -1292,13 +1290,12 @@ GmjkhFgCAAAAAElFTkSuQmCC\ transform: translate(8, 11.5) rotate(12.2788) translate(-8, -11.5); child: texture { bounds: 0 3.5 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABE0lE\ -QVQ4jbXSPy9DURjH8U9bSqhEmog/A6PFJGGRCInRZrQZTN6B1XsysJgMRklJDRYGtVBtWkRr6HOb\ -G2muRuJJbnJynvP9nt9zc/iHKuEIZ3jEFeaHhbfwgO6P73AYeAftAfANFn+Dp1FLQffYx8yw0Y9T\ -8B3mMs6OoYh8evMSnRDsZcCFkJf1fraRaCygFevzDEE3JBMYRSMR1CPaVwYsYk/Fup1swC2e0MBG\ -hqAcgtlI0hecxjgNHKRuSVcRKyHJi5EL0axiNxodrEWiFsaxhE1MBvOOCjq51A3rOMFrSNp4RhMv\ -+AhhHRfR6yeg9+4rWE0J8sjhLQTNgGsJlE6QVAnbWI65mzFOFdf4HMD8vb4BZVRHF0lPimAAAAAA\ -SUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABE0lEQVQ4jbXSPy9DURjH8U9bSqhE\ +mog/A6PFJGGRCInRZrQZTN6B1XsysJgMRklJDRYGtVBtWkRr6HObG2muRuJJbnJynvP9nt9zc/iH\ +KuEIZ3jEFeaHhbfwgO6P73AYeAftAfANFn+Dp1FLQffYx8yw0Y9T8B3mMs6OoYh8evMSnRDsZcCF\ +kJf1fraRaCygFevzDEE3JBMYRSMR1CPaVwYsYk/Fup1swC2e0MBGhqAcgtlI0hecxjgNHKRuSVcR\ +KyHJi5EL0axiNxodrEWiFsaxhE1MBvOOCjq51A3rOMFrSNp4RhMv+AhhHRfR6yeg9+4rWE0J8sjh\ +LQTNgGsJlE6QVAnbWI65mzFOFdf4HMD8vb4BZVRHF0lPimAAAAAASUVORK5CYII=\ "); } } @@ -1315,13 +1312,12 @@ SUVORK5CYII=\ transform: translate(8, 11.5) rotate(12.2788) translate(-8, -11.5); child: texture { bounds: 0 3.5 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABE0lE\ -QVQ4jbXSPy9DURjH8U9bSqhEmog/A6PFJGGRCInRZrQZTN6B1XsysJgMRklJDRYGtVBtWkRr6HOb\ -G2muRuJJbnJynvP9nt9zc/iHKuEIZ3jEFeaHhbfwgO6P73AYeAftAfANFn+Dp1FLQffYx8yw0Y9T\ -8B3mMs6OoYh8evMSnRDsZcCFkJf1fraRaCygFevzDEE3JBMYRSMR1CPaVwYsYk/Fup1swC2e0MBG\ -hqAcgtlI0hecxjgNHKRuSVcRKyHJi5EL0axiNxodrEWiFsaxhE1MBvOOCjq51A3rOMFrSNp4RhMv\ -+AhhHRfR6yeg9+4rWE0J8sjhLQTNgGsJlE6QVAnbWI65mzFOFdf4HMD8vb4BZVRHF0lPimAAAAAA\ -SUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABE0lEQVQ4jbXSPy9DURjH8U9bSqhE\ +mog/A6PFJGGRCInRZrQZTN6B1XsysJgMRklJDRYGtVBtWkRr6HObG2muRuJJbnJynvP9nt9zc/iH\ +KuEIZ3jEFeaHhbfwgO6P73AYeAftAfANFn+Dp1FLQffYx8yw0Y9T8B3mMs6OoYh8evMSnRDsZcCF\ +kJf1fraRaCygFevzDEE3JBMYRSMR1CPaVwYsYk/Fup1swC2e0MBGhqAcgtlI0hecxjgNHKRuSVcR\ +KyHJi5EL0axiNxodrEWiFsaxhE1MBvOOCjq51A3rOMFrSNp4RhMv+AhhHRfR6yeg9+4rWE0J8sjh\ +LQTNgGsJlE6QVAnbWI65mzFOFdf4HMD8vb4BZVRHF0lPimAAAAAASUVORK5CYII=\ "); } } @@ -1459,9 +1455,9 @@ SUVORK5CYII=\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -1500,9 +1496,9 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.831373 0.811765 0.792157 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAOklE\ -QVQ4jWNgGAWDD5QzMDD8J4DrKTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACH\ -ZRnQomjWaAAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOklEQVQ4jWNgGAWDD5QzMDD8J4Dr\ +KTGEoGZ8hhCtGZshODUz4zHgKAMDAyMDA8NBBgaGRlJtHwUkAACHZRnQomjWaAAAAABJRU5ErkJg\ +gg==\ "); } } @@ -1615,10 +1611,10 @@ ZRnQomjWaAAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 0 4 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAeUlE\ -QVQ4jcWTQQqAIBBFn9HV6i4uOmaL7hF1hVbZRkF0tMmCPnwYdeahH4RcM+AEH4AV+jNJw8EnMGkB\ -pf0MsiRDd4AAsdKBFhAywSTN8doIkBgGYLpKk0qvAVB+s0q9AHuk/zP4BLD7uiXIFWD0Re0TSd6A\ -of3uXhd1yT8mvMGiQgAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAeUlEQVQ4jcWTQQqAIBBFn9HV6i4u\ +OmaL7hF1hVbZRkF0tMmCPnwYdeahH4RcM+AEH4AV+jNJw8EnMGkBpf0MsiRDd4AAsdKBFhAywSTN\ +8doIkBgGYLpKk0qvAVB+s0q9AHuk/zP4BLD7uiXIFWD0Re0TSd6Aof3uXhd1yT8mvMGiQgAAAABJ\ +RU5ErkJggg==\ "); } } @@ -2087,19 +2083,19 @@ of3uXhd1yT8mvMGiQgAAAABJRU5ErkJggg==\ transform: translate(225, -10); child: texture { bounds: 1 1 20 25; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAABmJLR0QA/wD/AP+gvaeTAAAChklE\ -QVQ4jbXVTWsTQRgH8P88u2myu4ZEKVJqEj9Ar0V6kIQtPRWs1EpAPfegiFD8Av0C4sVbz1qt2IMe\ -fUmTXhTzKbKbEhRslrCzsyYz46Ev1jRNX4L/47Dz22eGmXkYBqReryc6nfaSaZoPtMYNpdRlADAM\ -7CrFvgHypeNkN6enp7v9c1n/QKVSuUuEF6lUys5mM2nLcpBImACAbrcLzjmCIOjEsQgB/bhYnH03\ -ENR6lba33WdEbHly8ppj2/ag4g8TRRzNph9KqdaKxepTxlbVP2C1WnmeTCaXC4WCQ2QMxQ6ilESj\ -0QjjOF4rldwVACAAqNW+LBkGnQsDACID+XzBMQxa3tr6vAgArF6vJzjvNHK5wsRpyzwpnIfwfX9H\ -Sn2dwrB9J5lMnrpnw2LbDsbGxtKMqUVizLyfyWTTF9b2k8lk04Zh3CPG1Mwo1R3EcRxojRnSGlnT\ -TIwMmqYJpeQVUgqMHTveFwtjDEREu91ub2Ss1+uBiLUJYN+jiI8Mcs6hNX0lrbvrQRB0RgWDoN1R\ -qveGxsd/vRVCRKNUyXmEOBaC83iTpqbKv7VWK81mM1RKnhuTUmJnxwsB+WR+fj4mACiVZl8pJdd9\ -3+eAPgen0Wz6XCn5qlicew3sPw57f8JDIcSW53nh2VANz2tEcSyqUrJHB6OHoOu6vVbrx0IU8U+N\ -RoNrPQzV8H2PCxFVLevSbdd1D88dHf2sXC7LVuvnkhDRR887Cd3DOOfblpVe6G8DA+/IxsaGMTFx\ -dTOVSs3l8wX771XS8DwviiJes+30rTP1lH7UslJzuVzBBgDfH44NBYG97sd554NtOze11iSEqFmW\ -cyJ2KngEfQ8Atn18z/57/gBelEdqMNUvpgAAAABJRU5ErkJggg==\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAChklEQVQ4jbXVTWsTQRgH8P88u2my\ +u4ZEKVJqEj9Ar0V6kIQtPRWs1EpAPfegiFD8Av0C4sVbz1qt2IMefUmTXhTzKbKbEhRslrCzsyYz\ +46Ev1jRNX4L/47Dz22eGmXkYBqReryc6nfaSaZoPtMYNpdRlADAM7CrFvgHypeNkN6enp7v9c1n/\ +QKVSuUuEF6lUys5mM2nLcpBImACAbrcLzjmCIOjEsQgB/bhYnH03ENR6lba33WdEbHly8ppj2/ag\ +4g8TRRzNph9KqdaKxepTxlbVP2C1WnmeTCaXC4WCQ2QMxQ6ilESj0QjjOF4rldwVACAAqNW+LBkG\ +nQsDACID+XzBMQxa3tr6vAgArF6vJzjvNHK5wsRpyzwpnIfwfX9HSn2dwrB9J5lMnrpnw2LbDsbG\ +xtKMqUVizLyfyWTTF9b2k8lk04Zh3CPG1Mwo1R3EcRxojRnSGlnTTIwMmqYJpeQVUgqMHTveFwtj\ +DEREu91ub2Ss1+uBiLUJYN+jiI8Mcs6hNX0lrbvrQRB0RgWDoN1RqveGxsd/vRVCRKNUyXmEOBaC\ +83iTpqbKv7VWK81mM1RKnhuTUmJnxwsB+WR+fj4mACiVZl8pJdd93+eAPgen0Wz6XCn5qlicew3s\ +Pw57f8JDIcSW53nh2VANz2tEcSyqUrJHB6OHoOu6vVbrx0IU8U+NRoNrPQzV8H2PCxFVLevSbdd1\ +D88dHf2sXC7LVuvnkhDRR887Cd3DOOfblpVe6G8DA+/IxsaGMTFxdTOVSs3l8wX771XS8DwviiJe\ +s+30rTP1lH7UslJzuVzBBgDfH44NBYG97sd554NtOze11iSEqFmWcyJ2KngEfQ8Atn18z/57/gBe\ +lEdqMNUvpgAAAABJRU5ErkJggg==\ "); } } @@ -2442,10 +2438,10 @@ cyJ2KngEfQ8Atn18z/57/gBelEdqMNUvpgAAAABJRU5ErkJggg==\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 14 29 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAeElE\ -QVQokb3QXwpAQBDH8e9uzscRXEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUp\ -YBXUB+gEC19cAmyAQaAmRJloOiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAe\ -i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAeElEQVQokb3QXwpAQBDH8e9uzscR\ +XEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUpYBXUB+gEC19cAmyAQaAmRJlo\ +OiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAei77LDp72K7jAZx9tAAAAAElF\ +TkSuQmCC\ "); } } @@ -2459,12 +2455,12 @@ i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ offset: 0.196078 0.196078 0.196078 0; child: texture { bounds: 54 28 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA8klE\ -QVQ4jbXSwSrEURTH8Y/YsZiUhfIGnkFJUrKSlIeQJ5AaUlPWdrKUJ9DMwhMQyZKVhSxEg7Lwz1i4\ -f925c/9jSk7dbuee8/3dczqHf7BxbOEcL3jFFeqY/A1exBM6FecZq1XwPD76wOUpMJvCY3gYAO7g\ -CCOpwMaA8CGGc+W3Msmnib+PoYjpErpNkhvhfTf4ewlcw0kscBPBx0l1S4k/hWvf4/2xZiRQYC3X\ -J6ZxF/Iu48B60kJOZEb3jmzHwVHc9xFZxnsUa2MiLW9O7yIVOAh3+faJlYoWLeBR70jjnyvh0mrY\ -xFkA3nCBnVzZf7Yvt2xyJ4TFGjYAAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA8klEQVQ4jbXSwSrEURTH8Y/YsZiU\ +hfIGnkFJUrKSlIeQJ5AaUlPWdrKUJ9DMwhMQyZKVhSxEg7Lwz1i4f925c/9jSk7dbuee8/3dczqH\ +f7BxbOEcL3jFFeqY/A1exBM6FecZq1XwPD76wOUpMJvCY3gYAO7gCCOpwMaA8CGGc+W3Msmnib+P\ +oYjpErpNkhvhfTf4ewlcw0kscBPBx0l1S4k/hWvf4/2xZiRQYC3XJ6ZxF/Iu48B60kJOZEb3jmzH\ +wVHc9xFZxnsUa2MiLW9O7yIVOAh3+faJlYoWLeBR70jjnyvh0mrYxFkA3nCBnVzZf7Yvt2xyJ4TF\ +GjYAAAAASUVORK5CYII=\ "); } } @@ -2520,11 +2516,11 @@ xFkA3nCBnVzZf7Yvt2xyJ4TFGjYAAAAASUVORK5CYII=\ offset: 0.196078 0.196078 0.196078 0; child: texture { bounds: 54 51 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAAz0lE\ -QVQ4jbXSTU4CQRAF4A/iFlbKOVywMoRDGLyAe70D4o3kBqz9uQAXICxwcM2wmAY6Y3djjL6kku7q\ -qtevfvgHDPCMd3wFe8MsvBVxhwp1xipMSsm7VsIYw5ZvlyIZZH4+oO3/xBV0Q8Ajeufqi9DHQ+z4\ -SPxSUlBrGnvE9hcEVVxCHPxT1HARLktcJ4JG6GQIljHBPEOwKCh4iS+5Md4EFW3/BpecerDCve+9\ -6CZKqEPsOiVrolmS3CpvcFsoC82GPeFVM95tOE8Psv8Ue0ISW4s5Tmr/AAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAz0lEQVQ4jbXSTU4CQRAF4A/iFlbK\ +OVywMoRDGLyAe70D4o3kBqz9uQAXICxwcM2wmAY6Y3djjL6kku7qqtevfvgHDPCMd3wFe8MsvBVx\ +hwp1xipMSsm7VsIYw5ZvlyIZZH4+oO3/xBV0Q8Ajeufqi9DHQ+z4SPxSUlBrGnvE9hcEVVxCHPxT\ +1HARLktcJ4JG6GQIljHBPEOwKCh4iS+5Md4EFW3/BpecerDCve+96CZKqEPsOiVrolmS3CpvcFso\ +C82GPeFVM95tOE8Psv8Ue0ISW4s5Tmr/AAAAAElFTkSuQmCC\ "); } } @@ -2575,10 +2571,10 @@ SPxSUlBrGnvE9hcEVVxCHPxT1HARLktcJ4JG6GQIljHBPEOwKCh4iS+5Md4EFW3/BpecerDCve+9\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 14 75 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAAeElE\ -QVQokb3QXwpAQBDH8e9uzscRXEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUp\ -YBXUB+gEC19cAmyAQaAmRJloOiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAe\ -i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAeElEQVQokb3QXwpAQBDH8e9uzscR\ +XEXyJFE8KFoXcxheVm1j0Cp+tQ87M5/2D/yc8g2qgC0GGKDzSIUpYBXUB+gEC19cAmyAQaAmRJlo\ +OiABJlGv5WnWD4dDq9i32tuOa41i+FiT719Gw/MT0rDj/Mu3sUAei77LDp72K7jAZx9tAAAAAElF\ +TkSuQmCC\ "); } } @@ -2592,12 +2588,12 @@ i77LDp72K7jAZx9tAAAAAElFTkSuQmCC\ offset: 0.196078 0.196078 0.196078 0; child: texture { bounds: 54 74 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA/klE\ -QVQ4jaXTzypFURTH8Q9CN2FyXYmRjqmpiZkykoF0n+J6Bxl4CQ/gASTlAZjwApIRMhDiSkd0Dc6W\ -3bHPPcmv1mCv/Vvf/W9t/qmBRG4aHYzhNORecIML5LF5qFTcxjFWcYQnDKKBFjI8hyBMxsX7mAzj\ -u8TuhrGMuTJgBnsl4FsCQHHsJYzGgC1MJIxVGsFCDFhLmMrAsmZjQJYwzNcAxmPAZ8KwXgPoxYCr\ -hCHDYh9ANwYcVJg6aFbMXfNz0y1cCucq6QG7uI1yOQ6Rf3fiq6JNN/1+vgZW8BFWfccJHlPb2lC0\ -b68i7hVN1FdN7OBc8Ym6OMM2puqK/6wvIccweWvwsr0AAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA/klEQVQ4jaXTzypFURTH8Q9CN2Fy\ +XYmRjqmpiZkykoF0n+J6Bxl4CQ/gASTlAZjwApIRMhDiSkd0Dc6W3bHPPcmv1mCv/Vvf/W9t/qmB\ +RG4aHYzhNORecIML5LF5qFTcxjFWcYQnDKKBFjI8hyBMxsX7mAzju8TuhrGMuTJgBnsl4FsCQHHs\ +JYzGgC1MJIxVGsFCDFhLmMrAsmZjQJYwzNcAxmPAZ8KwXgPoxYCrhCHDYh9ANwYcVJg6aFbMXfNz\ +0y1cCucq6QG7uI1yOQ6Rf3fiq6JNN/1+vgZW8BFWfccJHlPb2lC0b68i7hVN1FdN7OBc8Ym6OMM2\ +puqK/6wvIccweWvwsr0AAAAASUVORK5CYII=\ "); } } @@ -2659,9 +2655,9 @@ b68i7hVN1FdN7OBc8Ym6OMM2puqK/6wvIccweWvwsr0AAAAASUVORK5CYII=\ offset: 0.572549 0.584314 0.584314 0; child: texture { bounds: 14 98 14 14; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAASklE\ -QVQokWNgGJmAjYGBoZWBgeERFLcwMDCwEqOxlYGB4T8abiZG42MsGh+iK2LCovE/MWLYNC4iUgwD\ -sDJA/PQQihsZiAyc4QYASeYTs7b/ALUAAAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAASklEQVQokWNgGJmAjYGBoZWBgeER\ +FLcwMDCwEqOxlYGB4T8abiZG42MsGh+iK2LCovE/MWLYNC4iUgwDsDJA/PQQihsZiAyc4QYASeYT\ +s7b/ALUAAAAASUVORK5CYII=\ "); } } @@ -2675,14 +2671,13 @@ sDJA/PQQihsZiAyc4QYASeYTs7b/ALUAAAAASUVORK5CYII=\ offset: 0.196078 0.196078 0.196078 0; child: texture { bounds: 54 97 16 16; - texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABT0lE\ -QVQ4jZXTv0vXcRDH8cdXzQQTEZFCUVAU1xZ/LCIYrrVkoTi6NLTpX+DoGrTnkoMgqOjk8hXBhqbA\ -MALTSB1EpDQp/DF874Nvv4hfPTg4uHu+7n13n0+52+01/mAYu/hdov6atWIBS+i4D1iHSXzBRfgn\ -vMWDUnAnfiZgsa+h+iawEhX4fAuc+TuUBwNe4BgHSdEpevG/KL7ACX4E8xw20Y+xRGAzxL8XxVn+\ -FbqwAUeowkBS8Bft+FcUZ/mnMcZuDrPIxUzdmMMytvAL52hAE/owGq86REUOtZjAXixxCD1oxuMQ\ -2MdOXOFj7KQRU9ki67F4hwtkPoMaia1EYv0O8CrOYnRlCl9Xfwh9xXgInSYNjpHHG3wLbjB9wXzS\ -IY8RtMVodWhR+LHySd2HVOARpmNhpUY4w3s8pHC+1JrxEs+i65MQ3cO2qytsZcAlA5qEWoLbkBcA\ -AAAASUVORK5CYII=\ + texture: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABT0lEQVQ4jZXTv0vXcRDH8cdXzQQT\ +EZFCUVAU1xZ/LCIYrrVkoTi6NLTpX+DoGrTnkoMgqOjk8hXBhqbAMALTSB1EpDQp/DF874Nvv4hf\ +PTg4uHu+7n13n0+52+01/mAYu/hdov6atWIBS+i4D1iHSXzBRfgnvMWDUnAnfiZgsa+h+iawEhX4\ +fAuc+TuUBwNe4BgHSdEpevG/KL7ACX4E8xw20Y+xRGAzxL8XxVn+FbqwAUeowkBS8Bft+FcUZ/mn\ +McZuDrPIxUzdmMMytvAL52hAE/owGq86REUOtZjAXixxCD1oxuMQ2MdOXOFj7KQRU9ki67F4hwtk\ +PoMaia1EYv0O8CrOYnRlCl9Xfwh9xXgInSYNjpHHG3wLbjB9wXzSIY8RtMVodWhR+LHySd2HVOAR\ +pmNhpUY4w3s8pHC+1JrxEs+i65MQ3cO2qytsZcAlA5qEWoLbkBcAAAAASUVORK5CYII=\ "); } }