summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte.benjamin@googlemail.com>2021-09-17 03:13:38 +0000
committerBenjamin Otte <otte.benjamin@googlemail.com>2021-09-17 03:13:38 +0000
commit1d41dc716e99f3f2f0c0bfe6e9a9985f11909a59 (patch)
tree2040ff6dc2a011d4e5c3edfb35e94dbbe99cf08a
parent698b3542a125b56033dfacc244e536684f1938f6 (diff)
parent3914ecbd0fdceb60d83e30de5f08124b45b2cd5b (diff)
downloadgtk+-1d41dc716e99f3f2f0c0bfe6e9a9985f11909a59.tar.gz
Merge branch 'image-loading' into 'master'
Image loaders See merge request GNOME/gtk!3939
-rw-r--r--demos/gtk-demo/listbox.c11
-rw-r--r--demos/gtk-demo/listbox.ui1
-rw-r--r--examples/bp/bloatpad.c4
-rw-r--r--gdk/gdkcontentdeserializer.c109
-rw-r--r--gdk/gdkcontentserializer.c133
-rw-r--r--gdk/gdktexture.c318
-rw-r--r--gdk/gdktexture.h31
-rw-r--r--gdk/gdktextureprivate.h5
-rw-r--r--gdk/loaders/gdkjpeg.c329
-rw-r--r--gdk/loaders/gdkjpegprivate.h43
-rw-r--r--gdk/loaders/gdkpng.c568
-rw-r--r--gdk/loaders/gdkpngprivate.h43
-rw-r--r--gdk/loaders/gdktiff.c513
-rw-r--r--gdk/loaders/gdktiffprivate.h46
-rw-r--r--gdk/meson.build8
-rw-r--r--gsk/gskrendernodeparser.c30
-rw-r--r--gtk/gdkpixbufutils.c78
-rw-r--r--gtk/gdkpixbufutilsprivate.h6
-rw-r--r--gtk/gtkbuilder.c81
-rw-r--r--gtk/gtkcssimageurl.c21
-rw-r--r--gtk/gtkicontheme.c161
-rw-r--r--meson.build21
-rw-r--r--subprojects/libpng.wrap12
-rw-r--r--subprojects/libtiff.wrap12
-rw-r--r--testsuite/gdk/contentserializer.c71
-rw-r--r--testsuite/gdk/image-data/image.jpegbin0 -> 8397 bytes
-rw-r--r--testsuite/gdk/image-data/image.pngbin0 -> 1609 bytes
-rw-r--r--testsuite/gdk/image-data/image.tiffbin0 -> 205344 bytes
-rw-r--r--testsuite/gdk/image.c129
-rw-r--r--testsuite/gdk/memorytexture.c68
-rw-r--r--testsuite/gdk/meson.build26
-rw-r--r--testsuite/gsk/nodeparser/empty-texture.ref.node5
-rw-r--r--testsuite/gsk/nodeparser/widgetfactory.ref.node513
33 files changed, 2852 insertions, 544 deletions
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 <stdlib.h>
#include <string.h>
-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 @@
<property name="margin-start">8</property>
<property name="margin-end">8</property>
<property name="icon-name">image-missing</property>
+ <property name="icon-size">large</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
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 <gdk-pixbuf/gdk-pixbuf.h>
@@ -656,6 +658,56 @@ pixbuf_deserializer (GdkContentDeserializer *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,
gpointer deserializer)
@@ -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);
-
- g_free (name);
+ {
+ formats = g_slist_delete_link (formats, f);
+ formats = g_slist_prepend (formats, fmt);
- 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
@@ -606,6 +610,7 @@ gdk_content_serialize_finish (GAsyncResult *result,
/*** SERIALIZERS ***/
+
static void
pixbuf_serializer_finish (GObject *source,
GAsyncResult *res,
@@ -659,6 +664,77 @@ pixbuf_serializer (GdkContentSerializer *serializer)
}
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,
gpointer serializer)
@@ -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);
-
- g_free (name);
+ {
+ formats = g_slist_delete_link (formats, f);
+ formats = g_slist_prepend (formats, fmt);
- 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 <graphene.h>
+#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);
+ 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);
- status = cairo_surface_write_to_png (surface, filename);
+ return result;
+}
- if (status != CAIRO_STATUS_SUCCESS ||
- cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
- result = FALSE;
- else
- result = TRUE;
+/**
+ * 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);
- cairo_surface_destroy (surface);
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkjpegprivate.h"
+
+#include "gdkintl.h"
+#include "gdktexture.h"
+#include "gdkmemorytextureprivate.h"
+
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+
+/* {{{ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_JPEG_PRIVATE_H__
+#define __GDK_JPEG_PRIVATE_H__
+
+#include "gdkmemorytexture.h"
+#include <gio/gio.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkpngprivate.h"
+
+#include "gdkintl.h"
+#include "gdkmemorytextureprivate.h"
+#include "gdktexture.h"
+#include "gdktextureprivate.h"
+#include "gsk/ngl/fp16private.h"
+#include <png.h>
+#include <stdio.h>
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_PNG_PRIVATE_H__
+#define __GDK_PNG_PRIVATE_H__
+
+#include "gdktexture.h"
+#include <gio/gio.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdktiffprivate.h"
+
+#include "gdkintl.h"
+#include "gdkmemorytextureprivate.h"
+#include "gdktexture.h"
+#include "gdktextureprivate.h"
+
+#include <tiffio.h>
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_TIFF_PRIVATE_H__
+#define __GDK_TIFF_PRIVATE_H__
+
+#include "gdktexture.h"
+#include <gio/gio.h>
+
+#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_pixbuf_loader_write_bytes (loader, bytes, NULL))
- goto out;
+ 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_close (loader, NULL))
- goto out;
+ loader = gdk_pixbuf_loader_new ();
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (on_loader_size_prepared), &loader_data);
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- if (pixbuf != NULL)
- g_object_ref (pixbuf);
+ success = gdk_pixbuf_loader_write_bytes (loader, bytes, NULL);
+ /* close even when writing failed */
+ success &= gdk_pixbuf_loader_close (loader, NULL);
- out:
- gdk_pixbuf_loader_close (loader, NULL);
- g_object_unref (loader);
+ if (!success)
+ return NULL;
- if (!pixbuf)
- return NULL;
+ texture = gdk_texture_new_for_pixbuf (gdk_pixbuf_loader_get_pixbuf (loader));
+ g_object_unref (loader);
+ }
- 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,10 +2294,64 @@ 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;
GdkPixbuf *pixbuf = 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);
+ icon->texture = gdk_texture_new_from_resource (icon->filename);
+ }
+ else if (icon->filename)
+ {
+ if (icon->is_svg)
+ {
+ GdkPixbuf *source_pixbuf;
- if (source_pixbuf == NULL)
+ 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
{
- g_warning ("Failed to load icon %s: %s", icon->filename, load_error->message);
- g_clear_error (&load_error);
+ icon->texture = gdk_texture_new_from_filename (icon->filename, &load_error);
}
}
else
{
- GLoadableIcon *loadable;
GInputStream *stream;
+ GdkPixbuf *source_pixbuf;
+
+ g_assert (icon->loadable);
- loadable = icon_get_loadable (icon);
- stream = g_loadable_icon_load (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 <setjmp.h>
+ 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
@@ -31,6 +31,49 @@ compare_rgba_values (GValue *v1, GValue *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)
{
return G_VALUE_TYPE (v1) == G_TYPE_FILE &&
@@ -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;
@@ -183,6 +226,30 @@ test_content_color (void)
}
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)
{
GFile *file;
@@ -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
--- /dev/null
+++ b/testsuite/gdk/image-data/image.jpeg
Binary files differ
diff --git a/testsuite/gdk/image-data/image.png b/testsuite/gdk/image-data/image.png
new file mode 100644
index 0000000000..49ee0d459e
--- /dev/null
+++ b/testsuite/gdk/image-data/image.png
Binary files differ
diff --git a/testsuite/gdk/image-data/image.tiff b/testsuite/gdk/image-data/image.tiff
new file mode 100644
index 0000000000..ddd0d03609
--- /dev/null
+++ b/testsuite/gdk/image-data/image.tiff
Binary files 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 <gtk/gtk.h>
+#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=\
");
}
}