Add a color state node

This commit is contained in:
Matthias Clasen 2024-06-08 19:55:22 -04:00
parent 5f706c73fd
commit 2dc85d7e5f
13 changed files with 480 additions and 3 deletions

View File

@ -117,6 +117,15 @@ should be aware that the allowed values are meant to be used on 3D transformatio
so their naming might appear awkward. However, it is always possible to use the
matrix3d() production to specify all 16 values individually.
### color-state
| property | syntax | default | printed |
| ----------- | ---------------- | ---------------------- | ----------- |
| bounds | `<rect>` | 50 | always |
| color-state | `<ident>|<url>` | srgb | always |
Creates a node like `gsk_color_state_node_new()` with the given properties.
### conic-gradient
| property | syntax | default | printed |

View File

@ -300,6 +300,7 @@ node_supports_2d_transform (const GskRenderNode *node)
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_SUBSURFACE_NODE:
case GSK_COLOR_STATE_NODE:
return TRUE;
case GSK_SHADOW_NODE:
@ -357,6 +358,7 @@ node_supports_transform (const GskRenderNode *node)
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_SUBSURFACE_NODE:
case GSK_COLOR_STATE_NODE:
return TRUE;
case GSK_SHADOW_NODE:
@ -4228,6 +4230,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_subsurface_node (job, node);
break;
case GSK_COLOR_STATE_NODE:
gsk_gl_render_job_visit_node (job, gsk_color_state_node_get_child (node));
break;
case GSK_NOT_A_RENDER_NODE:
default:
g_assert_not_reached ();

View File

@ -1164,6 +1164,7 @@ gsk_gpu_node_processor_ubershader_instead_of_offscreen (GskGpuNodeProcessor *sel
case GSK_BLEND_NODE:
case GSK_TEXT_NODE:
case GSK_MASK_NODE:
case GSK_COLOR_STATE_NODE:
return TRUE;
case GSK_CONTAINER_NODE:
@ -3801,6 +3802,23 @@ gsk_gpu_node_processor_create_subsurface_pattern (GskGpuPatternWriter *self,
return gsk_gpu_node_processor_create_node_pattern (self, gsk_subsurface_node_get_child (node));
}
static void
gsk_gpu_node_processor_add_color_state_node (GskGpuNodeProcessor *self,
GskRenderNode *node)
{
GskGpuImage *image;
GskRenderNode *child;
child = gsk_color_state_node_get_child (node);
image = gsk_gpu_node_processor_create_offscreen (self->frame,
&self->scale,
&node->bounds,
child);
gsk_gpu_node_processor_image_op (self, image, &node->bounds, &node->bounds);
}
static void
gsk_gpu_node_processor_add_container_node (GskGpuNodeProcessor *self,
GskRenderNode *node)
@ -4028,6 +4046,12 @@ static const struct
gsk_gpu_node_processor_add_subsurface_node,
gsk_gpu_node_processor_create_subsurface_pattern,
},
[GSK_COLOR_STATE_NODE] = {
0,
0,
gsk_gpu_node_processor_add_color_state_node,
NULL,
},
};
static void

View File

