From 3a373b9b33504a85a1a38f82c944fc530dbdba5b Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 9 May 2019 03:56:25 +0200 Subject: [PATCH] rendernodeparser: Parse images differently Instead of encoding the raw data, encode the full image to a PNG. And instead of stuffing that encoding into a string, use a full data: url. And then remove the width and height properties, because they're now implicitly included in the data. And then change the parser to match. And because the parser now parses regular urls on top of data: urls, we can now load any random file. --- gsk/gskrendernodeparser.c | 136 +++++---- testsuite/gsk/compare/texture-url.node | 11 + testsuite/gsk/compare/texture-url.png | Bin 0 -> 179 bytes testsuite/gsk/meson.build | 7 +- .../serializedeserialize/widgetfactory.node | 282 +++++++----------- 5 files changed, 203 insertions(+), 233 deletions(-) create mode 100644 testsuite/gsk/compare/texture-url.node create mode 100644 testsuite/gsk/compare/texture-url.png diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index b9962931c7..20f43c8b18 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -1,13 +1,16 @@ #include "gskrendernodeparserprivate.h" -#include -#include -#include "gtk/css/gtkcssparserprivate.h" #include "gskroundedrectprivate.h" #include "gskrendernodeprivate.h" #include "gsktransformprivate.h" +#include "gdk/gdkrgbaprivate.h" +#include "gdk/gdktextureprivate.h" +#include +#include "gtk/css/gtkcssparserprivate.h" +#include "gtk/css/gtkcssdataurlprivate.h" + typedef struct _Declaration Declaration; struct _Declaration @@ -70,35 +73,67 @@ parse_rect (GtkCssParser *parser, } static gboolean -parse_data (GtkCssParser *parser, - gpointer out_data) +parse_texture (GtkCssParser *parser, + gpointer out_data) { - const GtkCssToken *token; - struct { - guchar *data; - gsize data_len; - } *texture_data = out_data; + GFile *file; + GdkTexture *texture; + GError *error = NULL; + GtkCssLocation start_location; - token = gtk_css_parser_get_token (parser); - if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING)) + start_location = *gtk_css_parser_get_start_location (parser); + file = gtk_css_parser_consume_url (parser); + if (file == NULL) return FALSE; - if (!g_str_has_prefix (token->string.string, "data:;base64,")) + if (g_file_has_uri_scheme (file, "data")) { - gtk_css_parser_error_value (parser, "Only base64 encoded data is allowed"); - return FALSE; + GInputStream *stream; + char *uri; + GdkPixbuf *pixbuf; + GBytes *bytes; + + uri = g_file_get_uri (file); + texture = NULL; + + bytes = gtk_css_data_url_parse (uri, NULL, &error); + if (bytes) + { + 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) + { + texture = gdk_texture_new_for_pixbuf (pixbuf); + g_object_unref (pixbuf); + } + } + + g_free (uri); } + else + { + texture = gdk_texture_new_from_file (file, &error); + } + g_object_unref (file); - texture_data->data = g_base64_decode (token->string.string + strlen ("data:;base64,"), - &texture_data->data_len); + if (texture == NULL) + { + gtk_css_parser_emit_error (parser, + &start_location, + gtk_css_parser_get_end_location (parser), + error); + g_clear_error (&error); + return FALSE; + } - gtk_css_parser_consume_token (parser); if (!parse_semicolon (parser)) { - g_free (texture_data->data); + g_object_unref (texture); return FALSE; } + *(GdkTexture **) out_data = texture; return TRUE; } @@ -691,35 +726,21 @@ static GskRenderNode * parse_texture_node (GtkCssParser *parser) { graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 0, 0); - struct { - guchar *data; - gsize data_len; - } texture_data = { NULL, 0 }; - double width = 0.0; - double height = 0.0; + GdkTexture *texture = NULL; const Declaration declarations[] = { { "bounds", parse_rect, &bounds }, - { "width", parse_double, &width }, - { "height", parse_double, &height }, - { "texture", parse_data, &texture_data } + { "texture", parse_texture, &texture } }; - GdkTexture *texture; - GdkPixbuf *pixbuf; GskRenderNode *node; parse_declarations (parser, declarations, G_N_ELEMENTS(declarations)); - pixbuf = gdk_pixbuf_new_from_data (texture_data.data, - GDK_COLORSPACE_RGB, - TRUE, - 8, - (int)width, - (int)height, - 4 * (int)width, - (GdkPixbufDestroyNotify)g_free, NULL); - - texture = gdk_texture_new_for_pixbuf (pixbuf); - g_object_unref (pixbuf); + if (texture == NULL) + { + gtk_css_parser_error_syntax (parser, "Missing \"texture\" property definition"); + return NULL; + } + node = gsk_texture_node_new (texture, &bounds); g_object_unref (texture); @@ -1497,6 +1518,16 @@ append_node_param (Printer *p, render_node_print (p, node); } +static cairo_status_t +surface_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + g_byte_array_append (closure, data, length); + + return CAIRO_STATUS_SUCCESS; +} + static void render_node_print (Printer *p, GskRenderNode *node) @@ -1737,30 +1768,25 @@ render_node_print (Printer *p, case GSK_TEXTURE_NODE: { GdkTexture *texture = gsk_texture_node_get_texture (node); - int stride; - int len; - guchar *data; + cairo_surface_t *surface; + GByteArray *array; char *b64; start_node (p, "texture"); append_rect_param (p, "bounds", &node->bounds); - /* TODO: width and height here are unnecessary and can later be computed from the data length? */ - append_float_param (p, "width", gdk_texture_get_width (texture)); - append_float_param (p, "height", gdk_texture_get_height (texture)); - - stride = 4 * gdk_texture_get_width (texture); - len = sizeof (guchar) * stride * gdk_texture_get_height (texture); - data = g_malloc (len); - gdk_texture_download (texture, data, stride); - b64 = g_base64_encode (data, len); + surface = gdk_texture_download_surface (texture); + array = g_byte_array_new (); + cairo_surface_write_to_png_stream (surface, surface_write, array); + b64 = g_base64_encode (array->data, array->len); _indent (p); - g_string_append_printf (p->str, "texture: \"data:;base64,%s\";\n", b64); + g_string_append_printf (p->str, "texture: url(\"data:image/png;base64,%s\");\n", b64); end_node (p); g_free (b64); - g_free (data); + g_byte_array_free (array, TRUE); + cairo_surface_destroy (surface); } break; diff --git a/testsuite/gsk/compare/texture-url.node b/testsuite/gsk/compare/texture-url.node new file mode 100644 index 0000000000..f31bf02610 --- /dev/null +++ b/testsuite/gsk/compare/texture-url.node @@ -0,0 +1,11 @@ +/* Add a color node to blow up the bounds so that + we can test the texture bounds work. */ +color { + bounds: 0 0 50 50; + color: white; +} + +texture { + bounds: 10 10 30 30; + texture: url('data:,'); +} diff --git a/testsuite/gsk/compare/texture-url.png b/testsuite/gsk/compare/texture-url.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4bdb3cb80ff46efc5a5aaf240dc201f037a686 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1SBWM%0B~AY)RhkE)4%caKYZ?lYt@`o-U3d z5v^};7;-fj2rxJ*|NDRXBXiOWC!dX%<0d|xb)eVs`NsL5-#4VEc5ZTsT%=Q|`z*QU z>!;W6H@)0;N(d?#I8DQQiYgK}A~vUP*RA|rYr%Q~r|n?;wS+y{=)U?>pq&h!u6{1- HoD!M