Merge branch 'css-color-hookup-4' into 'main'

Make non-srgb css colors work for shadows

See merge request GNOME/gtk!7557
This commit is contained in:
Benjamin Otte 2024-08-10 00:36:28 +00:00
commit 4426194ee6
17 changed files with 350 additions and 162 deletions

View File

@ -56,18 +56,22 @@ static const GskGpuShaderOpClass GSK_GPU_BLUR_OP_CLASS = {
static void
gsk_gpu_blur_op_full (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
guint32 variation,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
guint32 variation,
const GskGpuShaderImage *image,
const graphene_vec2_t *blur_direction,
float blur_color[4])
const GdkColor *blur_color)
{
GskGpuBlurInstance *instance;
GdkColorState *alt;
alt = gsk_gpu_color_states_find (ccs, blur_color);
gsk_gpu_shader_op_alloc (frame,
&GSK_GPU_BLUR_OP_CLASS,
color_states,
gsk_gpu_color_states_create (ccs, TRUE, alt, variation & VARIATION_COLORIZE ? FALSE : TRUE),
variation,
clip,
(GskGpuImage *[1]) { image->image },
@ -77,41 +81,49 @@ gsk_gpu_blur_op_full (GskGpuFrame *frame,
gsk_gpu_rect_to_float (image->coverage, offset, instance->rect);
gsk_gpu_rect_to_float (image->bounds, offset, instance->tex_rect);
graphene_vec2_to_float (blur_direction, instance->blur_direction);
gsk_gpu_vec4_to_float (blur_color, instance->blur_color);
gsk_gpu_color_to_float (blur_color, alt, opacity, instance->blur_color);
}
void
gsk_gpu_blur_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const graphene_vec2_t *blur_direction)
{
GdkColor blur_color;
gdk_color_init (&blur_color, ccs, (float[]) { 1, 1, 1, 1 });
gsk_gpu_blur_op_full (frame,
clip,
color_states,
0,
ccs,
opacity,
offset,
0,
image,
blur_direction,
(float[4]) { 1, 1, 1, 1 });
&blur_color);
gdk_color_finish (&blur_color);
}
void
gsk_gpu_blur_shadow_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const graphene_vec2_t *blur_direction,
float shadow_color[4])
const GdkColor *shadow_color)
{
gsk_gpu_blur_op_full (frame,
clip,
color_states,
VARIATION_COLORIZE,
ccs,
opacity,
offset,
VARIATION_COLORIZE,
image,
blur_direction,
shadow_color);

View File

@ -8,18 +8,20 @@ G_BEGIN_DECLS
void gsk_gpu_blur_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const graphene_vec2_t *blur_direction);
void gsk_gpu_blur_shadow_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const graphene_vec2_t *blur_direction,
float shadow_color[4]);
const GdkColor *shadow_color);
G_END_DECLS

View File

@ -52,16 +52,20 @@ static const GskGpuShaderOpClass GSK_GPU_COLORIZE_OP_CLASS = {
void
gsk_gpu_colorize_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const float color[4])
const GdkColor *color)
{
GskGpuColorizeInstance *instance;
GdkColorState *alt;
alt = gsk_gpu_color_states_find (ccs, color);
gsk_gpu_shader_op_alloc (frame,
&GSK_GPU_COLORIZE_OP_CLASS,
color_states,
gsk_gpu_color_states_create (ccs, TRUE, alt, FALSE),
0,
clip,
(GskGpuImage *[1]) { image->image },
@ -70,5 +74,5 @@ gsk_gpu_colorize_op (GskGpuFrame *frame,
gsk_gpu_rect_to_float (image->coverage ? image->coverage : image->bounds, offset, instance->rect);
gsk_gpu_rect_to_float (image->bounds, offset, instance->tex_rect);
gsk_gpu_vec4_to_float (color, instance->color);
gsk_gpu_color_to_float (color, alt, opacity, instance->color);
}

View File

@ -8,10 +8,11 @@ G_BEGIN_DECLS
void gsk_gpu_colorize_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuColorStates color_states,
GdkColorState *ccs,
float opacity,
const graphene_point_t *offset,
const GskGpuShaderImage *image,
const float color[4]);
const GdkColor *color);
G_END_DECLS

View File

