Compare commits

...

27 Commits

Author SHA1 Message Date
Matthias Clasen
91e19bc36d jpeg: Set errors 2021-09-13 21:08:39 -04:00
Matthias Clasen
30d21ef791 png: loader set error 2021-09-13 21:08:34 -04:00
Matthias Clasen
57b419ebb1 texture: Add error enum 2021-09-13 21:08:27 -04:00
Matthias Clasen
9d303723b0 rendernodeparser: Avoid gdk_texture_new_for_pixbuf
We can just use gdk_texture_new_from_bytes here now.

Update affected test output.
2021-09-13 21:08:21 -04:00
Benjamin Otte
08bd1129ad !fixup gdk_pixbuf_save_to_png_bytes() documentation 2021-09-14 02:46:39 +02:00
Benjamin Otte
0cb0fc8317 testsuite: Add png and tiff methods
We encode the texture to a PNG or TIFF and then decode it again.
2021-09-14 02:46:39 +02:00
Benjamin Otte
874a3837d9 texture: Add gdk_texture_save_to_tiff_bytes() 2021-09-14 02:46:39 +02:00
Benjamin Otte
31fc5a5a54 !fixup tiff save can't fail 2021-09-14 02:46:39 +02:00
Benjamin Otte
40d81f5959 !fixup can't fail 2021-09-14 02:46:39 +02:00
Benjamin Otte
4fad97d8ab Redo tiff load/save without streams
Less code.
2021-09-14 02:46:39 +02:00
Julian Sparber
f1a8c95ca4 stack: Dispose children before emitting items-changed
This makes sure that the `GListModel` returned by
`gtk_stack_get_pages()` actually has the items removed before
`items-changed` is emitted.

Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/4255
2021-09-14 02:46:39 +02:00
Matthias Clasen
65a1804f05 rendernodeparser: Use gdk_texture_save_to_png_bytes
Avoid cairo, and use our own api for saving png data.

Update affected test output.
2021-09-13 16:39:09 -04:00
Matthias Clasen
92873e62ed Add tests for the jpeg loader 2021-09-13 15:15:20 -04:00
Matthias Clasen
0f7c33f894 Load jpegs without gdk-pixbuf
Use our own loader for jpeg files.
2021-09-13 15:15:20 -04:00
Matthias Clasen
6a83d38e7b Add code to load jpegs
This lets us avoid gdk-pixbuf for loading
textures from the common image formats.

As a consequence, we are now linking against libjpeg.
2021-09-13 15:15:20 -04:00
Matthias Clasen
f89be80562 Add contentserializer tests for textures 2021-09-13 15:15:20 -04:00
Matthias Clasen
b66fa84161 Use our own loaders for content (de)serialization
Use our own loader to (de)serialiaze textures
to and from png and tiff.

We still fall back to gdk-pixbuf for handling all
the other image formats, and for pixbufs.
2021-09-13 15:15:20 -04:00
Matthias Clasen
ee7f2f9702 Add gdk_texture_save_to_tiff
This is a companion to gdk_texture_save_to_png, using
the tiff format, which will let us avoid lossy conversion
of HDR data, since we can store floating point data.
2021-09-13 15:15:20 -04:00
Matthias Clasen
900011808a Add tests for the tiff loader 2021-09-13 15:15:20 -04:00
Matthias Clasen
b8ee9873f3 Load tiffs without gdk-pixbuf
This will let us load floating point data, in
the future.
2021-09-13 15:15:20 -04:00
Matthias Clasen
0c9faea358 Add code to load and save tiff files
Add support for the tiff format, which is flexible
enough to handle all our memory texture formats
without loss.

As a consequence, we are now linking against libtiff.
2021-09-13 15:15:20 -04:00
Matthias Clasen
2568611a8c Add gdk_texture_save_to_png_bytes
Just expose what we already have available
internally, so e.g. tests can use it without
static linking.
2021-09-13 15:15:20 -04:00
Matthias Clasen
4c64111b5b Add tests for the png loader 2021-09-13 15:15:20 -04:00
Matthias Clasen
4f18a34dfa Save pngs without cairo
Use our own loader for pngs, which will allow
us to save e.g. 16-bit data in the future.
2021-09-13 15:15:20 -04:00
Matthias Clasen
bc0d315e6c Load pngs without gdk-pixbuf
Use our own loader for pngs, which will allow
us to get e.g. 16-bit data in the future.
2021-09-13 15:15:20 -04:00
Matthias Clasen
13d72eed2d Add code to load and save pngs
Using libpng instead of the lowest-common-denominator
gdk-pixbuf loader. This will allow us to load >8bit data,
and apply gamma and color correction in the future.
For now, this still just provides RGBA8 data.

As a consequence, we are now linking against libpng.
2021-09-13 15:15:20 -04:00
Matthias Clasen
7bafbeffcf Add gdk_texture_new_from_bytes
Add this new api, and make gdk_texture_new_from_file
a wrapper around it.
2021-09-13 14:03:15 -04:00
25 changed files with 1992 additions and 350 deletions

View File