@ -93,6 +93,14 @@
* Since: 4.14
*/
/**
* GSK_COLOR_STATE_NODE:
*
* A node that changes the compositing color state.
*
* Since: 4.16
*/
typedef enum {
GSK_NOT_A_RENDER_NODE = 0,
GSK_CONTAINER_NODE,
@ -125,6 +133,7 @@ typedef enum {
GSK_FILL_NODE,
GSK_STROKE_NODE,
GSK_SUBSURFACE_NODE,
GSK_COLOR_STATE_NODE,
} GskRenderNodeType;
/**

View File

@ -619,6 +619,10 @@ complex_clip:
visit_node (self, gsk_debug_node_get_child (node));
break;
case GSK_COLOR_STATE_NODE:
visit_node (self, gsk_color_state_node_get_child (node));
break;
case GSK_SUBSURFACE_NODE:
{
GdkSubsurface *subsurface = gsk_subsurface_node_get_subsurface (node);

View File

@ -167,6 +167,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_MASK_NODE (gsk_mask_node_get_type())
#define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type())
#define GSK_TYPE_SUBSURFACE_NODE (gsk_subsurface_node_get_type())
#define GSK_TYPE_COLOR_STATE_NODE (gsk_color_state_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@ -198,6 +199,7 @@ typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskMaskNode GskMaskNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
typedef struct _GskSubsurfaceNode GskSubsurfaceNode;
typedef struct _GskColorStateNode GskColorStateNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@ -602,6 +604,16 @@ GskRenderNode * gsk_subsurface_node_get_child (const GskRender
GDK_AVAILABLE_IN_4_14
gpointer gsk_subsurface_node_get_subsurface (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_16
GType gsk_color_state_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_16
GskRenderNode * gsk_color_state_node_new (GskRenderNode *child,
GdkColorState *color_state);
GDK_AVAILABLE_IN_4_16
GskRenderNode * gsk_color_state_node_get_child (const GskRenderNode *node) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_16
GdkColorState * gsk_color_state_node_get_color_state (const GskRenderNode *node);
/**
* GSK_VALUE_HOLDS_RENDER_NODE:
* @value: a `GValue`

View File

@ -7171,6 +7171,160 @@ gsk_subsurface_node_get_subsurface (const GskRenderNode *node)
return self->subsurface;
}
/* }}} */
/* {{{ GSK_COLOR_STATE_NODE */
/**
* GskColorStateNode:
*
* A render node that changes the compositing color state for its children.
*
* Since: 4.16
*/
struct _GskColorStateNode
{
GskRenderNode render_node;
GskRenderNode *child;
GdkColorState *color_state;
};
static void
gsk_color_state_node_finalize (GskRenderNode *node)
{
GskColorStateNode *self = (GskColorStateNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_COLOR_STATE_NODE));
gsk_render_node_unref (self->child);
gdk_color_state_unref (self->color_state);
parent_class->finalize (node);
}
static void
gsk_color_state_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskColorStateNode *self = (GskColorStateNode *) node;
/* FIXME */
gsk_render_node_draw (self->child, cr);
}
static gboolean
gsk_color_state_node_can_diff (const GskRenderNode *node1,
const GskRenderNode *node2)
{
GskColorStateNode *self1 = (GskColorStateNode *) node1;
GskColorStateNode *self2 = (GskColorStateNode *) node2;
return gdk_color_state_equal (self1->color_state, self2->color_state);
}
static void
gsk_color_state_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
GskDiffData *data)
{
GskColorStateNode *self1 = (GskColorStateNode *) node1;
GskColorStateNode *self2 = (GskColorStateNode *) node2;
if (!gdk_color_state_equal (self1->color_state, self2->color_state))
{
/* Shouldn't happen, can_diff() avoids this, but to be sure */
gsk_render_node_diff_impossible (node1, node2, data);
}
else
{
gsk_render_node_diff (self1->child, self2->child, data);
}
}
static void
gsk_color_state_node_class_init (gpointer g_class,
gpointer class_data)
{
GskRenderNodeClass *node_class = g_class;
node_class->node_type = GSK_COLOR_STATE_NODE;
node_class->finalize = gsk_color_state_node_finalize;
node_class->draw = gsk_color_state_node_draw;
node_class->can_diff = gsk_color_state_node_can_diff;
node_class->diff = gsk_color_state_node_diff;
}
/**
* gsk_color_state_node_new: (skip)
* @child: The child to composite in the new color state
* @color_state: the color state to use
*
* Creates a `GskRenderNode` that will change the compositing
* color state for its children.
*
* Returns: (transfer full) (type GskColorStateNode): A new `GskRenderNode`
*
* Since: 4.16
*/
GskRenderNode *
gsk_color_state_node_new (GskRenderNode *child,
GdkColorState *color_state)
{
GskColorStateNode *self;
GskRenderNode *node;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
self = gsk_render_node_alloc (GSK_COLOR_STATE_NODE);
node = (GskRenderNode *) self;
node->offscreen_for_opacity = child->offscreen_for_opacity;
self->child = gsk_render_node_ref (child);
self->color_state = gdk_color_state_ref (color_state);
gsk_rect_init_from_rect (&node->bounds, &child->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
return node;
}
/**
* gsk_color_state_node_get_child:
* @node: (type GskColorStateNode): a debug `GskRenderNode`
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): the child `GskRenderNode`
*
* Since: 4.16
*/
GskRenderNode *
gsk_color_state_node_get_child (const GskRenderNode *node)
{
const GskColorStateNode *self = (const GskColorStateNode *) node;
return self->child;
}
/**
* gsk_color_state_node_get_color_state: (skip)
* @node: (type GskColorStateNode): a color state `GskRenderNode`
*
* Gets the color state that was set on this node.
*
* Returns: (transfer none): the color state
*
* Since: 4.16
*/
GdkColorState *
gsk_color_state_node_get_color_state (const GskRenderNode *node)
{
const GskColorStateNode *self = (const GskColorStateNode *) node;
return self->color_state;
}
/* }}} */
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
@ -7217,6 +7371,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_mask_node, GSK_MASK_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_subsurface_node, GSK_SUBSURFACE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_state_node, GSK_COLOR_STATE_NODE)
static void
gsk_render_node_init_types_once (void)
@ -7372,6 +7527,11 @@ gsk_render_node_init_types_once (void)
sizeof (GskSubsurfaceNode),
gsk_subsurface_node_class_init);
gsk_render_node_types[GSK_SUBSURFACE_NODE] = node_type;
node_type = gsk_render_node_type_register_static (I_("GskColorStateNode"),
sizeof (GskColorStateNode),
gsk_color_state_node_class_init);
gsk_render_node_types[GSK_COLOR_STATE_NODE] = node_type;
}
static void