@ -265,12 +265,6 @@ gsk_gpu_node_processor_color_states_explicit (GskGpuNodeProcessor *self,
alt_premultiplied);
}
static inline GskGpuColorStates
gsk_gpu_node_processor_color_states_self (GskGpuNodeProcessor *self)
{
return gsk_gpu_color_states_create_equal (TRUE, TRUE);
}
static inline GskGpuColorStates
gsk_gpu_node_processor_color_states_for_rgba (GskGpuNodeProcessor *self)
{
@ -833,7 +827,7 @@ gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
const graphene_rect_t *rect,
const graphene_point_t *shadow_offset,
float blur_radius,
const GdkRGBA *shadow_color,
const GdkColor *shadow_color,
GskGpuImage *source_image,
GdkMemoryDepth source_depth,
const graphene_rect_t *source_rect)
@ -869,7 +863,8 @@ gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
graphene_vec2_init (&direction, blur_radius, 0.0f);
gsk_gpu_blur_op (other.frame,
gsk_gpu_clip_get_shader_clip (&other.clip, &other.offset, &intermediate_rect),
gsk_gpu_node_processor_color_states_self (&other),
other.ccs,
1,
&other.offset,
&(GskGpuShaderImage) {
source_image,
@ -888,7 +883,8 @@ gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
{
gsk_gpu_blur_shadow_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &real_offset, rect),
gsk_gpu_node_processor_color_states_for_rgba (self),
self->ccs,
1,
&real_offset,
&(GskGpuShaderImage) {
intermediate,
@ -897,13 +893,14 @@ gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
&intermediate_rect,
},
&direction,
GSK_RGBA_TO_VEC4 (shadow_color));
shadow_color);
}
else
{
gsk_gpu_blur_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &real_offset, rect),
gsk_gpu_node_processor_color_states_self (self),
self->ccs,
1,
&real_offset,
&(GskGpuShaderImage) {
intermediate,
@ -2635,14 +2632,17 @@ gsk_gpu_node_processor_add_shadow_node (GskGpuNodeProcessor *self,
for (i = 0; i < n_shadows; i++)
{
const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i);
const GskShadow2 *shadow = gsk_shadow_node_get_shadow2 (node, i);
if (shadow->radius == 0)
{
graphene_point_t shadow_offset = GRAPHENE_POINT_INIT (self->offset.x + shadow->dx,
self->offset.y + shadow->dy);
graphene_point_t shadow_offset = GRAPHENE_POINT_INIT (self->offset.x + shadow->offset.x,
self->offset.y + shadow->offset.y);
gsk_gpu_colorize_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &shadow_offset, &child->bounds),
gsk_gpu_node_processor_color_states_for_rgba (self),
self->ccs,
1,
&shadow_offset,
&(GskGpuShaderImage) {
image,
@ -2650,7 +2650,7 @@ gsk_gpu_node_processor_add_shadow_node (GskGpuNodeProcessor *self,
&child->bounds,
&tex_rect,
},
GSK_RGBA_TO_VEC4 (&shadow->color));
&shadow->color);
}
else
{
@ -2659,7 +2659,7 @@ gsk_gpu_node_processor_add_shadow_node (GskGpuNodeProcessor *self,
graphene_rect_inset_r (&child->bounds, - clip_radius, - clip_radius, &bounds);
gsk_gpu_node_processor_blur_op (self,
&bounds,
&GRAPHENE_POINT_INIT (shadow->dx, shadow->dy),
&shadow->offset,
shadow->radius,
&shadow->color,
image,
@ -2871,14 +2871,10 @@ gsk_gpu_node_processor_add_mask_node (GskGpuNodeProcessor *self,
if (gsk_render_node_get_node_type (source_child) == GSK_COLOR_NODE &&
mask_mode == GSK_MASK_MODE_ALPHA)
{
float color[4];
gdk_color_to_float (gsk_color_node_get_color2 (source_child), self->ccs, color);
color[3] *= self->opacity;
gsk_gpu_colorize_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
gsk_gpu_node_processor_color_states_explicit (self, self->ccs, FALSE),
self->ccs,
self->opacity,
&self->offset,
&(GskGpuShaderImage) {
mask_image,
@ -2886,7 +2882,7 @@ gsk_gpu_node_processor_add_mask_node (GskGpuNodeProcessor *self,
&node->bounds,
&mask_rect,
},
color);
gsk_color_node_get_color2 (source_child));
}
else
{
@ -2938,7 +2934,7 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
graphene_point_t offset;
guint i, num_glyphs;
float scale;
GdkRGBA color;
GdkColor color;
float align_scale_x, align_scale_y;
float inv_align_scale_x, inv_align_scale_y;
unsigned int flags_mask;
@ -2954,8 +2950,7 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
cache = gsk_gpu_device_get_cache (gsk_gpu_frame_get_device (self->frame));
color = *gsk_text_node_get_color (node);
color.alpha *= self->opacity;
gdk_color_init_from_rgba (&color, gsk_text_node_get_color (node));
num_glyphs = gsk_text_node_get_num_glyphs (node);
glyphs = gsk_text_node_get_glyphs (node, NULL);
font = gsk_text_node_get_font (node);
@ -3033,7 +3028,8 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
else
gsk_gpu_colorize_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &glyph_origin, &glyph_bounds),
gsk_gpu_node_processor_color_states_for_rgba (self),
self->ccs,
self->opacity,
&glyph_origin,
&(GskGpuShaderImage) {
image,
@ -3041,10 +3037,12 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
&glyph_bounds,
&glyph_tex_rect
},
GSK_RGBA_TO_VEC4 (&color));
&color);
offset.x += glyphs[i].geometry.width * inv_pango_scale;
}
gdk_color_finish (&color);
}
static void

