forked from AuroraMiddleware/gtk
46a9538b6a
Long time ago, Cairo shadows in both GTK3 and 4 were drawn at a size about twice their radius. Eventually this was fixed but the shadow extents are still calculated for the previous size and appear unreasonably large: for example, 141px for a 50px radius shadow. This can get very noticeable in places such as invisible window frame which gets included into screenshots. https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3419 just divides the radius by 2 when drawing a shadow with Cairo, do the same when calculating extents. See https://gitlab.gnome.org/GNOME/gtk/-/issues/3841
772 lines
22 KiB
C
772 lines
22 KiB
C
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 2011 Red Hat, Inc.
|
|
*
|
|
* Author: Cosimo Cecchi <cosimoc@gnome.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkcssshadowvalueprivate.h"
|
|
|
|
#include "gtkcsscolorvalueprivate.h"
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
#include "gtkcsscolorvalueprivate.h"
|
|
#include "gtksnapshotprivate.h"
|
|
#include "gtkpango.h"
|
|
|
|
#include "gsk/gskcairoblurprivate.h"
|
|
#include "gsk/gskroundedrectprivate.h"
|
|
|
|
#include <math.h>
|
|
|
|
typedef struct {
|
|
guint inset :1;
|
|
|
|
GtkCssValue *hoffset;
|
|
GtkCssValue *voffset;
|
|
GtkCssValue *radius;
|
|
GtkCssValue *spread;
|
|
GtkCssValue *color;
|
|
} ShadowValue;
|
|
|
|
struct _GtkCssValue {
|
|
GTK_CSS_VALUE_BASE
|
|
guint is_filter : 1; /* values stored in radius are std_dev, for drop-shadow */
|
|
guint n_shadows;
|
|
ShadowValue shadows[1];
|
|
};
|
|
|
|
static GtkCssValue * gtk_css_shadow_value_new (ShadowValue *shadows,
|
|
guint n_shadows,
|
|
gboolean is_filter);
|
|
|
|
static void
|
|
shadow_value_for_transition (ShadowValue *result,
|
|
gboolean inset)
|
|
{
|
|
result->inset = inset;
|
|
result->hoffset = _gtk_css_number_value_new (0, GTK_CSS_PX);
|
|
result->voffset = _gtk_css_number_value_new (0, GTK_CSS_PX);
|
|
result->radius = _gtk_css_number_value_new (0, GTK_CSS_PX);
|
|
result->spread = _gtk_css_number_value_new (0, GTK_CSS_PX);
|
|
result->color = gtk_css_color_value_new_transparent ();
|
|
}
|
|
|
|
static void
|
|
shadow_value_unref (const ShadowValue *shadow)
|
|
{
|
|
_gtk_css_value_unref (shadow->hoffset);
|
|
_gtk_css_value_unref (shadow->voffset);
|
|
_gtk_css_value_unref (shadow->spread);
|
|
_gtk_css_value_unref (shadow->radius);
|
|
_gtk_css_value_unref (shadow->color);
|
|
}
|
|
|
|
static gboolean
|
|
shadow_value_transition (const ShadowValue *start,
|
|
const ShadowValue *end,
|
|
guint property_id,
|
|
double progress,
|
|
ShadowValue *result)
|
|
|
|
{
|
|
if (start->inset != end->inset)
|
|
return FALSE;
|
|
|
|
result->inset = start->inset;
|
|
result->hoffset = _gtk_css_value_transition (start->hoffset, end->hoffset, property_id, progress);
|
|
result->voffset = _gtk_css_value_transition (start->voffset, end->voffset, property_id, progress);
|
|
result->radius = _gtk_css_value_transition (start->radius, end->radius, property_id, progress);
|
|
result->spread = _gtk_css_value_transition (start->spread, end->spread, property_id, progress);
|
|
result->color = _gtk_css_value_transition (start->color, end->color, property_id, progress);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_css_value_shadow_free (GtkCssValue *value)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < value->n_shadows; i ++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
shadow_value_unref (shadow);
|
|
}
|
|
|
|
g_slice_free1 (sizeof (GtkCssValue) + sizeof (ShadowValue) * (value->n_shadows - 1), value);
|
|
}
|
|
|
|
static GtkCssValue *
|
|
gtk_css_value_shadow_compute (GtkCssValue *value,
|
|
guint property_id,
|
|
GtkStyleProvider *provider,
|
|
GtkCssStyle *style,
|
|
GtkCssStyle *parent_style)
|
|
{
|
|
guint i;
|
|
ShadowValue *shadows;
|
|
|
|
shadows = g_alloca (sizeof (ShadowValue) * value->n_shadows);
|
|
|
|
for (i = 0; i < value->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
shadows[i].hoffset = _gtk_css_value_compute (shadow->hoffset, property_id, provider, style, parent_style);
|
|
shadows[i].voffset = _gtk_css_value_compute (shadow->voffset, property_id, provider, style, parent_style);
|
|
shadows[i].radius = _gtk_css_value_compute (shadow->radius, property_id, provider, style, parent_style);
|
|
shadows[i].spread = _gtk_css_value_compute (shadow->spread, property_id, provider, style, parent_style),
|
|
shadows[i].color = _gtk_css_value_compute (shadow->color, property_id, provider, style, parent_style);
|
|
shadows[i].inset = shadow->inset;
|
|
}
|
|
|
|
return gtk_css_shadow_value_new (shadows, value->n_shadows, value->is_filter);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_value_shadow_equal (const GtkCssValue *value1,
|
|
const GtkCssValue *value2)
|
|
{
|
|
guint i;
|
|
|
|
if (value1->n_shadows != value2->n_shadows)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < value1->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow1 = &value1->shadows[i];
|
|
const ShadowValue *shadow2 = &value2->shadows[i];
|
|
|
|
if (shadow1->inset != shadow2->inset ||
|
|
!_gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset) ||
|
|
!_gtk_css_value_equal (shadow1->voffset, shadow2->voffset) ||
|
|
!_gtk_css_value_equal (shadow1->radius, shadow2->radius) ||
|
|
!_gtk_css_value_equal (shadow1->spread, shadow2->spread) ||
|
|
!_gtk_css_value_equal (shadow1->color, shadow2->color))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GtkCssValue *
|
|
gtk_css_value_shadow_transition (GtkCssValue *start,
|
|
GtkCssValue *end,
|
|
guint property_id,
|
|
double progress)
|
|
{
|
|
|
|
guint i, len;
|
|
ShadowValue *shadows;
|
|
|
|
if (start->n_shadows > end->n_shadows)
|
|
len = start->n_shadows;
|
|
else
|
|
len = end->n_shadows;
|
|
|
|
shadows = g_newa (ShadowValue, len);
|
|
|
|
for (i = 0; i < MIN (start->n_shadows, end->n_shadows); i++)
|
|
{
|
|
if (!shadow_value_transition (&start->shadows[i], &end->shadows[i],
|
|
property_id, progress,
|
|
&shadows[i]))
|
|
{
|
|
while (i--)
|
|
shadow_value_unref (&shadows[i]);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (start->n_shadows > end->n_shadows)
|
|
{
|
|
for (; i < len; i++)
|
|
{
|
|
ShadowValue fill;
|
|
|
|
shadow_value_for_transition (&fill, start->shadows[i].inset);
|
|
if (!shadow_value_transition (&start->shadows[i], &fill, property_id, progress, &shadows[i]))
|
|
{
|
|
while (i--)
|
|
shadow_value_unref (&shadows[i]);
|
|
shadow_value_unref (&fill);
|
|
return NULL;
|
|
}
|
|
shadow_value_unref (&fill);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; i < len; i++)
|
|
{
|
|
ShadowValue fill;
|
|
|
|
shadow_value_for_transition (&fill, end->shadows[i].inset);
|
|
if (!shadow_value_transition (&fill, &end->shadows[i], property_id, progress, &shadows[i]))
|
|
{
|
|
while (i--)
|
|
shadow_value_unref (&shadows[i]);
|
|
shadow_value_unref (&fill);
|
|
return NULL;
|
|
}
|
|
shadow_value_unref (&fill);
|
|
}
|
|
}
|
|
|
|
return gtk_css_shadow_value_new (shadows, len, start->is_filter);
|
|
}
|
|
|
|
static void
|
|
gtk_css_value_shadow_print (const GtkCssValue *value,
|
|
GString *string)
|
|
{
|
|
guint i;
|
|
|
|
if (value->n_shadows == 0)
|
|
{
|
|
g_string_append (string, "none");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < value->n_shadows; i ++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (i > 0)
|
|
g_string_append (string, ", ");
|
|
|
|
_gtk_css_value_print (shadow->hoffset, string);
|
|
g_string_append_c (string, ' ');
|
|
_gtk_css_value_print (shadow->voffset, string);
|
|
g_string_append_c (string, ' ');
|
|
if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
|
|
_gtk_css_number_value_get (shadow->spread, 100) != 0)
|
|
{
|
|
_gtk_css_value_print (shadow->radius, string);
|
|
g_string_append_c (string, ' ');
|
|
}
|
|
|
|
if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
|
|
{
|
|
_gtk_css_value_print (shadow->spread, string);
|
|
g_string_append_c (string, ' ');
|
|
}
|
|
|
|
_gtk_css_value_print (shadow->color, string);
|
|
|
|
if (shadow->inset)
|
|
g_string_append (string, " inset");
|
|
}
|
|
}
|
|
|
|
static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
|
|
"GtkCssShadowValue",
|
|
gtk_css_value_shadow_free,
|
|
gtk_css_value_shadow_compute,
|
|
gtk_css_value_shadow_equal,
|
|
gtk_css_value_shadow_transition,
|
|
NULL,
|
|
NULL,
|
|
gtk_css_value_shadow_print
|
|
};
|
|
|
|
static GtkCssValue shadow_none_singleton = { >K_CSS_VALUE_SHADOW, 1, TRUE, FALSE, 0 };
|
|
|
|
GtkCssValue *
|
|
gtk_css_shadow_value_new_none (void)
|
|
{
|
|
return _gtk_css_value_ref (&shadow_none_singleton);
|
|
}
|
|
|
|
static GtkCssValue *
|
|
gtk_css_shadow_value_new (ShadowValue *shadows,
|
|
guint n_shadows,
|
|
gboolean is_filter)
|
|
{
|
|
GtkCssValue *retval;
|
|
guint i;
|
|
|
|
if (n_shadows == 0)
|
|
return gtk_css_shadow_value_new_none ();
|
|
|
|
retval = _gtk_css_value_alloc (>K_CSS_VALUE_SHADOW, sizeof (GtkCssValue) + sizeof (ShadowValue) * (n_shadows - 1));
|
|
retval->n_shadows = n_shadows;
|
|
retval->is_filter = is_filter;
|
|
|
|
memcpy (retval->shadows, shadows, sizeof (ShadowValue) * n_shadows);
|
|
|
|
retval->is_computed = TRUE;
|
|
for (i = 0; i < n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &retval->shadows[i];
|
|
|
|
if (!gtk_css_value_is_computed (shadow->hoffset) ||
|
|
!gtk_css_value_is_computed (shadow->voffset) ||
|
|
!gtk_css_value_is_computed (shadow->spread) ||
|
|
!gtk_css_value_is_computed (shadow->radius) ||
|
|
!gtk_css_value_is_computed (shadow->color))
|
|
{
|
|
retval->is_computed = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
GtkCssValue *
|
|
gtk_css_shadow_value_new_filter (void)
|
|
{
|
|
ShadowValue value;
|
|
|
|
value.inset = FALSE;
|
|
value.hoffset = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
|
|
value.voffset = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
|
|
value.radius = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
|
|
value.spread = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
|
|
value.color = _gtk_css_color_value_new_current_color ();
|
|
|
|
return gtk_css_shadow_value_new (&value, 1, TRUE);
|
|
}
|
|
|
|
enum {
|
|
HOFFSET,
|
|
VOFFSET,
|
|
RADIUS,
|
|
SPREAD,
|
|
N_VALUES
|
|
};
|
|
|
|
static gboolean
|
|
has_inset (GtkCssParser *parser,
|
|
gpointer option_data,
|
|
gpointer box_shadow_mode)
|
|
{
|
|
return box_shadow_mode && gtk_css_parser_has_ident (parser, "inset");
|
|
}
|
|
|
|
static gboolean
|
|
parse_inset (GtkCssParser *parser,
|
|
gpointer option_data,
|
|
gpointer box_shadow_mode)
|
|
{
|
|
gboolean *inset = option_data;
|
|
|
|
if (!gtk_css_parser_try_ident (parser, "inset"))
|
|
{
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
*inset = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_lengths (GtkCssParser *parser,
|
|
gpointer option_data,
|
|
gpointer box_shadow_mode)
|
|
{
|
|
GtkCssValue **values = option_data;
|
|
|
|
values[HOFFSET] = _gtk_css_number_value_parse (parser,
|
|
GTK_CSS_PARSE_LENGTH);
|
|
if (values[HOFFSET] == NULL)
|
|
return FALSE;
|
|
|
|
values[VOFFSET] = _gtk_css_number_value_parse (parser,
|
|
GTK_CSS_PARSE_LENGTH);
|
|
if (values[VOFFSET] == NULL)
|
|
return FALSE;
|
|
|
|
if (gtk_css_number_value_can_parse (parser))
|
|
{
|
|
values[RADIUS] = _gtk_css_number_value_parse (parser,
|
|
GTK_CSS_PARSE_LENGTH
|
|
| GTK_CSS_POSITIVE_ONLY);
|
|
if (values[RADIUS] == NULL)
|
|
return FALSE;
|
|
}
|
|
else
|
|
values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
|
|
|
|
if (box_shadow_mode && gtk_css_number_value_can_parse (parser))
|
|
{
|
|
values[SPREAD] = _gtk_css_number_value_parse (parser,
|
|
GTK_CSS_PARSE_LENGTH);
|
|
if (values[SPREAD] == NULL)
|
|
return FALSE;
|
|
}
|
|
else
|
|
values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_color (GtkCssParser *parser,
|
|
gpointer option_data,
|
|
gpointer box_shadow_mode)
|
|
{
|
|
GtkCssValue **color = option_data;
|
|
|
|
*color = _gtk_css_color_value_parse (parser);
|
|
if (*color == NULL)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_shadow_value_parse_one (GtkCssParser *parser,
|
|
gboolean box_shadow_mode,
|
|
ShadowValue *result)
|
|
{
|
|
GtkCssValue *values[N_VALUES] = { NULL, };
|
|
GtkCssValue *color = NULL;
|
|
gboolean inset = FALSE;
|
|
GtkCssParseOption options[] =
|
|
{
|
|
{ (void *) gtk_css_number_value_can_parse, parse_lengths, values },
|
|
{ has_inset, parse_inset, &inset },
|
|
{ (void *) gtk_css_color_value_can_parse, parse_color, &color },
|
|
};
|
|
guint i;
|
|
|
|
if (!gtk_css_parser_consume_any (parser, options, G_N_ELEMENTS (options), GUINT_TO_POINTER (box_shadow_mode)))
|
|
goto fail;
|
|
|
|
if (values[0] == NULL)
|
|
{
|
|
gtk_css_parser_error_syntax (parser, "Expected shadow value to contain a length");
|
|
goto fail;
|
|
}
|
|
|
|
if (color == NULL)
|
|
color = _gtk_css_color_value_new_current_color ();
|
|
|
|
result->hoffset = values[HOFFSET];
|
|
result->voffset = values[VOFFSET];
|
|
result->spread = values[SPREAD];
|
|
result->radius = values[RADIUS];
|
|
result->color = color;
|
|
result->inset = inset;
|
|
|
|
return TRUE;
|
|
|
|
fail:
|
|
for (i = 0; i < N_VALUES; i++)
|
|
{
|
|
g_clear_pointer (&values[i], gtk_css_value_unref);
|
|
}
|
|
g_clear_pointer (&color, gtk_css_value_unref);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define MAX_SHADOWS 64
|
|
GtkCssValue *
|
|
gtk_css_shadow_value_parse (GtkCssParser *parser,
|
|
gboolean box_shadow_mode)
|
|
{
|
|
ShadowValue shadows[MAX_SHADOWS];
|
|
int n_shadows = 0;
|
|
int i;
|
|
|
|
if (gtk_css_parser_try_ident (parser, "none"))
|
|
return gtk_css_shadow_value_new_none ();
|
|
|
|
do {
|
|
if (n_shadows == MAX_SHADOWS)
|
|
{
|
|
gtk_css_parser_error_syntax (parser, "Not more than %d shadows supported", MAX_SHADOWS);
|
|
goto fail;
|
|
}
|
|
if (gtk_css_shadow_value_parse_one (parser, box_shadow_mode, &shadows[n_shadows]))
|
|
n_shadows++;
|
|
|
|
} while (gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA));
|
|
|
|
return gtk_css_shadow_value_new (shadows, n_shadows, FALSE);
|
|
|
|
fail:
|
|
for (i = 0; i < n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &shadows[i];
|
|
|
|
shadow_value_unref (shadow);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GtkCssValue *
|
|
gtk_css_shadow_value_parse_filter (GtkCssParser *parser)
|
|
{
|
|
ShadowValue shadow;
|
|
|
|
if (gtk_css_shadow_value_parse_one (parser, FALSE, &shadow))
|
|
return gtk_css_shadow_value_new (&shadow, 1, TRUE);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gtk_css_shadow_value_get_extents (const GtkCssValue *value,
|
|
GtkBorder *border)
|
|
{
|
|
guint i;
|
|
|
|
memset (border, 0, sizeof (GtkBorder));
|
|
|
|
for (i = 0; i < value->n_shadows; i ++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
double hoffset, voffset, spread, radius, clip_radius;
|
|
|
|
spread = _gtk_css_number_value_get (shadow->spread, 0);
|
|
radius = _gtk_css_number_value_get (shadow->radius, 0);
|
|
if (!value->is_filter)
|
|
radius = radius / 2.0;
|
|
clip_radius = gsk_cairo_blur_compute_pixels (radius);
|
|
hoffset = _gtk_css_number_value_get (shadow->hoffset, 0);
|
|
voffset = _gtk_css_number_value_get (shadow->voffset, 0);
|
|
|
|
border->top = MAX (border->top, ceil (clip_radius + spread - voffset));
|
|
border->right = MAX (border->right, ceil (clip_radius + spread + hoffset));
|
|
border->bottom = MAX (border->bottom, ceil (clip_radius + spread + voffset));
|
|
border->left = MAX (border->left, ceil (clip_radius + spread - hoffset));
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_css_shadow_value_snapshot_outset (const GtkCssValue *value,
|
|
GtkSnapshot *snapshot,
|
|
const GskRoundedRect *border_box)
|
|
{
|
|
guint i;
|
|
double dx, dy, spread, radius;
|
|
const GdkRGBA *color;
|
|
|
|
g_return_if_fail (value->class == >K_CSS_VALUE_SHADOW);
|
|
|
|
for (i = 0; i < value->n_shadows; i ++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (shadow->inset)
|
|
continue;
|
|
|
|
color = gtk_css_color_value_get_rgba (shadow->color);
|
|
|
|
/* We don't need to draw invisible shadows */
|
|
if (gdk_rgba_is_clear (color))
|
|
continue;
|
|
|
|
dx = _gtk_css_number_value_get (shadow->hoffset, 0);
|
|
dy = _gtk_css_number_value_get (shadow->voffset, 0);
|
|
spread = _gtk_css_number_value_get (shadow->spread, 0);
|
|
radius = _gtk_css_number_value_get (shadow->radius, 0);
|
|
if (value->is_filter)
|
|
radius = 2 * radius;
|
|
|
|
gtk_snapshot_append_outset_shadow (snapshot, border_box, color, dx, dy, spread, radius);
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_css_shadow_value_snapshot_inset (const GtkCssValue *value,
|
|
GtkSnapshot *snapshot,
|
|
const GskRoundedRect *padding_box)
|
|
{
|
|
guint i;
|
|
double dx, dy, spread, radius;
|
|
const GdkRGBA *color;
|
|
|
|
g_return_if_fail (value->class == >K_CSS_VALUE_SHADOW);
|
|
|
|
for (i = 0; i < value->n_shadows; i ++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (!shadow->inset)
|
|
continue;
|
|
|
|
color = gtk_css_color_value_get_rgba (shadow->color);
|
|
|
|
/* We don't need to draw invisible shadows */
|
|
if (gdk_rgba_is_clear (color))
|
|
continue;
|
|
|
|
dx = _gtk_css_number_value_get (shadow->hoffset, 0);
|
|
dy = _gtk_css_number_value_get (shadow->voffset, 0);
|
|
spread = _gtk_css_number_value_get (shadow->spread, 0);
|
|
radius = _gtk_css_number_value_get (shadow->radius, 0);
|
|
if (value->is_filter)
|
|
radius = 2 * radius;
|
|
|
|
/* These are trivial to do with a color node */
|
|
if (spread == 0 && radius == 0 &&
|
|
gsk_rounded_rect_is_rectilinear (padding_box))
|
|
{
|
|
const graphene_rect_t *padding_bounds = &padding_box->bounds;
|
|
|
|
if (dx > 0)
|
|
{
|
|
const float y = dy > 0 ? dy : 0;
|
|
|
|
gtk_snapshot_append_color (snapshot, color,
|
|
&GRAPHENE_RECT_INIT (
|
|
padding_bounds->origin.x,
|
|
padding_bounds->origin.y + y,
|
|
dx,
|
|
padding_bounds->size.height - ABS (dy)
|
|
));
|
|
}
|
|
else if (dx < 0)
|
|
{
|
|
const float y = dy > 0 ? dy : 0;
|
|
|
|
gtk_snapshot_append_color (snapshot, color,
|
|
&GRAPHENE_RECT_INIT (
|
|
padding_bounds->origin.x + padding_bounds->size.width + dx,
|
|
padding_bounds->origin.y + y,
|
|
- dx,
|
|
padding_bounds->size.height - ABS (dy)
|
|
));
|
|
}
|
|
|
|
if (dy > 0)
|
|
{
|
|
gtk_snapshot_append_color (snapshot, color,
|
|
&GRAPHENE_RECT_INIT (
|
|
padding_bounds->origin.x,
|
|
padding_bounds->origin.y,
|
|
padding_bounds->size.width,
|
|
dy
|
|
));
|
|
}
|
|
else if (dy < 0)
|
|
{
|
|
gtk_snapshot_append_color (snapshot, color,
|
|
&GRAPHENE_RECT_INIT (
|
|
padding_bounds->origin.x,
|
|
padding_bounds->origin.y + padding_bounds->size.height + dy,
|
|
padding_bounds->size.width,
|
|
- dy
|
|
));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gtk_snapshot_append_inset_shadow (snapshot,
|
|
padding_box,
|
|
color,
|
|
dx, dy, spread, radius);
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gtk_css_shadow_value_is_clear (const GtkCssValue *value)
|
|
{
|
|
guint i;
|
|
|
|
if (!value)
|
|
return TRUE;
|
|
|
|
for (i = 0; i < value->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (!gdk_rgba_is_clear (gtk_css_color_value_get_rgba (shadow->color)))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gtk_css_shadow_value_is_none (const GtkCssValue *shadow)
|
|
{
|
|
return !shadow || shadow->n_shadows == 0;
|
|
}
|
|
|
|
gboolean
|
|
gtk_css_shadow_value_push_snapshot (const GtkCssValue *value,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
gboolean need_shadow = FALSE;
|
|
guint i;
|
|
|
|
for (i = 0; i < value->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (!gdk_rgba_is_clear (gtk_css_color_value_get_rgba (shadow->color)))
|
|
{
|
|
need_shadow = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (need_shadow)
|
|
{
|
|
GskShadow *shadows = g_newa (GskShadow, value->n_shadows);
|
|
|
|
for (i = 0; i < value->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
shadows[i].dx = _gtk_css_number_value_get (shadow->hoffset, 0);
|
|
shadows[i].dy = _gtk_css_number_value_get (shadow->voffset, 0);
|
|
shadows[i].color = *gtk_css_color_value_get_rgba (shadow->color);
|
|
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);
|
|
}
|
|
|
|
return need_shadow;
|
|
}
|
|
|
|
void
|
|
gtk_css_shadow_value_pop_snapshot (const GtkCssValue *value,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
gboolean need_shadow = FALSE;
|
|
guint i;
|
|
|
|
for (i = 0; i < value->n_shadows; i++)
|
|
{
|
|
const ShadowValue *shadow = &value->shadows[i];
|
|
|
|
if (!gdk_rgba_is_clear (gtk_css_color_value_get_rgba (shadow->color)))
|
|
{
|
|
need_shadow = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (need_shadow)
|
|
gtk_snapshot_pop (snapshot);
|
|
}
|