View File

@ -35,6 +35,7 @@
#include "gdk/gdkrgbaprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkmemoryformatprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include <gtk/css/gtkcss.h>
#include "gtk/css/gtkcssdataurlprivate.h"
#include "gtk/css/gtkcssparserprivate.h"
@ -1477,6 +1478,101 @@ parse_color_node (GtkCssParser *parser,
return gsk_color_node_new (&color, &bounds);
}
static gboolean
parse_color_state (GtkCssParser *parser,
Context *context,
gpointer out_color_state)
{
GdkColorState *color_state = NULL;
if (gtk_css_parser_try_ident (parser, "srgb"))
color_state = gdk_color_state_get_srgb ();
else if (gtk_css_parser_try_ident (parser, "srgb-linear"))
color_state = gdk_color_state_get_srgb_linear ();
else if (gtk_css_parser_try_ident (parser, "hsl"))
color_state = gdk_color_state_get_hsl ();
else if (gtk_css_parser_try_ident (parser, "hwb"))
color_state = gdk_color_state_get_hwb ();
else if (gtk_css_parser_try_ident (parser, "oklab"))
color_state = gdk_color_state_get_oklab ();
else if (gtk_css_parser_try_ident (parser, "oklch"))
color_state = gdk_color_state_get_oklch ();
else if (gtk_css_parser_try_ident (parser, "xyz"))
color_state = gdk_color_state_get_xyz ();
else if (gtk_css_parser_try_ident (parser, "display-p3"))
color_state = gdk_color_state_get_display_p3 ();
else if (gtk_css_parser_try_ident (parser, "rec2020"))
color_state = gdk_color_state_get_rec2020 ();
else if (gtk_css_parser_try_ident (parser, "rec2100-pq"))
color_state = gdk_color_state_get_rec2100_pq ();
else if (gtk_css_parser_try_ident (parser, "rec2100-linear"))
color_state = gdk_color_state_get_rec2100_linear ();
else
{
char *url, *scheme, *mimetype;
GBytes *bytes;
GError *error = NULL;
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)
{
g_free (scheme);
g_free (url);
return FALSE;
}
g_free (scheme);
bytes = gtk_css_data_url_parse (url, &mimetype, &error);
if (!bytes)
{
gtk_css_parser_error_value (parser, "Could not parse data url: %s", error->message);
g_error_free (error);
g_free (mimetype);
return FALSE;
}
g_free (url);
if (g_strcmp0 (mimetype, "application/vnd.iccprofile") != 0)
{
gtk_css_parser_error_value (parser, "Wrong mimetype for color-state: %s", mimetype);
g_free (mimetype);
g_bytes_unref (bytes);
return FALSE;
}
g_free (mimetype);
color_state = gdk_color_state_new_from_icc_profile (bytes, &error);
if (!color_state)
{
gtk_css_parser_error_value (parser, "Invalid ICC profile: %s", error->message);
g_error_free (error);
g_bytes_unref (bytes);
return FALSE;
}
g_bytes_unref (bytes);
}
if (color_state == NULL)
{
gtk_css_parser_error_value (parser, "Invalid color-state");
return FALSE;
}
*((GdkColorState **) out_color_state) = color_state;
return TRUE;
}
static void
clear_color_state (gpointer inout_color_state)
{
g_clear_pointer ((GdkColorState **) inout_color_state, gdk_color_state_unref);
}
static GskRenderNode *
parse_linear_gradient_node_internal (GtkCssParser *parser,
Context *context,
@ -2738,6 +2834,33 @@ parse_subsurface_node (GtkCssParser *parser,
return result;
}
static GskRenderNode *
parse_color_state_node (GtkCssParser *parser,
Context *context)
{
GskRenderNode *child = NULL;
GdkColorState *color_state = NULL;
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "color-state", parse_color_state, clear_color_state, &color_state },
};
GskRenderNode *result;
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
if (child == NULL)
child = create_default_render_node ();
if (color_state == NULL)
color_state = gdk_color_state_get_srgb ();
result = gsk_color_state_node_new (child, color_state);
gsk_render_node_unref (child);
gdk_color_state_unref (color_state);
return result;
}
static gboolean
parse_node (GtkCssParser *parser,
Context *context,
@ -2777,6 +2900,7 @@ parse_node (GtkCssParser *parser,
{ "glshader", parse_glshader_node },
{ "mask", parse_mask_node },
{ "subsurface", parse_subsurface_node },
{ "color-state", parse_color_state_node },
};
GskRenderNode **node_p = out_node;
const GtkCssToken *token;
@ -3132,6 +3256,10 @@ printer_init_duplicates_for_node (Printer *printer,
printer_init_duplicates_for_node (printer, gsk_subsurface_node_get_child (node));
break;
case GSK_COLOR_STATE_NODE:
printer_init_duplicates_for_node (printer, gsk_color_state_node_get_child (node));
break;
default:
case GSK_NOT_A_RENDER_NODE:
g_assert_not_reached ();
@ -3866,6 +3994,40 @@ append_dash_param (Printer *p,
g_string_append (p->str, ";\n");
}
static void
color_state_serialize (GdkColorState *cs,
Printer *p)
{
if (GDK_IS_NAMED_COLOR_STATE (cs))
{
g_string_append_printf (p->str, "%s", gdk_color_state_get_name (cs));
return;
}
else if (GDK_IS_LCMS_COLOR_STATE (cs))
{
GBytes *bytes;
bytes = gdk_color_state_save_to_icc_profile (cs, NULL);
if (bytes)
{
const guchar *data;
gsize len;
char *b64;
g_string_append (p->str, "url(\"data:application/vnc.iccprofile;base64,\\\n");
data = g_bytes_get_data (bytes, &len);
b64 = base64_encode_with_linebreaks (data, len);
append_escaping_newlines (p->str, b64);
g_free (b64);
g_string_append (p->str, "\")");
g_bytes_unref (bytes);
return;
}
}
g_string_append (p->str, "srgb");
}
static void
render_node_print (Printer *p,
GskRenderNode *node)
@ -4557,6 +4719,21 @@ render_node_print (Printer *p,
}
break;
case GSK_COLOR_STATE_NODE:
{
start_node (p, "color-state", node_name);
_indent (p);
g_string_append (p->str, "color-state: ");
color_state_serialize (gsk_color_state_node_get_color_state (node), p);
g_string_append (p->str, ";\n");
append_node_param (p, "child", gsk_color_state_node_get_child (node));
end_node (p);
}
break;
default:
g_error ("Unhandled node: %s", g_type_name_from_instance ((GTypeInstance *) node));
break;

View File

@ -14,7 +14,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_SUBSURFACE_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_COLOR_STATE_NODE + 1)
extern GType gsk_render_node_types[];

View File

@ -59,6 +59,7 @@
#include "gtk/gtkdebug.h"
#include "gtk/gtkbuiltiniconprivate.h"
#include "gtk/gtkrendernodepaintableprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include "recording.h"
#include "renderrecording.h"
@ -373,6 +374,9 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_SUBSURFACE_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_subsurface_node_get_child (node) }, 1);
case GSK_COLOR_STATE_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_color_state_node_get_child (node) }, 1);
}
}
@ -461,6 +465,8 @@ node_type_name (GskRenderNodeType type)
return "GL Shader";
case GSK_SUBSURFACE_NODE:
return "Subsurface";
case GSK_COLOR_STATE_NODE:
return "Color State";
}
}
@ -498,6 +504,7 @@ node_name (GskRenderNode *node)
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
case GSK_SUBSURFACE_NODE:
case GSK_COLOR_STATE_NODE:
return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
case GSK_DEBUG_NODE:
@ -1530,6 +1537,14 @@ populate_render_node_properties (GListStore *store,
}
break;
case GSK_COLOR_STATE_NODE:
{
GdkColorState *color_state = gsk_color_state_node_get_color_state (node);
add_text_row (store, "Color State", "%s", gdk_color_state_get_name (color_state));
}
break;
case GSK_NOT_A_RENDER_NODE:
default:
break;

