diff options
author | Timm Bäder <mail@baedert.org> | 2019-03-02 16:55:17 +0100 |
---|---|---|
committer | Timm Bäder <mail@baedert.org> | 2019-03-02 16:55:17 +0100 |
commit | 984a9c2c09bf51a1aa4498d653f2109bc4c6bcdb (patch) | |
tree | 290c5b9338876560c27b11663c25ae8765aa1872 | |
parent | 0c406160e6d7c22ef396d85147cc123ed1a0eb7b (diff) | |
download | gtk+-wip/baedert/render-node-parser.tar.gz |
Parse render nodes from text fileswip/baedert/render-node-parser
-rw-r--r-- | gsk/gskrendernode.c | 44 | ||||
-rw-r--r-- | gsk/gskrendernodeparser.c | 1408 | ||||
-rw-r--r-- | gsk/gskrendernodeparserprivate.h | 10 | ||||
-rw-r--r-- | gsk/meson.build | 1 |
4 files changed, 1424 insertions, 39 deletions
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c index 343fd4b8ce..ed6fc527f1 100644 --- a/gsk/gskrendernode.c +++ b/gsk/gskrendernode.c @@ -42,6 +42,7 @@ #include "gskdebugprivate.h" #include "gskrendererprivate.h" +#include "gskrendernodeparserprivate.h" #include <graphene-gobject.h> @@ -328,19 +329,11 @@ gsk_render_node_diff (GskRenderNode *node1, GBytes * gsk_render_node_serialize (GskRenderNode *node) { - GVariant *node_variant, *variant; GBytes *result; + char *str; - node_variant = gsk_render_node_serialize_node (node); - - variant = g_variant_new ("(suuv)", - GSK_RENDER_NODE_SERIALIZATION_ID, - (guint32) GSK_RENDER_NODE_SERIALIZATION_VERSION, - (guint32) gsk_render_node_get_node_type (node), - node_variant); - - result = g_variant_get_data_as_bytes (variant); - g_variant_unref (variant); + str = gsk_render_node_serialize_to_string (node); + result = g_bytes_new_take (str, strlen (str)); return result; } @@ -397,36 +390,9 @@ GskRenderNode * gsk_render_node_deserialize (GBytes *bytes, GError **error) { - char *id_string; - guint32 version, node_type; - GVariant *variant, *node_variant; GskRenderNode *node = NULL; - variant = g_variant_new_from_bytes (G_VARIANT_TYPE ("(suuv)"), bytes, FALSE); - - g_variant_get (variant, "(suuv)", &id_string, &version, &node_type, &node_variant); - - if (!g_str_equal (id_string, GSK_RENDER_NODE_SERIALIZATION_ID)) - { - g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_FORMAT, - "Data not in GskRenderNode serialization format."); - goto out; - } - - if (version != GSK_RENDER_NODE_SERIALIZATION_VERSION) - { - g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_VERSION, - "Format version %u not supported.", version); - goto out; - } - - node = gsk_render_node_deserialize_node (node_type, node_variant, error); - -out: - g_free (id_string); - g_variant_unref (node_variant); - g_variant_unref (variant); + node = gsk_render_node_deserialize_from_string ((const char *)g_bytes_get_data (bytes, NULL)); return node; } - diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c new file mode 100644 index 0000000000..534ec0d8db --- /dev/null +++ b/gsk/gskrendernodeparser.c @@ -0,0 +1,1408 @@ + +#include "gskrendernodeparserprivate.h" +#include "gskroundedrectprivate.h" +#include "gskrendernodeprivate.h" +#include <ctype.h> + +/* TODO: Hack. */ +#define GTK_COMPILATION +#include "../gtk/gtktransform.h" +#undef GTK_COMPILATION + + +enum { + TOK_IDENT, + TOK_NUMBER, + TOK_OPEN_BRACE, + TOK_CLOSE_BRACE, + TOK_OPEN_PAREN, + TOK_CLOSE_PAREN, + TOK_EQUALS, + TOK_COMMA, + TOK_HASH, + TOK_DATA, + TOK_EOF, +}; + +typedef struct +{ + const char *start; + int len; + int type; +} Token; + +static double +number_value (const Token *t) +{ + char *buff; + double value; + + g_assert (t->type == TOK_NUMBER); + + /* No strtod that takes a length :( */ + buff = g_strdup_printf ("%.*s", t->len, t->start); + value = strtod (buff, NULL); + g_free (buff); + + return value; +} + +static gboolean +is_ident (const Token *t, + const char *expected_text) +{ + if (t->type != TOK_IDENT) + return FALSE; + + if (t->len != strlen (expected_text)) + return FALSE; + + if (strncmp (t->start, expected_text, t->len) != 0) + return FALSE; + + return TRUE; +} + +typedef struct +{ + int n_tokens; + Token *tokens; + + int pos; + const Token *cur; +} Parser; + +static void +skip (Parser *p) +{ + p->pos ++; + + g_assert_cmpint (p->pos, <, p->n_tokens); + p->cur = &p->tokens[p->pos]; +} + +static const Token * +lookahead (Parser *p, + int lookahead) +{ + g_assert_cmpint (p->pos, <, p->n_tokens - lookahead); + + return &p->tokens[p->pos + lookahead]; +} + +static void +expect (Parser *p, + int expected_type) +{ + if (p->cur->type != expected_type) + g_error ("Expected token type %d but found %d ('%.*s')", + expected_type, p->cur->type, p->cur->len, p->cur->start); +} + +static void +expect_skip (Parser *p, + int expected_type) +{ + expect (p, expected_type); + skip (p); +} + +static void +expect_skip_ident (Parser *p, + const char *ident) +{ + if (p->cur->type != TOK_IDENT) + g_error ("Expected ident '%s', but found token %.*s", + ident, p->cur->len, p->cur->start); + + if (strncmp (ident, p->cur->start, p->cur->len) != 0) + g_error ("Expected ident '%s', but found token %.*s", + ident, p->cur->len, p->cur->start); + + skip (p); +} + +static void +parser_init (Parser *p, + Token *tokens, + int n_tokens) +{ + p->tokens = tokens; + p->pos = 0; + p->cur = &tokens[p->pos]; + p->n_tokens = n_tokens; +} + +static inline gboolean +isnum (const char *p) +{ + switch (*p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '.': + return TRUE; + + default: + return FALSE; + } +} + +static gboolean +is_ident_char (const char *p) +{ + if (isalpha (*p)) + return TRUE; + + switch (*p) + { + case '_': + return TRUE; + + default: + return FALSE; + } + + return FALSE; +} + +static Token * +tokenize (const char *string, + int *n_tokens) +{ + GArray *tokens = g_array_new (FALSE, TRUE, sizeof (Token)); + const char *p = string; + Token eof; + + while (*p != '\0') + { + Token tok = { + .start = p, + .len = 0, + .type = -1 + }; + + if (isnum (p)) + { + while (isnum (p)) + p ++; + + tok.len = p - tok.start; + tok.type = TOK_NUMBER; + } + else if (*p == '-' && isnum (p + 1)) + { + /* Start of a negative number */ + p ++; + + while (isnum (p)) + p ++; + + tok.len = p - tok.start; + tok.type = TOK_NUMBER; + } + else if (is_ident_char (p)) + { + while (is_ident_char (p)) + p++; + + tok.len = p - tok.start; + tok.type = TOK_IDENT; + } + else if (isspace (*p)) + { + p ++; + continue; + } + else if (*p == '"') + { + gboolean escaped = FALSE; + p++; + tok.start ++; /* Skip the " */ + for (; *p != '"'; p++) + { + if (*p == '\0') + break; + } + + tok.len = p - tok.start; + tok.type = TOK_DATA; + p++; + } + else if (*p == '{') + { + p ++; + tok.len = 1; + tok.type = TOK_OPEN_BRACE; + } + else if (*p == '}') + { + p ++; + tok.len = 1; + tok.type = TOK_CLOSE_BRACE; + } + else if (*p == '(') + { + p ++; + tok.len = 1; + tok.type = TOK_OPEN_PAREN; + } + else if (*p == ')') + { + p ++; + tok.len = 1; + tok.type = TOK_CLOSE_PAREN; + } + else if (*p == '=') + { + p ++; + tok.len = 1; + tok.type = TOK_EQUALS; + } + else if (*p == ',') + { + p ++; + tok.len = 1; + tok.type = TOK_COMMA; + } + else if (*p == '#') + { + /* Comment! Ignore everything until EOL */ + while (*p != '\n' && *p != '\0') + p ++; + + continue; + } + else + { + g_error ("Huh? %c", *p); + } + + g_assert (tok.type != -1); + g_assert (tok.len > 0); + + g_array_append_val (tokens, tok); + } + + eof.start = NULL; + eof.len = 1; + eof.type = TOK_EOF; + g_array_append_val (tokens, eof); + + *n_tokens = (int)tokens->len; + + return (Token *)g_array_free (tokens, FALSE); +} + +static void +parse_double4 (Parser *p, + double *out_values) +{ + int i; + + expect_skip (p, TOK_OPEN_PAREN); + expect (p, TOK_NUMBER); + out_values[0] = number_value (p->cur); + skip (p); + + for (i = 0; i < 3; i ++) + { + expect_skip (p, TOK_COMMA); + expect (p, TOK_NUMBER); + out_values[1 + i] = number_value (p->cur); + skip (p); + } + + expect_skip (p, TOK_CLOSE_PAREN); +} + +static void +parse_tuple (Parser *p, + double *out_values) +{ + expect_skip (p, TOK_OPEN_PAREN); + expect (p, TOK_NUMBER); + out_values[0] = number_value (p->cur); + skip (p); + + expect_skip (p, TOK_COMMA); + + expect (p, TOK_NUMBER); + out_values[1] = number_value (p->cur); + skip (p); + + expect_skip (p, TOK_CLOSE_PAREN); +} + +/* + * The cases we allow are: + * (x, y, w, h) (w1, h1) (w2, h2) (w3, h3) (w4, h4) for the full rect + * (x, y, w, h) s1 s2 s3 s4 for rect + quad corners + * (x, y, w, h) s for rect + all corners the same size + * (x, y, w, h) for just the rect with 0-sized corners + */ +static void +parse_rounded_rect (Parser *p, + GskRoundedRect *result) +{ + double rect[4]; + double corner0[2]; + double corner1[2]; + double corner2[2]; + double corner3[2]; + + parse_double4 (p, rect); + + if (p->cur->type == TOK_OPEN_PAREN) + { + parse_tuple (p, corner0); + parse_tuple (p, corner1); + parse_tuple (p, corner2); + parse_tuple (p, corner3); + } + else if (p->cur->type == TOK_NUMBER) + { + double val = number_value (p->cur); + + corner0[0] = corner0[1] = val; + + skip (p); + + if (p->cur->type == TOK_NUMBER) + { + corner1[0] = corner1[1] = number_value (p->cur); + skip (p); + corner2[0] = corner2[1] = number_value (p->cur); + skip (p); + corner3[0] = corner3[1] = number_value (p->cur); + skip (p); + } + else + { + corner1[0] = corner1[1] = val; + corner2[0] = corner2[1] = val; + corner3[0] = corner3[1] = val; + } + } + else + { + corner0[0] = corner0[1] = 0.0; + corner1[0] = corner1[1] = 0.0; + corner2[0] = corner2[1] = 0.0; + corner3[0] = corner3[1] = 0.0; + } + + gsk_rounded_rect_init (result, + &GRAPHENE_RECT_INIT (rect[0], rect[1], rect[2], rect[3]), + &(graphene_size_t) { corner0[0], corner0[1] }, + &(graphene_size_t) { corner1[0], corner1[1] }, + &(graphene_size_t) { corner2[0], corner2[1] }, + &(graphene_size_t) { corner3[0], corner3[1] }); +} + +static void +parse_matrix (Parser *p, + graphene_matrix_t *matrix) +{ + float vals[16]; + int i; + + expect_skip (p, TOK_OPEN_PAREN); + + vals[0] = number_value (p->cur); + skip (p); + + for (i = 1; i < 16; i ++) + { + expect_skip (p, TOK_COMMA); + vals[i] = number_value (p->cur); + skip (p); + } + + expect_skip (p, TOK_CLOSE_PAREN); + + graphene_matrix_init_from_float (matrix, vals); +} + +static double +parse_float_param (Parser *p, + const char *param_name) +{ + double value; + + expect_skip_ident (p, param_name); + expect_skip (p, TOK_EQUALS); + expect (p, TOK_NUMBER); + value = number_value (p->cur); + skip (p); + + return value; +} + +static void +parse_tuple_param (Parser *p, + const char *param_name, + double *values) +{ + expect_skip_ident (p, param_name); + expect_skip (p, TOK_EQUALS); + + parse_tuple (p, values); +} + +static void +parse_double4_param (Parser *p, + const char *param_name, + double *values) +{ + expect_skip_ident (p, param_name); + expect_skip (p, TOK_EQUALS); + + parse_double4 (p, values); +} + +static void +parse_rounded_rect_param (Parser *p, + const char *param_name, + GskRoundedRect *rect) +{ + expect_skip_ident (p, param_name); + expect_skip (p, TOK_EQUALS); + + parse_rounded_rect (p, rect); +} + +static void +parse_matrix_param (Parser *p, + const char *param_name, + graphene_matrix_t *matrix) +{ + expect_skip_ident (p, param_name); + expect_skip (p, TOK_EQUALS); + + parse_matrix (p, matrix); +} + +static GskRenderNode * +parse_node (Parser *p) +{ + GskRenderNode *result = NULL; + + g_assert (p->cur->type == TOK_IDENT); + + + if (is_ident (p->cur, "color")) + { + double color[4]; + double bounds[4]; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_double4_param (p, "bounds", bounds); + + expect_skip_ident (p, "color"); + expect_skip (p, TOK_EQUALS); + parse_double4 (p, color); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_color_node_new (&(GdkRGBA) { color[0], color[1], color[2], color[3] }, + &GRAPHENE_RECT_INIT (bounds[0], bounds[1], bounds[2], bounds[3])); + } + else if (is_ident (p->cur, "opacity")) + { + double opacity = 0.0; + GskRenderNode *child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + expect_skip_ident (p, "opacity"); + expect_skip (p, TOK_EQUALS); + expect (p, TOK_NUMBER); + opacity = number_value (p->cur); + skip (p); + + child = parse_node (p); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_opacity_node_new (child, opacity); + gsk_render_node_unref (child); + } + else if (is_ident (p->cur, "container")) + { + GPtrArray *children = g_ptr_array_new (); + guint i; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + while (p->cur->type != TOK_CLOSE_BRACE) + g_ptr_array_add (children, parse_node (p)); + + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_container_node_new ((GskRenderNode **)children->pdata, children->len); + + for (i = 0; i < children->len; i ++) + gsk_render_node_unref (g_ptr_array_index (children, i)); + + g_ptr_array_free (children, TRUE); + } + else if (is_ident (p->cur, "outset_shadow")) + { + GskRoundedRect outline; + double color[4]; + float dx, dy, spread, blur_radius; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + parse_rounded_rect_param (p, "outline", &outline); + + parse_double4_param (p, "color", color); + dx = parse_float_param (p, "dx"); + dy = parse_float_param (p, "dy"); + spread = parse_float_param (p, "spread"); + blur_radius = parse_float_param (p, "blur_radius"); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_outset_shadow_node_new (&outline, + &(GdkRGBA) { color[0], color[1], color[2], color[3] }, + dx, dy, + spread, + blur_radius); + } + else if (is_ident (p->cur, "cross_fade")) + { + double progress; + GskRenderNode *start_child; + GskRenderNode *end_child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + progress = parse_float_param (p, "progress"); + start_child = parse_node (p); + end_child = parse_node (p); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_cross_fade_node_new (start_child, end_child, progress); + + gsk_render_node_unref (start_child); + gsk_render_node_unref (end_child); + } + else if (is_ident (p->cur, "clip")) + { + double clip_rect[4]; + GskRenderNode *child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_double4_param (p, "clip", clip_rect); + child = parse_node (p); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_clip_node_new (child, + &GRAPHENE_RECT_INIT ( + clip_rect[0], clip_rect[1], + clip_rect[2], clip_rect[3] + )); + + gsk_render_node_unref (child); + } + else if (is_ident (p->cur, "rounded_clip")) + { + GskRoundedRect clip_rect; + GskRenderNode *child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_rounded_rect_param (p, "clip", &clip_rect); + child = parse_node (p); + + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_rounded_clip_node_new (child, &clip_rect); + + gsk_render_node_unref (child); + } + else if (is_ident (p->cur, "linear_gradient")) + { + GArray *stops = g_array_new (FALSE, TRUE, sizeof (GskColorStop)); + double bounds[4]; + double start[2]; + double end[2]; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + parse_double4_param (p, "bounds", bounds); + parse_tuple_param (p, "start", start); + parse_tuple_param (p, "end", end); + + expect_skip_ident (p, "stops"); + expect_skip (p, TOK_EQUALS); + while (p->cur->type == TOK_OPEN_PAREN) + { + GskColorStop stop; + double color[4]; + + skip (p); + stop.offset = number_value (p->cur); + skip (p); + expect_skip (p, TOK_COMMA); + parse_double4 (p, color); + expect_skip (p, TOK_CLOSE_PAREN); + + stop.color = (GdkRGBA) { color[0], color[1], color[2], color[3] }; + g_array_append_val (stops, stop); + } + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_linear_gradient_node_new (&GRAPHENE_RECT_INIT ( + bounds[0], bounds[1], + bounds[2], bounds[3] + ), + &(graphene_point_t) { start[0], start[1] }, + &(graphene_point_t) { end[0], end[1] }, + (GskColorStop *)stops->data, + stops->len); + g_array_free (stops, TRUE); + } + else if (is_ident (p->cur, "transform")) + { + graphene_matrix_t matrix; + GskRenderNode *child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + expect_skip_ident (p, "transform"); + expect_skip (p, TOK_EQUALS); + + if (p->cur->type == TOK_OPEN_PAREN) + { + parse_matrix (p, &matrix); + } + else + { + GtkTransform *transform = NULL; + expect (p, TOK_IDENT); + + for (;;) + { + /* Transform name */ + expect (p, TOK_IDENT); + + if (lookahead (p, 1)->type == TOK_OPEN_BRACE) /* Start of child node */ + break; + + if (is_ident (p->cur, "translate")) + { + double offset[2]; + skip (p); + parse_tuple (p, offset); + transform = gtk_transform_translate (transform, + &(graphene_point_t) { offset[0], offset[1] }); + + } + else + { + g_error ("Unknown transform type: %.*s", p->cur->len, p->cur->start); + } + } + + gtk_transform_to_matrix (transform, &matrix); + } + + child = parse_node (p); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_transform_node_new (child, &matrix); + + gsk_render_node_unref (child); + } + else if (is_ident (p->cur, "color_matrix")) + { + double offset_values[4]; + graphene_matrix_t matrix; + graphene_vec4_t offset; + GskRenderNode *child; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_matrix_param (p, "matrix", &matrix); + parse_double4_param (p, "offset", offset_values); + + graphene_vec4_init (&offset, + offset_values[0], + offset_values[1], + offset_values[2], + offset_values[3]); + + child = parse_node (p); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_color_matrix_node_new (child, &matrix, &offset); + + gsk_render_node_unref (child); + } + else if (is_ident (p->cur, "texture")) + { + char *zero_terminated_data; + char *texture_data; + gsize texture_data_len; + GdkTexture *texture; + double bounds[4]; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_double4_param (p, "bounds", bounds); + + expect_skip (p, TOK_IDENT); + expect_skip (p, TOK_EQUALS); + expect (p, TOK_DATA); + + zero_terminated_data = g_strdup_printf ("%.*s", p->cur->len, p->cur->start); + texture_data = g_base64_decode (zero_terminated_data, &texture_data_len); + guchar data[] = {1, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 1}; + GBytes *b = g_bytes_new_static (data, 12); + + /* TODO: :( */ + texture = gdk_memory_texture_new (2, 2, GDK_MEMORY_R8G8B8, + b, 6); + + expect_skip (p, TOK_DATA); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_texture_node_new (texture, + &GRAPHENE_RECT_INIT ( + bounds[0], bounds[1], + bounds[2], bounds[3] + )); + } + else if (is_ident (p->cur, "inset_shadow")) + { + GskRoundedRect outline; + double color[4]; + float dx, dy, spread, blur_radius; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + parse_rounded_rect_param (p, "outline", &outline); + + parse_double4_param (p, "color", color); + dx = parse_float_param (p, "dx"); + dy = parse_float_param (p, "dy"); + spread = parse_float_param (p, "spread"); + blur_radius = parse_float_param (p, "blur_radius"); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_inset_shadow_node_new (&outline, + &(GdkRGBA) { color[0], color[1], color[2], color[3] }, + dx, dy, + spread, + blur_radius); + } + else if (is_ident (p->cur, "border")) + { + GskRoundedRect outline; + double widths[4]; + double colors[4][4]; + + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + parse_rounded_rect_param (p, "outline", &outline); + parse_double4_param (p, "widths", widths); + + expect_skip_ident (p, "colors"); + expect_skip (p, TOK_EQUALS); + + parse_double4 (p, colors[0]); + parse_double4 (p, colors[1]); + parse_double4 (p, colors[2]); + parse_double4 (p, colors[3]); + + expect_skip (p, TOK_CLOSE_BRACE); + result = gsk_border_node_new (&outline, + (float[4]) { widths[0], widths[1], widths[2], widths[3] }, + (GdkRGBA[4]) { + (GdkRGBA) { colors[0][0], colors[0][1], colors[0][2], colors[0][3] }, + (GdkRGBA) { colors[1][0], colors[1][1], colors[1][2], colors[1][3] }, + (GdkRGBA) { colors[2][0], colors[2][1], colors[2][2], colors[2][3] }, + (GdkRGBA) { colors[3][0], colors[3][1], colors[3][2], colors[3][3] }, + }); + } + else if (is_ident (p->cur, "text")) + { + skip (p); + expect_skip (p, TOK_OPEN_BRACE); + + expect_skip (p, TOK_CLOSE_BRACE); + + result = gsk_color_node_new ( + &(GdkRGBA) { 0, 1, 0, 1 }, + &GRAPHENE_RECT_INIT (0, 0, 0, 0)); + } + else + { + g_error ("Unknown render node type: %.*s", p->cur->len, p->cur->start); + } + + return result; +} + +/** + * All errors are fatal. + */ +GskRenderNode * +gsk_render_node_deserialize_from_string (const char *string) +{ + GskRenderNode *root = NULL; + Token *tokens; + int n_tokens; + Parser parser; + + tokens = tokenize (string, &n_tokens); + + parser_init (&parser, tokens, n_tokens); + root = parse_node (&parser); + + g_free (tokens); + + return root; +} + + +typedef struct +{ + int indentation_level; + GString *str; +} Printer; + +static void +printer_init (Printer *self) +{ + self->indentation_level = 0; + self->str = g_string_new (NULL); +} + +#define IDENT_LEVEL 2 /* Spaces per level */ +static void +_indent (Printer *self) +{ + if (self->indentation_level > 0) + g_string_append_printf (self->str, "%*s", self->indentation_level * IDENT_LEVEL, " "); +} +#undef IDENT + +static void +start_node (Printer *self, + const char *node_name) +{ + _indent (self); + g_string_append_printf (self->str, "%s {\n", node_name); + self->indentation_level ++; +} + +static void +end_node (Printer *self) +{ + self->indentation_level --; + _indent (self); + g_string_append (self->str, "}\n"); +} + +static void +append_rect (GString *str, + const graphene_rect_t *r) +{ + g_string_append_printf (str, "(%f, %f, %f, %f)", + r->origin.x, + r->origin.y, + r->size.width, + r->size.height); +} + +static void +append_rounded_rect (GString *str, + const GskRoundedRect *r) +{ + append_rect (str, &r->bounds); + + if (!gsk_rounded_rect_is_rectilinear (r)) + { + g_string_append_printf (str, " (%f, %f) (%f, %f) (%f, %f) (%f, %f)", + r->corner[0].width, + r->corner[0].height, + r->corner[1].width, + r->corner[1].height, + r->corner[2].width, + r->corner[2].height, + r->corner[3].width, + r->corner[3].height); + } +} + +static void +append_rgba (GString *str, + const GdkRGBA *rgba) +{ + g_string_append_printf (str, "(%f, %f, %f, %f)", + rgba->red, + rgba->green, + rgba->blue, + rgba->alpha); +} + +static void +append_point (GString *str, + const graphene_point_t *p) +{ + g_string_append_printf (str, "(%f, %f)", p->x, p->y); +} + +static void +append_matrix (GString *str, + const graphene_matrix_t *m) +{ + float v[16]; + + graphene_matrix_to_float (m, v); + + g_string_append_printf (str, "(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)", + v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], + v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); +} + +static void +append_vec4 (GString *str, + const graphene_vec4_t *v) +{ + g_string_append_printf (str, "(%f, %f, %f, %f)", + graphene_vec4_get_x (v), + graphene_vec4_get_y (v), + graphene_vec4_get_z (v), + graphene_vec4_get_w (v)); +} + +static void +append_float_param (Printer *p, + const char *param_name, + float value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = %f\n", param_name, value); +} + +static void +append_rgba_param (Printer *p, + const char *param_name, + const GdkRGBA *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_rgba (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +append_rect_param (Printer *p, + const char *param_name, + const graphene_rect_t *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_rect (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +append_rounded_rect_param (Printer *p, + const char *param_name, + const GskRoundedRect *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_rounded_rect (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +append_point_param (Printer *p, + const char *param_name, + const graphene_point_t *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_point (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +append_vec4_param (Printer *p, + const char *param_name, + const graphene_vec4_t *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_vec4 (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +append_matrix_param (Printer *p, + const char *param_name, + const graphene_matrix_t *value) +{ + _indent (p); + g_string_append_printf (p->str, "%s = ", param_name); + append_matrix (p->str, value); + g_string_append_c (p->str, '\n'); +} + +static void +render_node_print (Printer *p, + GskRenderNode *node) +{ + switch (gsk_render_node_get_node_type (node)) + { + case GSK_CONTAINER_NODE: + { + guint i; + + start_node (p, "container"); + for (i = 0; i < gsk_container_node_get_n_children (node); i ++) + { + GskRenderNode *child = gsk_container_node_get_child (node, i); + + render_node_print (p, child); + } + end_node (p); + } + break; + + case GSK_COLOR_NODE: + { + start_node (p, "color"); + append_rect_param (p, "bounds", &node->bounds); + append_rgba_param (p, "color", gsk_color_node_peek_color (node)); + end_node (p); + } + break; + + case GSK_CROSS_FADE_NODE: + { + start_node (p, "cross_fade"); + + append_float_param (p, "progress", gsk_cross_fade_node_get_progress (node)); + render_node_print (p, gsk_cross_fade_node_get_start_child (node)); + render_node_print (p, gsk_cross_fade_node_get_end_child (node)); + + end_node (p); + } + break; + + case GSK_LINEAR_GRADIENT_NODE: + { + int i; + + start_node (p, "linear_gradient"); + + append_rect_param (p, "bounds", &node->bounds); + append_point_param (p, "start", gsk_linear_gradient_node_peek_start (node)); + append_point_param (p, "end", gsk_linear_gradient_node_peek_end (node)); + + _indent (p); + g_string_append (p->str, "stops ="); + for (i = 0; i < gsk_linear_gradient_node_get_n_color_stops (node); i ++) + { + const GskColorStop *stop = gsk_linear_gradient_node_peek_color_stops (node) + i; + + g_string_append_printf (p->str, " (%f, ", stop->offset); + append_rgba (p->str, &stop->color); + g_string_append_c (p->str, ')'); + } + g_string_append (p->str, "\n"); + + end_node (p); + } + break; + + case GSK_REPEATING_LINEAR_GRADIENT_NODE: + { + g_error ("Add public api to access the repeating linear gradient node data"); + } + break; + + case GSK_OPACITY_NODE: + { + start_node (p, "opacity"); + + append_float_param (p, "opacity", gsk_opacity_node_get_opacity (node)); + render_node_print (p, gsk_opacity_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_OUTSET_SHADOW_NODE: + { + start_node (p, "outset_shadow"); + + append_rounded_rect_param (p, "outline", gsk_outset_shadow_node_peek_outline (node)); + append_rgba_param (p, "color", gsk_outset_shadow_node_peek_color (node)); + append_float_param (p, "dx", gsk_outset_shadow_node_get_dx (node)); + append_float_param (p, "dy", gsk_outset_shadow_node_get_dy (node)); + append_float_param (p, "spread", gsk_outset_shadow_node_get_spread (node)); + append_float_param (p, "blur_radius", gsk_outset_shadow_node_get_blur_radius (node)); + + end_node (p); + } + break; + + case GSK_CLIP_NODE: + { + start_node (p, "clip"); + + append_rect_param (p, "clip", gsk_clip_node_peek_clip (node)); + render_node_print (p, gsk_clip_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_ROUNDED_CLIP_NODE: + { + start_node (p, "rounded_clip"); + + append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_peek_clip (node)); + render_node_print (p, gsk_rounded_clip_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_TRANSFORM_NODE: + { + start_node (p, "transform"); + + append_matrix_param (p, "transform", gsk_transform_node_peek_transform (node)); + render_node_print (p, gsk_transform_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_COLOR_MATRIX_NODE: + { + start_node (p, "color_matrix"); + + append_matrix_param (p, "matrix", gsk_color_matrix_node_peek_color_matrix (node)); + append_vec4_param (p, "offset", gsk_color_matrix_node_peek_color_offset (node)); + render_node_print (p, gsk_color_matrix_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_BORDER_NODE: + { + start_node (p, "border"); + + append_rounded_rect_param (p, "outline", gsk_border_node_peek_outline (node)); + + _indent (p); + g_string_append (p->str, "widths = ("); + g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[0]); + g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[1]); + g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[2]); + g_string_append_printf (p->str, "%f", gsk_border_node_peek_widths (node)[3]); + g_string_append (p->str, ")\n"); + + _indent (p); + g_string_append (p->str, "colors = "); + append_rgba (p->str, &gsk_border_node_peek_colors (node)[0]); + g_string_append (p->str, " "); + append_rgba (p->str, &gsk_border_node_peek_colors (node)[1]); + g_string_append (p->str, " "); + append_rgba (p->str, &gsk_border_node_peek_colors (node)[2]); + g_string_append (p->str, " "); + append_rgba (p->str, &gsk_border_node_peek_colors (node)[3]); + g_string_append (p->str, "\n"); + + end_node (p); + } + break; + + case GSK_SHADOW_NODE: + { + int i; + + start_node (p, "shadow"); + + _indent (p); + g_string_append (p->str, "shadows = "); + for (i = 0; i < gsk_shadow_node_get_n_shadows (node); i ++) + { + const GskShadow *s = gsk_shadow_node_peek_shadow (node, i); + + g_string_append_printf (p->str, "(%f, (%f, %f), ", + s->radius, s->dx, s->dy); + append_rgba (p->str, &s->color); + g_string_append (p->str, ", "); + /* TODO: One too many commas */ + } + + end_node (p); + } + break; + + case GSK_INSET_SHADOW_NODE: + { + start_node (p, "inset_shadow"); + + append_rounded_rect_param (p, "outline", gsk_inset_shadow_node_peek_outline (node)); + append_rgba_param (p, "color", gsk_inset_shadow_node_peek_color (node)); + append_float_param (p, "dx", gsk_inset_shadow_node_get_dx (node)); + append_float_param (p, "dy", gsk_inset_shadow_node_get_dy (node)); + append_float_param (p, "spread", gsk_inset_shadow_node_get_spread (node)); + append_float_param (p, "blur_radius", gsk_inset_shadow_node_get_blur_radius (node)); + + end_node (p); + } + break; + + case GSK_TEXTURE_NODE: + { + GdkTexture *texture = gsk_texture_node_get_texture (node); + int stride; + int len; + guchar *data; + char *b64; + + start_node (p, "texture"); + append_rect_param (p, "bounds", &node->bounds); + + 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); + + _indent (p); + g_string_append_printf (p->str, "data = \"%s\"\n", b64); + end_node (p); + + g_free (b64); + g_free (data); + } + break; + + case GSK_CAIRO_NODE: + { + + /*start_node (p, "cairo");*/ + /*append_rect_param (p, "bounds", &node->bounds);*/ + /*g_string_append (p->str, "?????");*/ + /*end_node (p);*/ + } + break; + + case GSK_TEXT_NODE: + { + start_node (p, "text"); + + _indent (p); + g_string_append_printf (p->str, "font = \"%s\"\n", + pango_font_description_to_string (pango_font_describe ( + (PangoFont *)gsk_text_node_peek_font (node)))); + + append_float_param (p, "x", gsk_text_node_get_x (node)); + append_float_param (p, "y", gsk_text_node_get_y (node)); + append_rgba_param (p, "color", gsk_text_node_peek_color (node)); + + end_node (p); + } + break; + + case GSK_DEBUG_NODE: + { + start_node (p, "debug"); + + _indent (p); + /* TODO: We potentially need to escape certain characters in the message */ + g_string_append_printf (p->str, "message = \"%s\"\n", gsk_debug_node_get_message (node)); + + render_node_print (p, gsk_debug_node_get_child (node)); + end_node (p); + } + break; + + case GSK_BLUR_NODE: + { + start_node (p, "blur"); + + append_float_param (p, "radius", gsk_blur_node_get_radius (node)); + render_node_print (p, gsk_blur_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_REPEAT_NODE: + { + start_node (p, "repeat"); + append_rect_param (p, "bounds", &node->bounds); + append_rect_param (p, "child_bounds", gsk_repeat_node_peek_child_bounds (node)); + + render_node_print (p, gsk_repeat_node_get_child (node)); + + end_node (p); + } + break; + + case GSK_BLEND_NODE: + { + start_node (p, "blend"); + + _indent (p); + /* TODO: (de)serialize enums! */ + g_string_append_printf (p->str, "mode = %d\n", gsk_blend_node_get_blend_mode (node)); + render_node_print (p, gsk_blend_node_get_bottom_child (node)); + render_node_print (p, gsk_blend_node_get_top_child (node)); + + end_node (p); + } + break; + + case GSK_NOT_A_RENDER_NODE: + g_assert_not_reached (); + break; + + default: + g_error ("Unhandled node: %s", node->node_class->type_name); + } +} + +char * +gsk_render_node_serialize_to_string (GskRenderNode *root) +{ + Printer p; + + printer_init (&p); + render_node_print (&p, root); + + return g_string_free (p.str, FALSE); +} diff --git a/gsk/gskrendernodeparserprivate.h b/gsk/gskrendernodeparserprivate.h new file mode 100644 index 0000000000..d45b202ff1 --- /dev/null +++ b/gsk/gskrendernodeparserprivate.h @@ -0,0 +1,10 @@ + +#ifndef __GSK_RENDER_NODE_PARSER_PRIVATE_H__ +#define __GSK_RENDER_NODE_PARSER_PRIVATE_H__ + +#include "gskrendernode.h" + +GskRenderNode * gsk_render_node_deserialize_from_string (const char *string); +char * gsk_render_node_serialize_to_string (GskRenderNode *root); + +#endif diff --git a/gsk/meson.build b/gsk/meson.build index 46c578454b..14c58ecf2a 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -33,6 +33,7 @@ gsk_private_sources = files([ 'gskdebug.c', 'gskprivate.c', 'gskprofiler.c', + 'gskrendernodeparser.c', 'gl/gskshaderbuilder.c', 'gl/gskglprofiler.c', 'gl/gskglrenderer.c', |