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.
This commit is contained in:
Benjamin Otte 2019-05-28 05:51:20 +02:00
parent 2f37207487
commit 1e0c0c0ba7
8 changed files with 155 additions and 5 deletions

View File

@ -297,6 +297,8 @@
#mesondefine GTK_PRINT_BACKENDS
#mesondefine HAVE_CAIRO_SCRIPT_INTERPRETER
#mesondefine HAVE_HARFBUZZ
#mesondefine HAVE_PANGOFT

View File

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

View File

@ -156,6 +156,7 @@ gsk_deps = [
graphene_dep,
pango_dep,
cairo_dep,
cairo_csi_dep,
pixbuf_dep,
libgdk_dep,
]

View File

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

View File

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

View File

@ -0,0 +1,7 @@
transform {
transform: scale(0.5);
child: cairo {
bounds: 0 0 100 100;
script: url("data:;base64,JSFDYWlyb1NjcmlwdAo8PCAvY29udGVudCAvL0NPTE9SX0FMUEhBIC93aWR0aCA1MCAvaGVpZ2h0IDUwID4+IHN1cmZhY2UgY29udGV4dAoxIDAgMC44IHJnYiBzZXQtc291cmNlCnBhaW50CnBvcAo=");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

View File

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