View File

@ -203,6 +203,12 @@ node_attach (const GskRenderNode *node,
return res;
}
case GSK_COLOR_STATE_NODE:
child = node_attach (gsk_color_state_node_get_child (node), surface, idx);
res = gsk_color_state_node_new (child, gsk_color_state_node_get_color_state (node));
gsk_render_node_unref (child);
return res;
case GSK_NOT_A_RENDER_NODE:
default:
g_assert_not_reached ();

View File

@ -34,6 +34,7 @@ static char *directory = NULL;
static guint texture_count;
static guint font_count;
static guint icc_profile_count;
static GHashTable *fonts;
@ -155,7 +156,52 @@ extract_font (GskRenderNode *node)
font_count++;
}
#define N_NODE_TYPES (GSK_SUBSURFACE_NODE + 1)
static void
extract_icc_profile (GskRenderNode *node)
{
GdkColorState *cs;
GBytes *bytes;
const gchar *data;
gsize len;
char *filename;
char *path;
cs = gsk_color_state_node_get_color_state (node);
bytes = gdk_color_state_save_to_icc_profile (cs, NULL);
if (bytes == NULL)
return;
do {
filename = g_strdup_printf ("gtk-icc-profile-%u.ttf", icc_profile_count);
path = g_build_path ("/", directory, filename, NULL);
if (!g_file_test (path, G_FILE_TEST_EXISTS))
break;
g_free (path);
g_free (filename);
icc_profile_count++;
} while (TRUE);
if (verbose)
g_print ("Writing ICC profile to %s\n", filename);
data = g_bytes_get_data (bytes, &len);
if (!g_file_set_contents (path, data, len, NULL))
{
g_printerr (_("Failed to write %s\n"), filename);
}
g_bytes_unref (bytes);
g_free (path);
g_free (filename);
icc_profile_count++;
}
#define N_NODE_TYPES (GSK_COLOR_STATE_NODE + 1)
static void
extract_from_node (GskRenderNode *node)
{
@ -257,6 +303,11 @@ extract_from_node (GskRenderNode *node)
extract_from_node (gsk_subsurface_node_get_child (node));
break;
case GSK_COLOR_STATE_NODE:
extract_icc_profile (node);
extract_from_node (gsk_color_state_node_get_child (node));
break;
case GSK_NOT_A_RENDER_NODE:
default:
g_assert_not_reached ();

View File

@ -29,7 +29,7 @@
#include <gtk/gtk.h>
#include "gtk-rendernode-tool.h"
#define N_NODE_TYPES (GSK_SUBSURFACE_NODE + 1)
#define N_NODE_TYPES (GSK_COLOR_STATE_NODE + 1)
static void
count_nodes (GskRenderNode *node,
unsigned int *counts,
@ -144,6 +144,10 @@ count_nodes (GskRenderNode *node,
count_nodes (gsk_subsurface_node_get_child (node), counts, &d);
break;
case GSK_COLOR_STATE_NODE:
count_nodes (gsk_color_state_node_get_child (node), counts, &d);
break;
case GSK_NOT_A_RENDER_NODE:
default:
g_assert_not_reached ();