@@ -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>
@@ -655,6 +657,88 @@ pixbuf_deserializer (GdkContentDeserializer *deserializer)
deserializer);
}
static void
texture_deserializer_finish (GObject *source,
GAsyncResult *res,
gpointer data)
{
GdkContentDeserializer *deserializer = GDK_CONTENT_DESERIALIZER (source);
GdkTexture *texture;
GValue *value;
GError *error = NULL;
texture = g_task_propagate_pointer (G_TASK (res), &error);
if (texture == NULL)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
value = gdk_content_deserializer_get_value (deserializer);
g_value_take_object (value, texture);
gdk_content_deserializer_return_success (deserializer);
}
static GBytes *
read_all_data (GInputStream *source,
GError **error)
{
GOutputStream *output;
gssize size;
GBytes *bytes;
output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
size = g_output_stream_splice (output, source, 0, NULL, error);
if (size == -1)
{
g_object_unref (output);
return NULL;
}
g_output_stream_close (output, NULL, NULL);
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output));
g_object_unref (output);
return bytes;
}
static void
deserialize_texture_in_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GdkContentDeserializer *deserializer = source_object;
GBytes *bytes;
GError *error = NULL;
GdkTexture *texture = NULL;
bytes = read_all_data (gdk_content_deserializer_get_input_stream (deserializer), &error);
if (bytes)
{
texture = gdk_texture_new_from_bytes (bytes, &error);
g_bytes_unref (bytes);
}
if (texture)
g_task_return_pointer (task, texture, g_object_unref);
else
g_task_return_error (task, error);
}
static void
texture_deserializer (GdkContentDeserializer *deserializer)
{
GTask *task;
task = g_task_new (deserializer,
gdk_content_deserializer_get_cancellable (deserializer),
texture_deserializer_finish,
NULL);
g_task_run_in_thread (task, deserialize_texture_in_thread);
g_object_unref (task);
}
static void
string_deserializer_finish (GObject *source,
GAsyncResult *result,
@@ -863,48 +947,65 @@ 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);
formats = gdk_pixbuf_get_formats ();
/* Make sure png comes first */
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char *name;
char *name;
name = gdk_pixbuf_format_get_name (fmt);
if (g_str_equal (name, "png"))
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
g_free (name);
break;
}
g_free (name);
break;
}
g_free (name);
}
}
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char **mimes, **m;
char *name;
name = gdk_pixbuf_format_get_name (fmt);
mimes = gdk_pixbuf_format_get_mime_types (fmt);
for (m = mimes; *m; m++)
{
gdk_content_register_deserializer (*m,
GDK_TYPE_TEXTURE,
pixbuf_deserializer,
NULL,
NULL);
{
/* Turning pngs and tiffs into textures is handled above */
if (!g_str_equal (name, "png") &&
!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);

View File

@@ -26,6 +26,9 @@
#include "filetransferportalprivate.h"
#include "gdktextureprivate.h"
#include "gdkrgba.h"
#include "loaders/gdkpngprivate.h"
#include "loaders/gdktiffprivate.h"
#include "gdkmemorytextureprivate.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
@@ -606,6 +609,7 @@ gdk_content_serialize_finish (GAsyncResult *result,
/*** SERIALIZERS ***/
static void
pixbuf_serializer_finish (GObject *source,
GAsyncResult *res,
@@ -658,6 +662,82 @@ pixbuf_serializer (GdkContentSerializer *serializer)
g_object_unref (pixbuf);
}
static void
texture_serializer_finish (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GdkContentSerializer *serializer = GDK_CONTENT_SERIALIZER (source);
GError *error = NULL;
if (!g_task_propagate_boolean (G_TASK (res), &error))
gdk_content_serializer_return_error (serializer, error);
else
gdk_content_serializer_return_success (serializer);
}
static void
serialize_texture_in_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GdkContentSerializer *serializer = source_object;
const GValue *value;
GdkTexture *texture;
GBytes *bytes = NULL;
GError *error = NULL;
gboolean result = FALSE;
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
g_assert_not_reached ();
if (bytes)
{
GInputStream *input = g_memory_input_stream_new_from_bytes (bytes);
gssize spliced;
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;
}
else
g_set_error_literal (&error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Saving png failed");
if (result)
g_task_return_boolean (task, result);
else
g_task_return_error (task, error);
}
static void
texture_serializer (GdkContentSerializer *serializer)
{
GTask *task;
task = g_task_new (serializer,
gdk_content_serializer_get_cancellable (serializer),
texture_serializer_finish,
NULL);
g_task_run_in_thread (task, serialize_texture_in_thread);
g_object_unref (task);
}
static void
string_serializer_finish (GObject *source,
GAsyncResult *result,
@@ -877,51 +957,66 @@ 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);
formats = gdk_pixbuf_get_formats ();
/* Make sure png comes first */
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char *name;
char *name;
name = gdk_pixbuf_format_get_name (fmt);
if (g_str_equal (name, "png"))
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
g_free (name);
break;
}
g_free (name);
break;
}
g_free (name);
}
}
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char **mimes, **m;
char *name;
if (!gdk_pixbuf_format_is_writable (fmt))
continue;
continue;
name = gdk_pixbuf_format_get_name (fmt);
mimes = gdk_pixbuf_format_get_mime_types (fmt);
for (m = mimes; *m; m++)
{
gdk_content_register_serializer (GDK_TYPE_TEXTURE,
*m,
pixbuf_serializer,
gdk_pixbuf_format_get_name (fmt),
g_free);
{
/* Turning textures into pngs or tiffs is handled above */
if (!g_str_equal (name, "png") &&
!g_str_equal (name, "tiff"))
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);

View File

@@ -46,6 +46,11 @@
#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
@@ -383,28 +388,92 @@ GdkTexture *
gdk_texture_new_from_file (GFile *file,
GError **error)
{
GBytes *bytes;
GdkTexture *texture;
GdkPixbuf *pixbuf;
GInputStream *stream;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
if (stream == NULL)
bytes = g_file_load_bytes (file, NULL, NULL, error);
if (bytes == NULL)
return NULL;
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
g_object_unref (stream);
if (pixbuf == NULL)
return NULL;
texture = gdk_texture_new_from_bytes (bytes, error);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
g_bytes_unref (bytes);
return texture;
}
/**
* gdk_texture_new_from_bytes:
* @bytes: a `GBytes` containing the data to load
* @error: Return location for an error
*
* Creates a new texture by loading an image from memory,
*
* The file format is detected automatically. The supported formats
* are PNG and JPEG, though more formats might be available.
*
* If %NULL is returned, then @error will be set.
*
* Return value: A newly-created `GdkTexture`
*
* Since: 4.6
*/
GdkTexture *
gdk_texture_new_from_bytes (GBytes *bytes,
GError **error)
{
const char *data;
gsize size;
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
data = g_bytes_get_data (bytes, &size);
if (size > strlen (PNG_SIGNATURE) &&
memcmp (data, PNG_SIGNATURE, strlen (PNG_SIGNATURE)) == 0)
{
return gdk_load_png (bytes, error);
}
else if ((size > strlen (TIFF_SIGNATURE1) &&
memcmp (data, TIFF_SIGNATURE1, strlen (TIFF_SIGNATURE1)) == 0) ||
(size > strlen (TIFF_SIGNATURE2) &&
memcmp (data, TIFF_SIGNATURE2, strlen (TIFF_SIGNATURE2)) == 0))
{
return gdk_load_tiff (bytes, error);
}
else if (size > strlen (JPEG_SIGNATURE) &&
memcmp (data, JPEG_SIGNATURE, strlen (JPEG_SIGNATURE)) == 0)
{
return gdk_load_jpeg (bytes, error);
}
else
{
GInputStream *stream;
GdkPixbuf *pixbuf;
stream = g_memory_input_stream_new_from_bytes (bytes);
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
g_object_unref (stream);
if (pixbuf)
{
GdkTexture *texture;
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
return texture;
}
}
return NULL;
}
/**
* gdk_texture_get_width: (attributes org.gtk.Method.get_property=width)
* @texture: a `GdkTexture`
@@ -602,7 +671,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 gdk_texture_save_to_png_bytes() or look into the
* gdk-pixbuf library.
*
* Returns: %TRUE if saving succeeded, %FALSE on failure.
*/
@@ -610,30 +680,109 @@ gboolean
gdk_texture_save_to_png (GdkTexture *texture,
const char *filename)
{
cairo_surface_t *surface;
cairo_status_t status;
GBytes *bytes;
gboolean result;
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
gdk_texture_download (texture,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface));
cairo_surface_mark_dirty (surface);
status = cairo_surface_write_to_png (surface, filename);
if (status != CAIRO_STATUS_SUCCESS ||
cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
result = FALSE;
else
result = TRUE;
cairo_surface_destroy (surface);
bytes = gdk_save_png (texture);
result = g_file_set_contents (filename,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
NULL);
g_bytes_unref (bytes);
return result;
}
/**
* gdk_texture_save_to_png_bytes:
* @texture: a `GdkTexture`
*
* Store the given @texture in memory as a PNG file.
* Use 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 gdk_texture_save_to_tiff_bytes()
* instead.
*
* Returns: a newly allocated `GBytes` containing PNG data
*
* Since: 4.6
*/
GBytes *
gdk_texture_save_to_png_bytes (GdkTexture *texture)
{
g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
return gdk_save_png (texture);
}
/**
* gdk_texture_save_to_tiff:
* @texture: a `GdkTexture`
* @filename: (type filename): the filename to store to
*
* Store the given @texture to the @filename as a TIFF file.
*
* GTK will attempt to store data without loss.
* Returns: %TRUE if saving succeeded, %FALSE on failure.
*
* Since: 4.6
*/
gboolean
gdk_texture_save_to_tiff (GdkTexture *texture,
const char *filename)
{
GBytes *bytes;
gboolean result;
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
bytes = gdk_save_tiff (texture);
result = g_file_set_contents (filename,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
NULL);
g_bytes_unref (bytes);
return result;
}
/**
* gdk_texture_save_to_tiff_bytes:
* @texture: a `GdkTexture`
*
* Store the given @texture in memory as a TIFF file.
* Use 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 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);
}

View File

@@ -38,6 +38,25 @@ 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_INSUFFICIENT_MEMORY: Not enough memory to handle this image
* @GDK_TEXTURE_ERROR_CORRUPT_IMAGE: The image data appears corrupted
* @GDK_TEXTURE_ERROR_UNSUPPORTED: The image format is not supported
*
* Possible errors that can be returned by `GdkTexture` constructors.
*/
typedef enum
{
GDK_TEXTURE_ERROR_INSUFFICIENT_MEMORY,
GDK_TEXTURE_ERROR_CORRUPT_IMAGE,
GDK_TEXTURE_ERROR_UNSUPPORTED,
} GdkTextureError;
GDK_AVAILABLE_IN_ALL
GType gdk_texture_get_type (void) G_GNUC_CONST;
@@ -49,6 +68,9 @@ GdkTexture * gdk_texture_new_from_resource (const char
GDK_AVAILABLE_IN_ALL
GdkTexture * gdk_texture_new_from_file (GFile *file,
GError **error);
GDK_AVAILABLE_IN_4_6
GdkTexture * gdk_texture_new_from_bytes (GBytes *bytes,
GError **error);
GDK_AVAILABLE_IN_ALL
int gdk_texture_get_width (GdkTexture *texture) G_GNUC_PURE;
@@ -66,6 +88,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

146
gdk/loaders/gdkjpeg.c Normal file
View File

@@ -0,0 +1,146 @@
/* 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 "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,
cinfo->err->msg_code == JERR_OUT_OF_MEMORY
? GDK_TEXTURE_ERROR_INSUFFICIENT_MEMORY
: 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 */
}
/* }}} */
/* {{{ Public API */
GdkTexture *
gdk_load_jpeg (GBytes *input_bytes,
GError **error)
{
struct jpeg_decompress_struct info;
struct error_handler_data jerr;
struct jpeg_error_mgr err;
int width, height;
int size;
unsigned char *data;
unsigned char *row[1];
GBytes *bytes;
GdkTexture *texture;
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;
}
info.err = jpeg_std_error (&err);
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;
size = width * height * 3;
data = g_try_malloc_n (width * 3, height);
if (!data)
{
g_set_error_literal (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_INSUFFICIENT_MEMORY,
"Not enough memory to load jpeg");
jpeg_destroy_decompress (&info);
return NULL;
}
while (info.output_scanline < info.output_height)
{
row[0] = (unsigned char *)(&data[3 *info.output_width * info.output_scanline]);
jpeg_read_scanlines (&info, row, 1);
}
jpeg_finish_decompress (&info);
jpeg_destroy_decompress (&info);
bytes = g_bytes_new_take (data, size);
texture = gdk_memory_texture_new (width, height,
GDK_MEMORY_R8G8B8,
bytes, width * 3);
g_bytes_unref (bytes);
return texture;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -0,0 +1,29 @@
/* 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);
#endif

242
gdk/loaders/gdkpng.c Normal file
View File

@@ -0,0 +1,242 @@
/* 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 "gdktexture.h"
#include "gdktextureprivate.h"
#include "gdkmemorytextureprivate.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.
*/
/* {{{ 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;
}
}
static void
convert_float_to_16bit_inplace (float *src,
int width,
int height)
{
gsize x, y;
guint16 *dest = (guint16 *)src;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
dest[4 * x ] = (guint16) CLAMP(65536.f * src[x * 4 ], 0.f, 65535.f);
dest[4 * x + 1] = (guint16) CLAMP(65536.f * src[x * 4 + 1], 0.f, 65535.f);
dest[4 * x + 2] = (guint16) CLAMP(65536.f * src[x * 4 + 2], 0.f, 65535.f);
dest[4 * x + 3] = (guint16) CLAMP(65536.f * src[x * 4 + 3], 0.f, 65535.f);
}
dest += width * 4;
src += width * 4;
}
}
/* }}} */
/* {{{ Public API */
GdkTexture *
gdk_load_png (GBytes *bytes,
GError **error)
{
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
gsize size;
gsize stride;
guchar *buffer;
GdkTexture *texture;
GBytes *out_bytes;
png_image_begin_read_from_memory (&image,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
if (PNG_IMAGE_FAILED (image))
{
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED,
"Failed to parse png image (%s)", image.message);
png_image_free (&image);
return NULL;
}
image.format = PNG_FORMAT_RGBA;
stride = PNG_IMAGE_ROW_STRIDE (image);
size = PNG_IMAGE_BUFFER_SIZE (image, stride);
buffer = g_try_malloc (size);
if (!buffer)
{
g_set_error_literal (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_INSUFFICIENT_MEMORY,
"Not enough memory to load png");
png_image_free (&image);
return NULL;
}
png_image_finish_read (&image, NULL, buffer, stride, NULL);
if (PNG_IMAGE_FAILED (image))
{
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED,
"Failed to parse png image (%s)", image.message);
png_image_free (&image);
return NULL;
}
if (image.format & PNG_FORMAT_FLAG_LINEAR)
stride *= 2;
out_bytes = g_bytes_new_take (buffer, size);
texture = gdk_memory_texture_new (image.width, image.height,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
out_bytes, stride);
g_bytes_unref (out_bytes);
png_image_free (&image);
return texture;
}
GBytes *
gdk_save_png (GdkTexture *texture)
{
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
int stride;
const guchar *data;
guchar *new_data = NULL;
png_alloc_size_t size;
gpointer buffer;
GdkTexture *memory_texture;
GdkMemoryFormat format;
gboolean result G_GNUC_UNUSED;
image.width = gdk_texture_get_width (texture);
image.height = gdk_texture_get_height (texture);
memory_texture = gdk_texture_download_texture (texture);
format = gdk_memory_texture_get_format (GDK_MEMORY_TEXTURE (memory_texture));
switch (format)
{
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
data = gdk_memory_texture_get_data (GDK_MEMORY_TEXTURE (memory_texture));
stride = gdk_memory_texture_get_stride (GDK_MEMORY_TEXTURE (memory_texture));
image.format = PNG_FORMAT_RGBA;
break;
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
case GDK_MEMORY_A8R8G8B8_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 = image.width * 4;
new_data = g_malloc (stride * image.height);
gdk_texture_download (memory_texture, new_data, stride);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
flip_02 (new_data, image.width, image.height, stride);
#endif
data = new_data;
image.format = PNG_FORMAT_RGBA;
break;
case GDK_MEMORY_R16G16B16:
data = gdk_memory_texture_get_data (GDK_MEMORY_TEXTURE (memory_texture));
stride = gdk_memory_texture_get_stride (GDK_MEMORY_TEXTURE (memory_texture));
image.format = PNG_FORMAT_LINEAR_RGB;
break;
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
data = gdk_memory_texture_get_data (GDK_MEMORY_TEXTURE (memory_texture));
stride = gdk_memory_texture_get_stride (GDK_MEMORY_TEXTURE (memory_texture));
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
break;
case GDK_MEMORY_R16G16B16_FLOAT:
case GDK_MEMORY_R32G32B32_FLOAT:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
/* This isn't very efficient */
new_data = g_malloc (image.width * image.height * 16);
gdk_texture_download_float (memory_texture, (float *)new_data, image.width * 16);
convert_float_to_16bit_inplace ((float *)new_data, image.width, image.height);
data = new_data;
stride = image.width * 8;
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
break;
case GDK_MEMORY_N_FORMATS:
default:
g_assert_not_reached ();
}
if (image.format & PNG_FORMAT_FLAG_LINEAR)
stride /= 2;
png_image_write_get_memory_size (image, size, FALSE, data, stride, NULL);
buffer = g_malloc (size);
result = png_image_write_to_memory (&image, buffer, &size, FALSE, data, stride, NULL);
g_assert (result);
g_object_unref (memory_texture);
png_image_free (&image);
g_free (new_data);
return g_bytes_new_take (buffer, size);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -0,0 +1,31 @@
/* 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);
#endif

502
gdk/loaders/gdktiff.c Normal file
View File

@@ -0,0 +1,502 @@
/* 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 "gdktexture.h"
#include "gdktextureprivate.h"
#include "gdkmemorytextureprivate.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_read (thandle_t handle,
tdata_t buffer,
tsize_t size)
{
TiffIO *io = (TiffIO *) handle;
gsize read;
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)
{
errno = EINVAL;
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->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:
errno = EINVAL;
return -1;
case SEEK_SET:
break;
case SEEK_CUR:
offset += io->position;
break;
case SEEK_END:
offset += io->size;
break;
}
if (offset < 0)
{
errno = EINVAL;
return -1;
}
if (offset > io->size)
{
/* Linux apparently can do that */
errno = EINVAL;
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_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,
G_IO_ERROR, G_IO_ERROR_FAILED,
"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_new (guchar, height * stride);
line = data;
for (int y = 0; y < height; y++)
{
if (TIFFReadScanline (tif, line, y, 0) == -1)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"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: */

View File

@@ -0,0 +1,32 @@
/* 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);
#endif

View File

@@ -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([
@@ -256,8 +259,8 @@ endif
libgdk = static_library('gdk',
sources: [gdk_sources, gdk_backends_gen_headers, gdkconfig],
dependencies: gdk_deps + [libgtk_css_dep],
link_with: [libgtk_css, ],
dependencies: gdk_deps + [libgtk_css_dep, png_dep, tiff_dep, jpeg_dep],
link_with: [libgtk_css],
include_directories: [confinc, gdkx11_inc, wlinc],
c_args: libgdk_c_args + common_cflags,
link_whole: gdk_backends,

View File

@@ -94,15 +94,8 @@ parse_texture (GtkCssParser *parser,
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
@@ -2690,26 +2683,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;

View File

@@ -690,13 +690,14 @@ gtk_stack_dispose (GObject *obj)
GtkStack *stack = GTK_STACK (obj);
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
GtkWidget *child;
if (priv->pages)
g_list_model_items_changed (G_LIST_MODEL (priv->pages), 0, g_list_length (priv->children), 0);
guint n_pages = g_list_length (priv->children);
while ((child = gtk_widget_get_first_child (GTK_WIDGET (stack))))
stack_remove (stack, child, TRUE);
if (priv->pages)
g_list_model_items_changed (G_LIST_MODEL (priv->pages), 0, n_pages, 0);
G_OBJECT_CLASS (gtk_stack_parent_class)->dispose (obj);
}

View File

@@ -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,

12
subprojects/libpng.wrap Normal file
View File

@@ -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

12
subprojects/libtiff.wrap Normal file
View File

@@ -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

View File

@@ -30,6 +30,49 @@ compare_rgba_values (GValue *v1, GValue *v2)
(GdkRGBA *)g_value_get_boxed (v2));
}
static gboolean
textures_equal (GdkTexture *t1, GdkTexture *t2)
{
guchar *d1, *d2;
int width, height;
gboolean ret;
width = gdk_texture_get_width (t1);
height = gdk_texture_get_height (t1);
if (width != gdk_texture_get_width (t2))
return FALSE;
if (height != gdk_texture_get_height (t2))
return FALSE;
d1 = g_malloc (width * height * 4);
d2 = g_malloc (width * height * 4);
gdk_texture_download (t1, d1, width * 4);
gdk_texture_download (t2, d2, width * 4);
ret = memcmp (d1, d2, width * height * 4) == 0;
if (!ret)
{
gdk_texture_save_to_png (t1, "texture1.png");
gdk_texture_save_to_png (t2, "texture2.png");
}
g_free (d1);
g_free (d2);
return ret;
}
static gboolean
compare_texture_values (GValue *v1, GValue *v2)
{
return G_VALUE_TYPE (v1) == GDK_TYPE_TEXTURE &&
G_VALUE_TYPE (v2) == GDK_TYPE_TEXTURE &&
textures_equal ((GdkTexture *)g_value_get_object (v1),
(GdkTexture *)g_value_get_object (v2));
}
static gboolean
compare_file_values (GValue *v1, GValue *v2)
{
@@ -125,7 +168,7 @@ test_content_roundtrip (const GValue *value,
TestData data = { 0, };
data.ostream = g_memory_output_stream_new_resizable ();
data.mime_type = g_strdup (mime_type);
data.mime_type = mime_type;
g_value_init (&data.value, G_VALUE_TYPE (value));
g_value_copy (value, &data.value);
data.compare = compare;
@@ -182,6 +225,30 @@ test_content_color (void)
g_value_unset (&value);
}
static void
test_content_texture (gconstpointer data)
{
const char *mimetype = data;
GValue value = G_VALUE_INIT;
char *path;
GFile *file;
GdkTexture *texture;
GError *error = NULL;
path = g_test_build_filename (G_TEST_DIST, "image-data", "image.png", NULL);
file = g_file_new_for_path (path);
texture = gdk_texture_new_from_file (file, &error);
g_assert_no_error (error);
g_object_unref (file);
g_free (path);
g_value_init (&value, GDK_TYPE_TEXTURE);
g_value_set_object (&value, texture);
test_content_roundtrip (&value, mimetype, compare_texture_values);
g_value_unset (&value);
g_object_unref (texture);
}
static void
test_content_file (void)
{
@@ -406,6 +473,8 @@ main (int argc, char *argv[])
g_test_add_func ("/content/text_plain_utf8", test_content_text_plain_utf8);
g_test_add_func ("/content/text_plain", test_content_text_plain);
g_test_add_func ("/content/color", test_content_color);
g_test_add_data_func ("/content/texture/png", "image/png", test_content_texture);
g_test_add_data_func ("/content/texture/tiff", "image/tiff", test_content_texture);
g_test_add_func ("/content/file", test_content_file);
g_test_add_func ("/content/files", test_content_files);
g_test_add_func ("/content/custom", test_custom_format);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

129
testsuite/gdk/image.c Normal file
View File

@@ -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 ();
}

View File

@@ -12,6 +12,8 @@ typedef enum {
TEXTURE_METHOD_LOCAL,
TEXTURE_METHOD_GL,
TEXTURE_METHOD_GL_RELEASED,
TEXTURE_METHOD_PNG,
TEXTURE_METHOD_TIFF,
N_TEXTURE_METHODS
} TextureMethod;
@@ -448,6 +450,28 @@ 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_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 N_TEXTURE_METHODS:
default:
g_assert_not_reached ();
@@ -584,7 +608,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", "tiff" };
char *test_name = g_strdup_printf ("%s/%s/%s",
name,
g_enum_get_value (enum_class, format)->value_nick,

View File

@@ -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

View File

@@ -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+9AAAAAXNSR0IArs4c6QAAAClJREFUGJVj\
/M9w5j8DGmBkMGFEF2NCF8AFBlAhhqMZGBgYsHlwKHgGAM+OBd3t1NcVAAAAAElFTkSuQmCC\
");
}

View File

@@ -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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\
YRiGYRiGIfw/xkuszVpN0tYAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAB9JREFUOI1j\
YBjygBGN/59UfUzUc8tAgdEwGA0DqgAAwf0DCjxFCiwAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAH1JREFUOI3t\
0TEKAjEQRuEPr6BCCgvx/newsHdRlC0sxFMoNmszskNYMQfYB4HJ5J8XQpiZYsAB69RbYh9nf7lE\
8BySFbro9S2CDW5Jcor6jm2LoJYMeGA3FVz8ELzxTPtXrCYKrsY396kuLYJjNVCSpGsRfIP5tmL8\
iZmKD5NiJVtlqH49AAAAAElFTkSuQmCC\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAMpJREFUOI3N\
0t1KQlEQBeCvMPMqUANRH6ZHCPEphBIfUk2M0EDQhN7BC4kIoi4ctSP7nG5dMAx7fhZr9gznhgoe\
McYnNniK2FXUjPCTam5jHsmUvaD5551BGa+RWOIet2igg3XknvMIBhF8QzWhro73E0UZ7Jk7ieb9\
zKeWwUcEGwmCvD8BpfDf4b8SBBeJ2AGX4Rfh74qKi/AQslaoJfI1TOXsH64xi4I1urgJ6zqucVqk\
ouV4C3mH1PpvlDL6mGBrd8pD9ELlGeIXovZHoNZrT68AAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAH1JREFUOI3t\
0TEKAjEQRuEPr6BCCgvx/newsHdRlC0sxFMoNmszskNYMQfYB4HJ5J8XQpiZYsAB69RbYh9nf7lE\
8BySFbro9S2CDW5Jcor6jm2LoJYMeGA3FVz8ELzxTPtXrCYKrsY396kuLYJjNVCSpGsRfIP5tmL8\
iZmKD5NiJVtlqH49AAAAAElFTkSuQmCC\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAABxJREFUOI1j\
YBgFwwAwovH/k6qPiXpuGQVDGAAAQ18BBL4/glcAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAChJREFUOI1j\
YBj24D8U4wRMlNow8AYwovHx+hebPopdQAiMhFgYBgAA8qYFDMtT4XcAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAABxJREFUOI1j\
YBgFwwAwovH/k6qPiXpuGQVDGAAAQ18BBL4/glcAAAAASUVORK5CYII=\
");
}
}
@@ -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/9hAAAAAXNSR0IArs4c6QAAAChJREFUOI1j\
YBj24D8U4wRMlNow8AYwovHx+hebPopdQAiMhFgYBgAA8qYFDMtT4XcAAAAASUVORK5CYII=\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHhJREFUKJG9\
0F8KQEAQx/Hvbs7HEVxF8iRRPChaF3MYXlZtY9AqfrUPOzOf9g/8nPINqoAtBhig80iFKWAV1Afo\
BAtfXAJsgEGgJkSZaDogASZRr+Vp1g+HQ6vYt9rbjmuNYvhYk+9fRsPzE9Kw4/zLt7FAHou+yw6e\
9iu4wGcfbQAAAABJRU5ErkJggg==\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADlJREFUKJFj\
YBgFRAFvBgaGJwwMDP9x4McMDAxe2DQ+xqMJhh/BFDNRw6leBGx9xMDA4EkNi4Y9AAAbuBpo5IRY\
AgAAAABJRU5ErkJggg==\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHhJREFUKJG9\
0F8KQEAQx/Hvbs7HEVxF8iRRPChaF3MYXlZtY9AqfrUPOzOf9g/8nPINqoAtBhig80iFKWAV1Afo\
BAtfXAJsgEGgJkSZaDogASZRr+Vp1g+HQ6vYt9rbjmuNYvhYk+9fRsPzE9Kw4/zLt7FAHou+yw6e\
9iu4wGcfbQAAAABJRU5ErkJggg==\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADlJREFUKJFj\
YBgFRAFvBgaGJwwMDP9x4McMDAxe2DQ+xqMJhh/BFDNRw6leBGx9xMDA4EkNi4Y9AAAbuBpo5IRY\
AgAAAABJRU5ErkJggg==\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADlJREFUKJFj\
YBgFRAFvBgaGJwwMDP9x4McMDAxe2DQ+xqMJhh/BFDNRw6leBGx9xMDA4EkNi4Y9AAAbuBpo5IRY\
AgAAAABJRU5ErkJggg==\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAEpJREFUKJFj\
YBiZgI2BgaGVgYHhERS3MDAwsBKjsZWBgeE/Gm4mRuNjLBofoitiwqLxPzFi2DQuIlIMA7AyQPz0\
EIobGYgMnOEGAEnmE7O2/wC1AAAAAElFTkSuQmCC\
");
}
}
@@ -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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADlJREFUKJFj\
YBgFRAFvBgaGJwwMDP9x4McMDAxe2DQ+xqMJhh/BFDNRw6leBGx9xMDA4EkNi4Y9AAAbuBpo5IRY\
AgAAAABJRU5ErkJggg==\
");
}
}
@@ -1292,13 +1290,13 @@ 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/9hAAAAAXNSR0IArs4c6QAAARNJREFUOI21\
0j8vQ1EYx/FPW0qoRJqIPwOjxSRhkQiJ0Wa0GUzegdV7MrCYDEZJSQ0WBrVQbVpEa+hzmxtprkbi\
SW5ycp7z/Z7fc3P4hyrhCGd4xBXmh4W38IDuj+9wGHgH7QHwDRZ/g6dRS0H32MfMsNGPU/Ad5jLO\
jqGIfHrzEp0Q7GXAhZCX9X62kWgsoBXr8wxBNyQTGEUjEdQj2lcGLGJPxbqdbMAtntDARoagHILZ\
SNIXnMY4DRykbklXESshyYuRC9GsYjcaHaxFohbGsYRNTAbzjgo6udQN6zjBa0jaeEYTL/gIYR0X\
0esnoPfuK1hNCfLI4S0EzYBrCZROkFQJ21iOuZsxThXX+BzA/L2+AWVURxdJT4pgAAAAAElFTkSu\
QmCC\
");
}
}
@@ -1315,13 +1313,13 @@ 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/9hAAAAAXNSR0IArs4c6QAAARNJREFUOI21\
0j8vQ1EYx/FPW0qoRJqIPwOjxSRhkQiJ0Wa0GUzegdV7MrCYDEZJSQ0WBrVQbVpEa+hzmxtprkbi\
SW5ycp7z/Z7fc3P4hyrhCGd4xBXmh4W38IDuj+9wGHgH7QHwDRZ/g6dRS0H32MfMsNGPU/Ad5jLO\
jqGIfHrzEp0Q7GXAhZCX9X62kWgsoBXr8wxBNyQTGEUjEdQj2lcGLGJPxbqdbMAtntDARoagHILZ\
SNIXnMY4DRykbklXESshyYuRC9GsYjcaHaxFohbGsYRNTAbzjgo6udQN6zjBa0jaeEYTL/gIYR0X\
0esnoPfuK1hNCfLI4S0EzYBrCZROkFQJ21iOuZsxThXX+BzA/L2+AWVURxdJT4pgAAAAAElFTkSu\
QmCC\
");
}
}
@@ -1459,9 +1457,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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -1500,9 +1498,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/9hAAAAAXNSR0IArs4c6QAAADpJREFUOI1j\
YBgFgw+UMzAw/CeA6ykxhKBmfIYQrRmbITg1M+Mx4CgDAwMjAwPDQQYGhkZSbR8FJAAAh2UZ0KJo\
1mgAAAAASUVORK5CYII=\
");
}
}
@@ -1615,10 +1613,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/9hAAAAAXNSR0IArs4c6QAAAHlJREFUOI3F\
k0EKgCAQRZ/R1eouLjpmi+4RdYVW2UZBdLTJgj58GHXmoR+EXDPgBB+AFfozScPBJzBpAaX9DLIk\
Q3eAALHSgRYQMsEkzfHaCJAYBmC6SpNKrwFQfrNKvQB7pP8z+ASw+7olyBVg9EXtE0negKH97l4X\
dck/JrzBokIAAAAASUVORK5CYII=\
");
}
}
@@ -2087,19 +2085,21 @@ 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,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAAAXNSR0IArs4c6QAAAvFJREFUOI2t\
1c9r02AYB/DnTd60Td7Mdk06Z0FFrJhsdnhwoujamwqi4qZ40OsmsoF4U3ccKlLdEPwT/MHQ7WiR\
Wl2jh3TbYSJjk2VOelgug1prUl1/vB5GpZ2tutXvKQTeDw8vD98XQY1gjLlQqKvn+PFjFxVFOUgI\
aQYAsO1v6dnZuWQsFnusae/GC4VCfv1ZtP5HOBw+d+3a1Ydut1vw+7c18TwBjsMAAJDP58G2bTBN\
M5vJfLFGRh4MJBLaWOV5tvzBMIgZGBgY7uvrHVIUtdnv9ztdLh5YlgWEECCEgGUxuFw8+Hw+pyRJ\
YjC47+SWLW7f9PR0jFKgVWB/f/9wT093r6qqxOFw1rqJqnAcB7IsO1paWoKiKMqTk1Mvf4HhcKjn\
8uXeIVVVCcOwf5Yq7wsx4PV6HbLsDS4tfZ5LpVLzCGPMPX8+muro2N8qCMI/Y5WxbQtmZmaWz5+/\
sJMJhY52u91uslkMAEAQCHg8nqaurqNn2b6+3tudnQc7XC5+0yAAAMacc3X1B2ba29VDjUxXDiEE\
2traDjGEiB6MuYZBjDGIIvEylAJCv6335oIQAsayrHQ+X2gYKxQKYFnWF2Z+/uNULmc3DNq2DbOz\
czrz6lXsqWma2UZB01zOxuPxUWZiIvEsk8nkGpnStnOQzX79rmlvx9lSqVRcWVlZbm9vOyHLsgMh\
ZkNYsViEhYV56969+1cMY/E9CwDw6dPSh9bWrbtkWVYkSeJqtFqdUDCMBfvFi+iTR4+e3AGoaBtd\
T0YVZe9hnuf9kiQ5/o5SMIyFnK4nE0NDty5RSktVIKW0FI+/Hg0EdncSQnZ4vRJXf0HXJtN1Xbtx\
Y/BMsVj8tXdVXUUppW/eTDwLBHYfEMV6aBlLvr1+ffD0+mfgt/Iro3v2BDoJIdurUQqGYeTWsJun\
ar0pNdu0EhXFNRQAYHHRyOm6rtXD6oJlNJHQxlRVOSII/LZ0Ol1MJpN/xP4pGGMuErkbjUTuRvH/\
qKWN5ichVT1RK2aa7gAAAABJRU5ErkJggg==\
");
}
}
@@ -2442,10 +2442,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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHhJREFUKJG9\
0F8KQEAQx/Hvbs7HEVxF8iRRPChaF3MYXlZtY9AqfrUPOzOf9g/8nPINqoAtBhig80iFKWAV1Afo\
BAtfXAJsgEGgJkSZaDogASZRr+Vp1g+HQ6vYt9rbjmuNYvhYk+9fRsPzE9Kw4/zLt7FAHou+yw6e\
9iu4wGcfbQAAAABJRU5ErkJggg==\
");
}
}
@@ -2459,12 +2459,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/9hAAAAAXNSR0IArs4c6QAAAPJJREFUOI21\
0sEqxFEUx/GP2LGYlIXyBp5BSVKykpSHkCeQGlJT1naylCfQzMITEMmSlYUsRIOy8M9YuH/duXP/\
Y0pO3W7nnvP93XM6h3+wcWzhHC94xRXqmPwNXsQTOhXnGatV8Dw++sDlKTCbwmN4GADu4AgjqcDG\
gPAhhnPltzLJp4m/j6GI6RK6TZIb4X03+HsJXMNJLHATwcdJdUuJP4Vr3+P9sWYkUGAt1yemcRfy\
LuPAetJCTmRG945sx8FR3PcRWcZ7FGtjIi1vTu8iFTgId/n2iZWKFi3gUe9I458r4dJq2MRZAN5w\
gZ1c2X+2L7dscieExRo2AAAAAElFTkSuQmCC\
");
}
}
@@ -2520,11 +2520,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/9hAAAAAXNSR0IArs4c6QAAAM9JREFUOI21\
0k1OAkEQBeAP4hZWyjlcsDKEQxi8gHu9A+KN5Aas/bkAFyAscHDNsJgGOmN3Y4y+pJLu6qrXr374\
BwzwjHd8BXvDLLwVcYcKdcYqTErJu1bCGMOWb5ciGWR+PqDt/8QVdEPAI3rn6ovQx0Ps+Ej8UlJQ\
axp7xPYXBFVcQhz8U9RwES5LXCeCRuhkCJYxwTxDsCgoeIkvuTHeBBVt/waXnHqwwr3vvegmSqhD\
7Dola6JZktwqb3BbKAvNhj3hVTPebThPD7L/FHtCEluLOU5q/wAAAABJRU5ErkJggg==\
");
}
}
@@ -2575,10 +2575,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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHhJREFUKJG9\
0F8KQEAQx/Hvbs7HEVxF8iRRPChaF3MYXlZtY9AqfrUPOzOf9g/8nPINqoAtBhig80iFKWAV1Afo\
BAtfXAJsgEGgJkSZaDogASZRr+Vp1g+HQ6vYt9rbjmuNYvhYk+9fRsPzE9Kw4/zLt7FAHou+yw6e\
9iu4wGcfbQAAAABJRU5ErkJggg==\
");
}
}
@@ -2592,12 +2592,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/9hAAAAAXNSR0IArs4c6QAAAP5JREFUOI2l\
088qRVEUx/EPQjdhcl2JkY6pqYmZMpKBdJ/iegcZeAkP4AEk5QGY8AKSETIQ4kpHdA3Olt2xzz3J\
r9Zgr/1b3/1vbf6pgURuGh2M4TTkXnCDC+SxeahU3MYxVnGEJwyigRYyPIcgTMbF+5gM47vE7oax\
jLkyYAZ7JeBbAkBx7CWMxoAtTCSMVRrBQgxYS5jKwLJmY0CWMMzXAMZjwGfCsF4D6MWAq4Qhw2If\
QDcGHFSYOmhWzF3zc9MtXArnKukBu7iNcjkOkX934quiTTf9fr4GVvARVn3HCR5T29pQtG+vIu4V\
TdRXTezgXPGJujjDNqbqiv+sLyHHMHlr8LK9AAAAAElFTkSuQmCC\
");
}
}
@@ -2659,9 +2659,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,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAEpJREFUKJFj\
YBiZgI2BgaGVgYHhERS3MDAwsBKjsZWBgeE/Gm4mRuNjLBofoitiwqLxPzFi2DQuIlIMA7AyQPz0\
EIobGYgMnOEGAEnmE7O2/wC1AAAAAElFTkSuQmCC\
");
}
}
@@ -2675,14 +2675,14 @@ 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/9hAAAAAXNSR0IArs4c6QAAAU9JREFUOI2V\
079L13EQx/HHV80EExGRQlFQFNcWfywiGK61ZKE4ujS06V/g6Bq055KDIKjo5PIVwYamwDAC00gd\
RKQ0KfwxfO+Db7+IXz04OLh7vu59d59PudvtNf5gGLv4XaL+mrViAUvouA9Yh0l8wUX4J7zFg1Jw\
J34mYLGvofomsBIV+HwLnPk7lAcDXuAYB0nRKXrxvyi+wAl+BPMcNtGPsURgM8S/F8VZ/hW6sAFH\
qMJAUvAX7fhXFGf5pzHGbg6zyMVM3ZjDMrbwC+doQBP6MBqvOkRFDrWYwF4scQg9aMbjENjHTlzh\
Y+ykEVPZIuuxeIcLZD6DGomtRGL9DvAqzmJ0ZQpfV38IfcV4CJ0mDY6Rxxt8C24wfcF80iGPEbTF\
aHVoUfix8kndh1TgEaZjYaVGOMN7PKRwvtSa8RLPouuTEN3DtqsrbGXAJQOahFqC25AXAAAAAElF\
TkSuQmCC\
");
}
}