[gtk/wip/otte/menu: 883/888] rendernodeparser: Parse cairo script



commit f3e9e3fe82a8a85f5ca09d567a551184e37b60f4
Author: Benjamin Otte <otte redhat com>
Date:   Tue May 28 05:51:20 2019 +0200

    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.

 config.h.meson                          |   2 +
 gsk/gskrendernodeparser.c               | 142 +++++++++++++++++++++++++++++++-
 gsk/meson.build                         |   1 +
 gtk/css/gtkcssenums.h                   |   2 +
 meson.build                             |   3 +
 testsuite/gsk/compare/scaled-cairo.node |   7 ++
 testsuite/gsk/compare/scaled-cairo.png  | Bin 0 -> 151 bytes
 testsuite/gsk/meson.build               |   3 +-
 8 files changed, 155 insertions(+), 5 deletions(-)
---
diff --git a/config.h.meson b/config.h.meson
index 436e1a1501..b8005f0adf 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -294,6 +294,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 97cc7fdf28..3ae9a34936 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
Binary files /dev/null and b/testsuite/gsk/compare/scaled-cairo.png 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 = [


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]