summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2019-05-28 05:51:20 +0200
committerBenjamin Otte <otte@redhat.com>2019-05-30 15:32:36 +0200
commit1e0c0c0ba7232af75b10f0b6b7185a0ca7bd921f (patch)
tree931cb6da37731fe0b0516a930611d0ea3c7836a0
parent2f3720748710f0d496ccce848efb62057a83f22a (diff)
downloadgtk+-1e0c0c0ba7232af75b10f0b6b7185a0ca7bd921f.tar.gz
rendernodeparser: Parse cairo script
Use cairo-script-interpreter to parse the scripts that generate cairo nodes. This requires libcairoscriptinterpreter.so to work properly, but if it isn't found we disable this (unimportant for normal functioning) code and just emits a parser warning. The testsuite requires it however or it will fail. A new test is included that tests all of this.
-rw-r--r--config.h.meson2
-rw-r--r--gsk/gskrendernodeparser.c142
-rw-r--r--gsk/meson.build1
-rw-r--r--gtk/css/gtkcssenums.h2
-rw-r--r--meson.build3
-rw-r--r--testsuite/gsk/compare/scaled-cairo.node7
-rw-r--r--testsuite/gsk/compare/scaled-cairo.pngbin0 -> 151 bytes
-rw-r--r--testsuite/gsk/meson.build3
8 files changed, 155 insertions, 5 deletions
diff --git a/config.h.meson b/config.h.meson
index e52badaede..a8ea83d018 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -297,6 +297,8 @@
#mesondefine GTK_PRINT_BACKENDS
+#mesondefine HAVE_CAIRO_SCRIPT_INTERPRETER
+
#mesondefine HAVE_HARFBUZZ
#mesondefine HAVE_PANGOFT
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 59b8a4292c..6dc1d19448 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -36,6 +36,9 @@
#ifdef CAIRO_HAS_SCRIPT_SURFACE
#include <cairo-script.h>
#endif
+#ifdef HAVE_CAIRO_SCRIPT_INTERPRETER
+#include <cairo-script-interpreter.h>
+#endif
typedef struct _Declaration Declaration;
@@ -132,6 +135,131 @@ clear_texture (gpointer inout_texture)
g_clear_object ((GdkTexture **) inout_texture);
}
+static cairo_surface_t *
+csi_hooks_surface_create (void *closure,
+ cairo_content_t content,
+ double width,
+ double height,
+ long uid)
+{
+ return cairo_surface_create_similar (closure, content, width, height);
+}
+
+static const cairo_user_data_key_t csi_hooks_key;
+
+static cairo_t *
+csi_hooks_context_create (void *closure,
+ cairo_surface_t *surface)
+{
+ cairo_t *cr = cairo_create (surface);
+
+ cairo_set_user_data (cr,
+ &csi_hooks_key,
+ cairo_surface_reference (surface),
+ (cairo_destroy_func_t) cairo_surface_destroy);
+
+ return cr;
+}
+
+static void
+csi_hooks_context_destroy (void *closure,
+ void *ptr)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ surface = cairo_get_user_data (ptr, &csi_hooks_key);
+ cr = cairo_create (closure);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+}
+
+static gboolean
+parse_script (GtkCssParser *parser,
+ gpointer out_data)
+{
+#ifdef HAVE_CAIRO_SCRIPT_INTERPRETER
+ GError *error = NULL;
+ GBytes *bytes;
+ GtkCssLocation start_location;
+ char *url, *scheme;
+ cairo_script_interpreter_t *csi;
+ cairo_script_interpreter_hooks_t hooks = {
+ .surface_create = csi_hooks_surface_create,
+ .context_create = csi_hooks_context_create,
+ .context_destroy = csi_hooks_context_destroy,
+ };
+
+ start_location = *gtk_css_parser_get_start_location (parser);
+ url = gtk_css_parser_consume_url (parser);
+ if (url == NULL)
+ return FALSE;
+
+ scheme = g_uri_parse_scheme (url);
+ if (scheme && g_ascii_strcasecmp (scheme, "data") == 0)
+ {
+ bytes = gtk_css_data_url_parse (url, NULL, &error);
+ }
+ else
+ {
+ GFile *file;
+
+ file = gtk_css_parser_resolve_url (parser, url);
+ bytes = g_file_load_bytes (file, NULL, NULL, &error);
+ g_object_unref (file);
+ }
+
+ g_free (scheme);
+ g_free (url);
+
+ if (bytes == NULL)
+ {
+ gtk_css_parser_emit_error (parser,
+ &start_location,
+ gtk_css_parser_get_end_location (parser),
+ error);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ hooks.closure = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ csi = cairo_script_interpreter_create ();
+ cairo_script_interpreter_install_hooks (csi, &hooks);
+ cairo_script_interpreter_feed_string (csi, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+ g_bytes_unref (bytes);
+ if (cairo_surface_status (hooks.closure) != CAIRO_STATUS_SUCCESS)
+ {
+ gtk_css_parser_error_value (parser, "Invalid Cairo script: %s", cairo_status_to_string (cairo_surface_status (hooks.closure)));
+ cairo_script_interpreter_destroy (csi);
+ return FALSE;
+ }
+ if (cairo_script_interpreter_destroy (csi) != CAIRO_STATUS_SUCCESS)
+ {
+ gtk_css_parser_error_value (parser, "Invalid Cairo script");
+ cairo_surface_destroy (hooks.closure);
+ return FALSE;
+ }
+
+ *(cairo_surface_t **) out_data = hooks.closure;
+ return TRUE;
+#else
+ gtk_css_parser_warn (parser,
+ GTK_CSS_PARSER_WARNING_UNIMPLEMENTED,
+ gtk_css_parser_get_block_location (parser),
+ gtk_css_parser_get_start_location (parser),
+ "GTK was compiled with script interpreter support. Using fallback pixel data for Cairo node.");
+ *(cairo_surface_t **) out_data = NULL;
+ return TRUE;
+#endif
+}
+
+static void
+clear_surface (gpointer inout_surface)
+{
+ g_clear_pointer ((cairo_surface_t **) inout_surface, cairo_surface_destroy);
+}
+
static gboolean
parse_rounded_rect (GtkCssParser *parser,
gpointer out_rect)
@@ -930,9 +1058,11 @@ parse_cairo_node (GtkCssParser *parser)
{
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
GdkTexture *pixels = NULL;
+ cairo_surface_t *surface = NULL;
const Declaration declarations[] = {
{ "bounds", parse_rect, NULL, &bounds },
- { "pixels", parse_texture, clear_texture, &pixels }
+ { "pixels", parse_texture, clear_texture, &pixels },
+ { "script", parse_script, clear_surface, &surface }
};
GskRenderNode *node;
cairo_t *cr;
@@ -943,13 +1073,16 @@ parse_cairo_node (GtkCssParser *parser)
cr = gsk_cairo_node_get_draw_context (node);
- if (pixels != NULL)
+ if (surface != NULL)
+ {
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+ }
+ else if (pixels != NULL)
{
- cairo_surface_t *surface;
surface = gdk_texture_download_surface (pixels);
cairo_set_source_surface (cr, surface, 0, 0);
cairo_paint (cr);
- cairo_surface_destroy (surface);
}
else
{
@@ -959,6 +1092,7 @@ parse_cairo_node (GtkCssParser *parser)
cairo_destroy (cr);
g_clear_object (&pixels);
+ g_clear_pointer (&surface, cairo_surface_destroy);
return node;
}
diff --git a/gsk/meson.build b/gsk/meson.build
index 5b3e4d7ee6..55064806b6 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -156,6 +156,7 @@ gsk_deps = [
graphene_dep,
pango_dep,
cairo_dep,
+ cairo_csi_dep,
pixbuf_dep,
libgdk_dep,
]
diff --git a/gtk/css/gtkcssenums.h b/gtk/css/gtkcssenums.h
index 1894b55b74..5470a61e51 100644
--- a/gtk/css/gtkcssenums.h
+++ b/gtk/css/gtkcssenums.h
@@ -62,6 +62,8 @@ typedef enum
* deprecated and will be removed in a future version
* @GTK_CSS_PARSER_WARNING_SYNTAX: A syntax construct was used
* that should be avoided
+ * @GTK_CSS_PARSER_WARNING_UNIMPLEMENTED: A feature is not
+ * implemented
*
* Warnings that can occur while parsing CSS.
*
diff --git a/meson.build b/meson.build
index 34cf3cfcc5..3ea4ac57d2 100644
--- a/meson.build
+++ b/meson.build
@@ -461,11 +461,14 @@ if cc.get_id() == 'msvc'
endif
endif
+cairo_csi_dep = cc.find_library('cairo-script-interpreter')
+
if not harfbuzz_dep.found()
harfbuzz_dep = dependency('harfbuzz', version: '>= 0.9', required: false,
fallback: ['harfbuzz', 'libharfbuzz_dep'])
endif
+cdata.set('HAVE_CAIRO_SCRIPT_INTERPRETER', cairo_csi_dep.found())
cdata.set('HAVE_HARFBUZZ', harfbuzz_dep.found())
cdata.set('HAVE_PANGOFT', pangoft_dep.found())
diff --git a/testsuite/gsk/compare/scaled-cairo.node b/testsuite/gsk/compare/scaled-cairo.node
new file mode 100644
index 0000000000..7606c4050c
--- /dev/null
+++ b/testsuite/gsk/compare/scaled-cairo.node
@@ -0,0 +1,7 @@
+transform {
+ transform: scale(0.5);
+ child: cairo {
+ bounds: 0 0 100 100;
+ script: url("data:;base64,JSFDYWlyb1NjcmlwdAo8PCAvY29udGVudCAvL0NPTE9SX0FMUEhBIC93aWR0aCA1MCAvaGVpZ2h0IDUwID4+IHN1cmZhY2UgY29udGV4dAoxIDAgMC44IHJnYiBzZXQtc291cmNlCnBhaW50CnBvcAo=");
+ }
+}
diff --git a/testsuite/gsk/compare/scaled-cairo.png b/testsuite/gsk/compare/scaled-cairo.png
new file mode 100644
index 0000000000..c900788caa
--- /dev/null
+++ b/testsuite/gsk/compare/scaled-cairo.png
Binary files differ
diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build
index 4f469603a9..f2bc21b920 100644
--- a/testsuite/gsk/meson.build
+++ b/testsuite/gsk/meson.build
@@ -23,6 +23,7 @@ compare_render_tests = [
'clip-coordinates-3d',
'clipped_rounded_clip',
'color-blur0',
+ 'color-matrix-identity',
'cross-fade-in-opacity',
'empty-blend',
'empty-blur',
@@ -50,9 +51,9 @@ compare_render_tests = [
'outset_shadow_offset_y',
'outset_shadow_rounded_top',
'outset_shadow_simple',
+ 'scaled-cairo',
'shadow-in-opacity',
'texture-url',
- 'color-matrix-identity',
]
renderers = [