View File

@ -5565,7 +5565,9 @@ struct _GskShadowNode
GskRenderNode *child;
gsize n_shadows;
GskShadow *shadows;
GskShadow2 *shadows;
GskShadow *rgba_shadows;
};
static void
@ -5575,8 +5577,13 @@ gsk_shadow_node_finalize (GskRenderNode *node)
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_SHADOW_NODE));
gsk_render_node_unref (self->child);
for (gsize i = 0; i < self->n_shadows; i++)
gdk_color_finish (&self->shadows[i].color);
g_free (self->shadows);
g_free (self->rgba_shadows);
parent_class->finalize (node);
}
@ -5596,31 +5603,28 @@ gsk_shadow_node_draw (GskRenderNode *node,
for (i = 0; i < self->n_shadows; i++)
{
GskShadow *shadow = &self->shadows[i];
GskShadow2 *shadow = &self->shadows[i];
cairo_pattern_t *pattern;
GdkColor color;
/* We don't need to draw invisible shadows */
if (gdk_rgba_is_clear (&shadow->color))
if (gdk_color_is_clear (&shadow->color))
continue;
cairo_save (cr);
cr = gsk_cairo_blur_start_drawing (cr, 0.5 * shadow->radius, GSK_BLUR_X | GSK_BLUR_Y);
cairo_save (cr);
cairo_translate (cr, shadow->dx, shadow->dy);
cairo_translate (cr, shadow->offset.x, shadow->offset.y);
cairo_push_group (cr);
gsk_render_node_draw_ccs (self->child, cr, ccs);
pattern = cairo_pop_group (cr);
cairo_reset_clip (cr);
gdk_cairo_set_source_rgba_ccs (cr, ccs, &shadow->color);
gdk_cairo_set_source_color (cr, ccs, &shadow->color);
cairo_mask (cr, pattern);
cairo_pattern_destroy (pattern);
cairo_restore (cr);
gdk_color_init_from_rgba (&color, &shadow->color);
cr = gsk_cairo_blur_finish_drawing (cr, ccs, 0.5 * shadow->radius, &color, GSK_BLUR_X | GSK_BLUR_Y);
gdk_color_finish (&color);
cr = gsk_cairo_blur_finish_drawing (cr, ccs, 0.5 * shadow->radius, &shadow->color, GSK_BLUR_X | GSK_BLUR_Y);
cairo_restore (cr);
}
@ -5647,13 +5651,12 @@ gsk_shadow_node_diff (GskRenderNode *node1,
for (i = 0; i < self1->n_shadows; i++)
{
GskShadow *shadow1 = &self1->shadows[i];
GskShadow *shadow2 = &self2->shadows[i];
GskShadow2 *shadow1 = &self1->shadows[i];
GskShadow2 *shadow2 = &self2->shadows[i];
float clip_radius;
if (!gdk_rgba_equal (&shadow1->color, &shadow2->color) ||
shadow1->dx != shadow2->dx ||
shadow1->dy != shadow2->dy ||
if (!gdk_color_equal (&shadow1->color, &shadow2->color) ||
!graphene_point_equal (&shadow1->offset, &shadow2->offset) ||
shadow1->radius != shadow2->radius)
{
gsk_render_node_diff_impossible (node1, node2, data);
@ -5661,10 +5664,10 @@ gsk_shadow_node_diff (GskRenderNode *node1,
}
clip_radius = gsk_cairo_blur_compute_pixels (shadow1->radius / 2.0);
top = MAX (top, ceil (clip_radius - shadow1->dy));
right = MAX (right, ceil (clip_radius + shadow1->dx));
bottom = MAX (bottom, ceil (clip_radius + shadow1->dy));
left = MAX (left, ceil (clip_radius - shadow1->dx));
top = MAX (top, ceil (clip_radius - shadow1->offset.y));
right = MAX (right, ceil (clip_radius + shadow1->offset.x));
bottom = MAX (bottom, ceil (clip_radius + shadow1->offset.y));
left = MAX (left, ceil (clip_radius - shadow1->offset.x));
}
sub = cairo_region_create ();
@ -5695,10 +5698,10 @@ gsk_shadow_node_get_bounds (GskShadowNode *self,
for (i = 0; i < self->n_shadows; i++)
{
float clip_radius = gsk_cairo_blur_compute_pixels (self->shadows[i].radius / 2.0);
top = MAX (top, clip_radius - self->shadows[i].dy);
right = MAX (right, clip_radius + self->shadows[i].dx);
bottom = MAX (bottom, clip_radius + self->shadows[i].dy);
left = MAX (left, clip_radius - self->shadows[i].dx);
top = MAX (top, clip_radius - self->shadows[i].offset.y);
right = MAX (right, clip_radius + self->shadows[i].offset.x);
bottom = MAX (bottom, clip_radius + self->shadows[i].offset.y);
left = MAX (left, clip_radius - self->shadows[i].offset.x);
}
bounds->origin.x -= left;
@ -5735,10 +5738,52 @@ GskRenderNode *
gsk_shadow_node_new (GskRenderNode *child,
const GskShadow *shadows,
gsize n_shadows)
{
GskShadow2 *shadows2;
GskRenderNode *node;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (shadows != NULL, NULL);
g_return_val_if_fail (n_shadows > 0, NULL);
shadows2 = g_new (GskShadow2, n_shadows);
for (gsize i = 0; i < n_shadows; i++)
{
gdk_color_init_from_rgba (&shadows2[i].color, &shadows[i].color);
graphene_point_init (&shadows2[i].offset, shadows[i].dx, shadows[i].dy);
shadows2[i].radius = shadows[i].radius;
}
node = gsk_shadow_node_new2 (child, shadows2, n_shadows);
for (gsize i = 0; i < n_shadows; i++)
gdk_color_finish (&shadows2[i].color);
g_free (shadows2);
return node;
}
/*< private >
* gsk_shadow_node_new2:
* @child: The node to draw
* @shadows: (array length=n_shadows): The shadows to apply
* @n_shadows: number of entries in the @shadows array
*
* Creates a `GskRenderNode` that will draw a @child with the given
* @shadows below it.
*
* Returns: (transfer full) (type GskShadowNode): A new `GskRenderNode`
*/
GskRenderNode *
gsk_shadow_node_new2 (GskRenderNode *child,
const GskShadow2 *shadows,
gsize n_shadows)
{
GskShadowNode *self;
GskRenderNode *node;
gsize i;
GdkMemoryDepth depth;
gboolean is_hdr;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (shadows != NULL, NULL);
@ -5750,19 +5795,25 @@ gsk_shadow_node_new (GskRenderNode *child,
self->child = gsk_render_node_ref (child);
self->n_shadows = n_shadows;
self->shadows = g_malloc_n (n_shadows, sizeof (GskShadow));
memcpy (self->shadows, shadows, n_shadows * sizeof (GskShadow));
self->shadows = g_new (GskShadow2, n_shadows);
gsk_shadow_node_get_bounds (self, &node->bounds);
depth = gsk_render_node_get_preferred_depth (child);
is_hdr = gsk_render_node_is_hdr (child);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
for (i = 0; i < n_shadows; i++)
{
node->preferred_depth = gdk_memory_depth_merge (node->preferred_depth,
my_color_get_depth (&shadows->color));
gdk_color_init_copy (&self->shadows[i].color, &shadows[i].color);
graphene_point_init_from_point (&self->shadows[i].offset, &shadows[i].offset);
self->shadows[i].radius = shadows[i].radius;
depth = gdk_memory_depth_merge (depth, gdk_color_get_depth (&shadows[i].color));
is_hdr = is_hdr || color_state_is_hdr (shadows[i].color.color_state);
}
node->preferred_depth = depth;
node->is_hdr = is_hdr;
gsk_shadow_node_get_bounds (self, &node->bounds);
return node;
}
@ -5794,6 +5845,45 @@ gsk_shadow_node_get_child (const GskRenderNode *node)
const GskShadow *
gsk_shadow_node_get_shadow (const GskRenderNode *node,
gsize i)
{
GskShadowNode *self = (GskShadowNode *) node;
const GskShadow *shadow;
G_LOCK (rgba);
if (self->rgba_shadows == NULL)
{
self->rgba_shadows = g_new (GskShadow, self->n_shadows);
for (gsize j = 0; j < self->n_shadows; j++)
{
gdk_color_to_float (&self->shadows[j].color,
GDK_COLOR_STATE_SRGB,
(float *) &self->rgba_shadows[j].color);
self->rgba_shadows[j].dx = self->shadows[j].offset.x;
self->rgba_shadows[j].dy = self->shadows[j].offset.y;
self->rgba_shadows[j].radius = self->shadows[j].radius;
}
}
shadow = &self->rgba_shadows[i];
G_UNLOCK (rgba);
return shadow;
}
/*< private >
* gsk_shadow_node_get_shadow2:
* @node: (type GskShadowNode): a shadow `GskRenderNode`
* @i: the given index
*
* Retrieves the shadow data at the given index @i.
*
* Returns: (transfer none): the shadow data
*/
const GskShadow2 *
gsk_shadow_node_get_shadow2 (const GskRenderNode *node,
gsize i)
{
const GskShadowNode *self = (const GskShadowNode *) node;

View File

@ -693,6 +693,10 @@ parse_float4 (GtkCssParser *parser,
return TRUE;
}
static gboolean parse_color2 (GtkCssParser *parser,
Context *context,
gpointer color);
static gboolean
parse_shadows (GtkCssParser *parser,
Context *context,
@ -702,10 +706,11 @@ parse_shadows (GtkCssParser *parser,
do
{
GskShadow shadow = { GDK_RGBA("000000"), 0, 0, 0 };
GskShadow2 shadow;
GdkColor color = GDK_COLOR_SRGB (0, 0, 0, 1);
double dx = 0, dy = 0, radius = 0;
if (!gdk_rgba_parser_parse (parser, &shadow.color))
if (!parse_color2 (parser, context, &color))
gtk_css_parser_error_value (parser, "Expected shadow color");
if (!gtk_css_parser_consume_number (parser, &dx))
@ -720,11 +725,13 @@ parse_shadows (GtkCssParser *parser,
gtk_css_parser_error_value (parser, "Expected shadow blur radius");
}
shadow.dx = dx;
shadow.dy = dy;
gdk_color_init_copy (&shadow.color, &color);
graphene_point_init (&shadow.offset, dx, dy);
shadow.radius = radius;
g_array_append_val (shadows, shadow);
gdk_color_finish (&color);
}
while (gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA));
@ -734,7 +741,15 @@ parse_shadows (GtkCssParser *parser,
static void
clear_shadows (gpointer inout_shadows)
{
g_array_set_size (inout_shadows, 0);
GArray *shadows = inout_shadows;
for (gsize i = 0; i < shadows->len; i++)
{
GskShadow2 *shadow = &g_array_index (shadows, GskShadow2, i);
gdk_color_finish (&shadow->color);
}
g_array_set_size (shadows, 0);
}
static const struct
@ -1437,12 +1452,6 @@ create_default_path (void)
return gsk_path_builder_free_to_path (builder);
}
typedef struct
{
GdkColorState *color_state;
float values[4];
} Color;
static gboolean
parse_cicp_range (GtkCssParser *parser,
Context *context,
@ -1578,35 +1587,6 @@ parse_color_state (GtkCssParser *parser,
return TRUE;
}
static gboolean
gtk_css_parser_consume_number_or_percentage (GtkCssParser *parser,
double min,
double max,
double *value)
{
if (gtk_css_parser_has_percentage (parser))
{
double number;
gtk_css_parser_consume_percentage (parser, &number);
*value = min + (number / 100.0) * (max - min);
return TRUE;
}
else if (gtk_css_parser_has_number (parser))
{
double number;
gtk_css_parser_consume_number (parser, &number);
*value = number;
return TRUE;
}
else
{
gtk_css_parser_error_syntax (parser, "Expected a number or percentage");
return FALSE;
}
}
typedef struct {
Context *context;
GdkColor *color;
@ -2935,10 +2915,10 @@ parse_shadow_node (GtkCssParser *parser,
Context *context)
{
GskRenderNode *child = NULL;
GArray *shadows = g_array_new (FALSE, TRUE, sizeof (GskShadow));
GArray *shadows = g_array_new (FALSE, TRUE, sizeof (GskShadow2));
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "shadows", parse_shadows, clear_shadows, shadows }
{ "shadows", parse_shadows, clear_shadows, shadows },
};
GskRenderNode *result;
@ -2948,12 +2928,13 @@ parse_shadow_node (GtkCssParser *parser,
if (shadows->len == 0)
{
GskShadow default_shadow = { GDK_RGBA("000000"), 1, 1, 0 };
GskShadow2 default_shadow = { GDK_COLOR_SRGB (0, 0, 0, 1), GRAPHENE_POINT_INIT (1, 1), 0 };
g_array_append_val (shadows, default_shadow);
}
result = gsk_shadow_node_new (child, (GskShadow *)shadows->data, shadows->len);
result = gsk_shadow_node_new2 (child, (GskShadow2 *)shadows->data, shadows->len);
clear_shadows (shadows);
g_array_free (shadows, TRUE);
gsk_render_node_unref (child);
@ -3389,6 +3370,11 @@ printer_init_duplicates_for_node (Printer *printer,
case GSK_SHADOW_NODE:
printer_init_duplicates_for_node (printer, gsk_shadow_node_get_child (node));
for (int i = 0; i < gsk_shadow_node_get_n_shadows (node); i++)
{
const GskShadow2 * shadow = gsk_shadow_node_get_shadow2 (node, i);
printer_init_check_color_state (printer, shadow->color.color_state);
}
break;
case GSK_DEBUG_NODE:
@ -4522,25 +4508,21 @@ render_node_print (Printer *p,
g_string_append (p->str, "shadows: ");
for (i = 0; i < n_shadows; i ++)
{
const GskShadow *s = gsk_shadow_node_get_shadow (node, i);
char *color;
const GskShadow2 *s = gsk_shadow_node_get_shadow2 (node, i);
if (i > 0)
g_string_append (p->str, ", ");
color = gdk_rgba_to_string (&s->color);
g_string_append (p->str, color);
print_color (p, &s->color);
g_string_append_c (p->str, ' ');
string_append_double (p->str, s->dx);
string_append_double (p->str, s->offset.x);
g_string_append_c (p->str, ' ');
string_append_double (p->str, s->dy);
string_append_double (p->str, s->offset.y);
if (s->radius > 0)
{
g_string_append_c (p->str, ' ');
string_append_double (p->str, s->radius);
}
g_free (color);
}
g_string_append_c (p->str, ';');

View File

@ -149,6 +149,20 @@ GskRenderNode * gsk_outset_shadow_node_new2 (const GskRoundedRect *o
float blur_radius);
const GdkColor *gsk_outset_shadow_node_get_color2 (const GskRenderNode *node);
typedef struct _GskShadow2 GskShadow2;
struct _GskShadow2
{
GdkColor color;
graphene_point_t offset;
float radius;
};
GskRenderNode * gsk_shadow_node_new2 (GskRenderNode *child,
const GskShadow2 *shadows,
gsize n_shadows);
const GskShadow2 *gsk_shadow_node_get_shadow2 (const GskRenderNode *node,
gsize i);
G_END_DECLS

View File

@ -1290,6 +1290,35 @@ gtk_css_parser_consume_percentage (GtkCssParser *self,
return FALSE;
}
gboolean
gtk_css_parser_consume_number_or_percentage (GtkCssParser *parser,
double min,
double max,
double *value)
{
double number = 0;
if (gtk_css_parser_has_percentage (parser))
{
if (gtk_css_parser_consume_percentage (parser, &number))
{
*value = min + (number / 100.0) * (max - min);
return TRUE;
}
}
else if (gtk_css_parser_has_number (parser))
{
if (gtk_css_parser_consume_number (parser, &number))
{
*value = number;
return TRUE;
}
}
gtk_css_parser_error_syntax (parser, "Expected a number or percentage");
return FALSE;
}
gsize
gtk_css_parser_consume_any (GtkCssParser *parser,
const GtkCssParseOption *options,

View File

@ -154,6 +154,11 @@ gboolean gtk_css_parser_consume_integer (GtkCssParser
int *number);
gboolean gtk_css_parser_consume_percentage (GtkCssParser *self,
double *number);
gboolean gtk_css_parser_consume_number_or_percentage
(GtkCssParser *parser,
double min,
double max,
double *value);
gboolean gtk_css_parser_consume_function (GtkCssParser *self,
guint min_args,
guint max_args,

View File

@ -755,28 +755,31 @@ gboolean
gtk_css_shadow_value_push_snapshot (const GtkCssValue *value,
GtkSnapshot *snapshot)
{
GskShadow *shadows;
GskShadow2 *shadows;
guint i;
if (gtk_css_shadow_value_is_clear (value))
return FALSE;
shadows = g_newa (GskShadow, value->n_shadows);
shadows = g_newa (GskShadow2, value->n_shadows);
for (i = 0; i < value->n_shadows; i++)
{
const ShadowValue *shadow = &value->shadows[i];
shadows[i].color = *gtk_css_color_value_get_rgba (shadow->color);
shadows[i].dx = gtk_css_number_value_get (shadow->hoffset, 0);
shadows[i].dy = gtk_css_number_value_get (shadow->voffset, 0);
gtk_css_color_to_color (gtk_css_color_value_get_color (shadow->color), &shadows[i].color);
graphene_point_init (&shadows[i].offset,
gtk_css_number_value_get (shadow->hoffset, 0),
gtk_css_number_value_get (shadow->voffset, 0));
shadows[i].radius = gtk_css_number_value_get (shadow->radius, 0);
if (value->is_filter)
shadows[i].radius *= 2;
}
gtk_snapshot_push_shadow (snapshot, shadows, value->n_shadows);
gtk_snapshot_push_shadow2 (snapshot, shadows, value->n_shadows);
for (i = 0; i < value->n_shadows; i++)
gdk_color_finish (&shadows[i].color);
return TRUE;
}

View File

@ -119,8 +119,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
} stroke;
struct {
gsize n_shadows;
GskShadow *shadows;
GskShadow a_shadow; /* Used if n_shadows == 1 */
GskShadow2 *shadows;
GskShadow2 a_shadow; /* Used if n_shadows == 1 */
} shadow;
struct {
GskBlendMode blend_mode;
@ -1348,11 +1348,11 @@ gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
if (node == NULL)
return NULL;
shadow_node = gsk_shadow_node_new (node,
state->data.shadow.shadows != NULL ?
state->data.shadow.shadows :
&state->data.shadow.a_shadow,
state->data.shadow.n_shadows);
shadow_node = gsk_shadow_node_new2 (node,
state->data.shadow.shadows != NULL
? state->data.shadow.shadows
: &state->data.shadow.a_shadow,
state->data.shadow.n_shadows);
gsk_render_node_unref (node);
@ -1391,6 +1391,12 @@ gtk_snapshot_append_stroke (GtkSnapshot *snapshot,
static void
gtk_snapshot_clear_shadow (GtkSnapshotState *state)
{
if (state->data.shadow.shadows != 0)
for (gsize i = 0; i < state->data.shadow.n_shadows; i++)
gdk_color_finish (&state->data.shadow.shadows[i].color);
else
gdk_color_finish (&state->data.shadow.a_shadow.color);
g_free (state->data.shadow.shadows);
}
@ -1408,6 +1414,41 @@ void
gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
const GskShadow *shadow,
gsize n_shadows)
{
GskShadow2 *shadow2;
g_return_if_fail (n_shadows > 0);
shadow2 = g_new (GskShadow2, n_shadows);
for (gsize i = 0; i < n_shadows; i++)
{
gdk_color_init_from_rgba (&shadow2[i].color, &shadow[i].color);
graphene_point_init (&shadow2[i].offset, shadow[i].dx,shadow[i].dy);
shadow2[i].radius = shadow[i].radius;
}
gtk_snapshot_push_shadow2 (snapshot, shadow2, n_shadows);
for (gsize i = 0; i < n_shadows; i++)
gdk_color_finish (&shadow2[i].color);
g_free (shadow2);
}
/*< private >
* gtk_snapshot_push_shadow2:
* @snapshot: a `GtkSnapshot`
* @shadow: (array length=n_shadows): the first shadow specification
* @n_shadows: number of shadow specifications
*
* Applies a shadow to an image.
*
* The image is recorded until the next call to [method@Gtk.Snapshot.pop].
*/
void
gtk_snapshot_push_shadow2 (GtkSnapshot *snapshot,
const GskShadow2 *shadow,
gsize n_shadows)
{
GtkSnapshotState *state;
GskTransform *transform;
@ -1429,20 +1470,23 @@ gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
if (n_shadows == 1)
{
state->data.shadow.shadows = NULL;
memcpy (&state->data.shadow.a_shadow, shadow, sizeof (GskShadow));
state->data.shadow.a_shadow.dx *= scale_x;
state->data.shadow.a_shadow.dy *= scale_y;
state->data.shadow.a_shadow.radius *= scale_x;
gdk_color_init_copy (&state->data.shadow.a_shadow.color, &shadow->color);
graphene_point_init (&state->data.shadow.a_shadow.offset,
shadow->offset.x * scale_x,
shadow->offset.y * scale_y);
state->data.shadow.a_shadow.radius = shadow->radius * scale_x;
}
else
{
state->data.shadow.shadows = g_malloc (sizeof (GskShadow) * n_shadows);
memcpy (state->data.shadow.shadows, shadow, sizeof (GskShadow) * n_shadows);
state->data.shadow.shadows = g_malloc (sizeof (GskShadow2) * n_shadows);
memcpy (state->data.shadow.shadows, shadow, sizeof (GskShadow2) * n_shadows);
for (i = 0; i < n_shadows; i++)
{
state->data.shadow.shadows[i].dx *= scale_x;
state->data.shadow.shadows[i].dy *= scale_y;
state->data.shadow.shadows[i].radius *= scale_x;
gdk_color_init_copy (&state->data.shadow.shadows[i].color, &shadow[i].color);
graphene_point_init (&state->data.shadow.shadows[i].offset,
shadow[i].offset.x * scale_x,
shadow[i].offset.y * scale_y);
state->data.shadow.shadows[i].radius = shadow[i].radius * scale_x;
}
}

View File

@ -61,5 +61,9 @@ void gtk_snapshot_append_outset_shadow2 (GtkSnapshot
float spread,
float blur_radius);
void gtk_snapshot_push_shadow2 (GtkSnapshot *snapshot,
const GskShadow2 *shadow,
gsize n_shadows);
G_END_DECLS

View File

@ -1532,14 +1532,14 @@ G_GNUC_END_IGNORE_DEPRECATIONS
for (i = 0; i < gsk_shadow_node_get_n_shadows (node); i++)
{
char *label;
const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i);
const GskShadow2 *shadow = gsk_shadow_node_get_shadow2 (node, i);
label = g_strdup_printf ("Color %d", i);
add_color_row (store, label, &shadow->color);
add_color2_row (store, label, &shadow->color);
g_free (label);
label = g_strdup_printf ("Offset %d", i);
add_text_row (store, label, "%.2f %.2f", shadow->dx, shadow->dy);
add_text_row (store, label, "%.2f %.2f", shadow->offset.x, shadow->offset.y);
g_free (label);
label = g_strdup_printf ("Radius %d", i);

View File

@ -3,5 +3,5 @@ shadow {
bounds: 100 100 100 100;
color: green;
}
shadows: red 10 10 4, blue -10 -10 8;
shadows: red 10 10 4, blue -10 -10 8, color(srgb-linear 0.5 0.5 0.3) -5 5 8;
}

View File

@ -1,5 +1,5 @@
shadow {
shadows: rgb(255,0,0) 10 10 4, rgb(0,0,255) -10 -10 8;
shadows: rgb(255,0,0) 10 10 4, rgb(0,0,255) -10 -10 8, color(srgb-linear 0.5 0.5 0.3) -5 5 8;
child: color {
bounds: 100 100 100 100;
color: rgb(0,128,0);

View File

@ -219,10 +219,10 @@ replay_shadow_node (GskRenderNode *node, GtkSnapshot *snapshot)
{
gsize n_shadows = gsk_shadow_node_get_n_shadows (node);
/* Hack: we know GskShadowNode stores shadows in a contiguous array. */
const GskShadow *shadow = gsk_shadow_node_get_shadow (node, 0);
const GskShadow2 *shadow = gsk_shadow_node_get_shadow2 (node, 0);
GskRenderNode *child = gsk_shadow_node_get_child (node);
gtk_snapshot_push_shadow (snapshot, shadow, n_shadows);
gtk_snapshot_push_shadow2 (snapshot, shadow, n_shadows);
replay_node (child, snapshot);
gtk_snapshot_pop (snapshot);
}