gtk2/gtk/gtkcssfiltervalue.c
Benjamin Otte 83fb7a649f css: Merge GtkStyleProviderPrivate into GtkStyleProvider
This is just lots of renaming.

The interface remains private, so the public API does not change, apart
from removing the definition of the Interface object to avoid
subclassing.
2017-10-31 04:33:54 +01:00

921 lines
28 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2017 Red Hat, Inc.
*
* 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 "gtkcssbgsizevalueprivate.h"
#include <math.h>
#include <string.h>
#include "gtkcssfiltervalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
typedef union _GtkCssFilter GtkCssFilter;
typedef enum {
GTK_CSS_FILTER_NONE,
GTK_CSS_FILTER_BLUR,
GTK_CSS_FILTER_BRIGHTNESS,
GTK_CSS_FILTER_CONTRAST,
GTK_CSS_FILTER_DROP_SHADOW,
GTK_CSS_FILTER_GRAYSCALE,
GTK_CSS_FILTER_HUE_ROTATE,
GTK_CSS_FILTER_INVERT,
GTK_CSS_FILTER_OPACITY,
GTK_CSS_FILTER_SATURATE,
GTK_CSS_FILTER_SEPIA
} GtkCssFilterType;
union _GtkCssFilter {
GtkCssFilterType type;
struct {
GtkCssFilterType type;
GtkCssValue *value;
} brightness, contrast, grayscale, hue_rotate, invert, opacity, saturate, sepia, blur;
};
struct _GtkCssValue {
GTK_CSS_VALUE_BASE
guint n_filters;
GtkCssFilter filters[1];
};
static GtkCssValue * gtk_css_filter_value_alloc (guint n_values);
static gboolean gtk_css_filter_value_is_none (const GtkCssValue *value);
static void
gtk_css_filter_clear (GtkCssFilter *filter)
{
switch (filter->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
_gtk_css_value_unref (filter->brightness.value);
break;
case GTK_CSS_FILTER_CONTRAST:
_gtk_css_value_unref (filter->contrast.value);
break;
case GTK_CSS_FILTER_GRAYSCALE:
_gtk_css_value_unref (filter->grayscale.value);
break;
case GTK_CSS_FILTER_HUE_ROTATE:
_gtk_css_value_unref (filter->hue_rotate.value);
break;
case GTK_CSS_FILTER_INVERT:
_gtk_css_value_unref (filter->invert.value);
break;
case GTK_CSS_FILTER_OPACITY:
_gtk_css_value_unref (filter->opacity.value);
break;
case GTK_CSS_FILTER_SATURATE:
_gtk_css_value_unref (filter->saturate.value);
break;
case GTK_CSS_FILTER_SEPIA:
_gtk_css_value_unref (filter->sepia.value);
break;
case GTK_CSS_FILTER_BLUR:
_gtk_css_value_unref (filter->blur.value);
break;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
break;
}
}
static void
gtk_css_filter_init_identity (GtkCssFilter *filter,
GtkCssFilterType type)
{
switch (type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
filter->brightness.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_CONTRAST:
filter->contrast.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_GRAYSCALE:
filter->grayscale.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_HUE_ROTATE:
filter->hue_rotate.value = _gtk_css_number_value_new (0, GTK_CSS_DEG);
break;
case GTK_CSS_FILTER_INVERT:
filter->invert.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_OPACITY:
filter->opacity.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_SATURATE:
filter->saturate.value = _gtk_css_number_value_new (1, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_SEPIA:
filter->sepia.value = _gtk_css_number_value_new (0, GTK_CSS_NUMBER);
break;
case GTK_CSS_FILTER_BLUR:
filter->blur.value = _gtk_css_number_value_new (0, GTK_CSS_PX);
break;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
break;
}
filter->type = type;
}
#define R 0.2126
#define G 0.7152
#define B 0.0722
static gboolean
gtk_css_filter_get_matrix (const GtkCssFilter *filter,
graphene_matrix_t *matrix,
graphene_vec4_t *offset)
{
double value;
switch (filter->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
value = _gtk_css_number_value_get (filter->brightness.value, 1.0);
graphene_matrix_init_scale (matrix, value, value, value);
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
break;
case GTK_CSS_FILTER_CONTRAST:
value = _gtk_css_number_value_get (filter->contrast.value, 1.0);
graphene_matrix_init_scale (matrix, value, value, value);
graphene_vec4_init (offset, 0.5 - 0.5 * value, 0.5 - 0.5 * value, 0.5 - 0.5 * value, 0.0);
break;
case GTK_CSS_FILTER_GRAYSCALE:
value = _gtk_css_number_value_get (filter->grayscale.value, 1.0);
graphene_matrix_init_from_float (matrix, (float[16]) {
1.0 - (1.0 - R) * value, R * value, R * value, 0.0,
G * value, 1.0 - (1.0 - G) * value, G * value, 0.0,
B * value, B * value, 1.0 - (1.0 - B) * value, 0.0,
0.0, 0.0, 0.0, 1.0
});
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
break;
case GTK_CSS_FILTER_HUE_ROTATE:
{
double c, s;
value = _gtk_css_number_value_get (filter->grayscale.value, 1.0) * G_PI / 180.0;
c = cos (value);
s = sin (value);
graphene_matrix_init_from_float (matrix, (float[16]) {
0.213 + 0.787 * c - 0.213 * s,
0.213 - 0.213 * c + 0.143 * s,
0.213 - 0.213 * c - 0.787 * s,
0,
0.715 - 0.715 * c - 0.715 * s,
0.715 + 0.285 * c + 0.140 * s,
0.715 - 0.715 * c + 0.715 * s,
0,
0.072 - 0.072 * c + 0.928 * s,
0.072 - 0.072 * c - 0.283 * s,
0.072 + 0.928 * c + 0.072 * s,
0,
0, 0, 0, 1
});
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
}
break;
case GTK_CSS_FILTER_INVERT:
value = _gtk_css_number_value_get (filter->invert.value, 1.0);
graphene_matrix_init_scale (matrix, 1.0 - 2 * value, 1.0 - 2 * value, 1.0 - 2 * value);
graphene_vec4_init (offset, value, value, value, 0.0);
break;
case GTK_CSS_FILTER_OPACITY:
value = _gtk_css_number_value_get (filter->invert.value, 1.0);
graphene_matrix_init_from_float (matrix, (float[16]) {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, value
});
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
break;
case GTK_CSS_FILTER_SATURATE:
value = _gtk_css_number_value_get (filter->saturate.value, 1.0);
graphene_matrix_init_from_float (matrix, (float[16]) {
R + (1.0 - R) * value, R - R * value, R - R * value, 0.0,
G - G * value, G + (1.0 - G) * value, G - G * value, 0.0,
B - B * value, B - B * value, B + (1.0 - B) * value, 0.0,
0.0, 0.0, 0.0, 1.0
});
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
break;
case GTK_CSS_FILTER_SEPIA:
value = _gtk_css_number_value_get (filter->sepia.value, 1.0);
graphene_matrix_init_from_float (matrix, (float[16]) {
1.0 - 0.607 * value, 0.349 * value, 0.272 * value, 0.0,
0.769 * value, 1.0 - 0.314 * value, 0.534 * value, 0.0,
0.189 * value, 0.168 * value, 1.0 - 0.869 * value, 0.0,
0.0, 0.0, 0.0, 1.0
});
graphene_vec4_init (offset, 0.0, 0.0, 0.0, 0.0);
break;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_BLUR:
case GTK_CSS_FILTER_DROP_SHADOW:
return FALSE;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
#undef R
#undef G
#undef B
static int
gtk_css_filter_value_compute_matrix (const GtkCssValue *value,
int first,
graphene_matrix_t *matrix,
graphene_vec4_t *offset)
{
graphene_matrix_t m, m2;
graphene_vec4_t o, o2;
int i;
if (!gtk_css_filter_get_matrix (&value->filters[first], matrix, offset))
return first;
for (i = first + 1; i < value->n_filters; i++)
{
if (!gtk_css_filter_get_matrix (&value->filters[i], &m, &o))
return i;
graphene_matrix_multiply (matrix, &m, &m2);
graphene_matrix_transform_vec4 (&m, offset, &o2);
graphene_matrix_init_from_matrix (matrix, &m2);
graphene_vec4_add (&o, &o2, offset);
}
return value->n_filters;
}
static void
gtk_css_value_filter_free (GtkCssValue *value)
{
guint i;
for (i = 0; i < value->n_filters; i++)
{
gtk_css_filter_clear (&value->filters[i]);
}
g_slice_free1 (sizeof (GtkCssValue) + sizeof (GtkCssFilter) * (value->n_filters - 1), value);
}
/* returns TRUE if dest == src */
static gboolean
gtk_css_filter_compute (GtkCssFilter *dest,
GtkCssFilter *src,
guint property_id,
GtkStyleProvider *provider,
GtkCssStyle *style,
GtkCssStyle *parent_style)
{
dest->type = src->type;
switch (src->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
dest->brightness.value = _gtk_css_value_compute (src->brightness.value, property_id, provider, style, parent_style);
return dest->brightness.value == src->brightness.value;
case GTK_CSS_FILTER_CONTRAST:
dest->contrast.value = _gtk_css_value_compute (src->contrast.value, property_id, provider, style, parent_style);
return dest->contrast.value == src->contrast.value;
case GTK_CSS_FILTER_GRAYSCALE:
dest->grayscale.value = _gtk_css_value_compute (src->grayscale.value, property_id, provider, style, parent_style);
return dest->grayscale.value == src->grayscale.value;
case GTK_CSS_FILTER_HUE_ROTATE:
dest->hue_rotate.value = _gtk_css_value_compute (src->hue_rotate.value, property_id, provider, style, parent_style);
return dest->hue_rotate.value == src->hue_rotate.value;
case GTK_CSS_FILTER_INVERT:
dest->invert.value = _gtk_css_value_compute (src->invert.value, property_id, provider, style, parent_style);
return dest->invert.value == src->invert.value;
case GTK_CSS_FILTER_OPACITY:
dest->opacity.value = _gtk_css_value_compute (src->opacity.value, property_id, provider, style, parent_style);
return dest->opacity.value == src->opacity.value;
case GTK_CSS_FILTER_SATURATE:
dest->saturate.value = _gtk_css_value_compute (src->saturate.value, property_id, provider, style, parent_style);
return dest->saturate.value == src->saturate.value;
case GTK_CSS_FILTER_SEPIA:
dest->sepia.value = _gtk_css_value_compute (src->sepia.value, property_id, provider, style, parent_style);
return dest->sepia.value == src->sepia.value;
case GTK_CSS_FILTER_BLUR:
dest->blur.value = _gtk_css_value_compute (src->blur.value, property_id, provider, style, parent_style);
return dest->blur.value == src->blur.value;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
return FALSE;
}
}
static GtkCssValue *
gtk_css_value_filter_compute (GtkCssValue *value,
guint property_id,
GtkStyleProvider *provider,
GtkCssStyle *style,
GtkCssStyle *parent_style)
{
GtkCssValue *result;
gboolean changes;
guint i;
/* Special case the 99% case of "none" */
if (gtk_css_filter_value_is_none (value))
return _gtk_css_value_ref (value);
changes = FALSE;
result = gtk_css_filter_value_alloc (value->n_filters);
for (i = 0; i < value->n_filters; i++)
{
changes |= !gtk_css_filter_compute (&result->filters[i],
&value->filters[i],
property_id,
provider,
style,
parent_style);
}
if (!changes)
{
_gtk_css_value_unref (result);
result = _gtk_css_value_ref (value);
}
return result;
}
static gboolean
gtk_css_filter_equal (const GtkCssFilter *filter1,
const GtkCssFilter *filter2)
{
if (filter1->type != filter2->type)
return FALSE;
switch (filter1->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
return _gtk_css_value_equal (filter1->brightness.value, filter2->brightness.value);
case GTK_CSS_FILTER_CONTRAST:
return _gtk_css_value_equal (filter1->contrast.value, filter2->contrast.value);
case GTK_CSS_FILTER_GRAYSCALE:
return _gtk_css_value_equal (filter1->grayscale.value, filter2->grayscale.value);
case GTK_CSS_FILTER_HUE_ROTATE:
return _gtk_css_value_equal (filter1->hue_rotate.value, filter2->hue_rotate.value);
case GTK_CSS_FILTER_INVERT:
return _gtk_css_value_equal (filter1->invert.value, filter2->invert.value);
case GTK_CSS_FILTER_OPACITY:
return _gtk_css_value_equal (filter1->opacity.value, filter2->opacity.value);
case GTK_CSS_FILTER_SATURATE:
return _gtk_css_value_equal (filter1->saturate.value, filter2->saturate.value);
case GTK_CSS_FILTER_SEPIA:
return _gtk_css_value_equal (filter1->sepia.value, filter2->sepia.value);
case GTK_CSS_FILTER_BLUR:
return _gtk_css_value_equal (filter1->blur.value, filter2->blur.value);
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
return FALSE;
}
}
static gboolean
gtk_css_value_filter_equal (const GtkCssValue *value1,
const GtkCssValue *value2)
{
const GtkCssValue *larger;
guint i, n;
n = MIN (value1->n_filters, value2->n_filters);
for (i = 0; i < n; i++)
{
if (!gtk_css_filter_equal (&value1->filters[i], &value2->filters[i]))
return FALSE;
}
larger = value1->n_filters > value2->n_filters ? value1 : value2;
for (; i < larger->n_filters; i++)
{
GtkCssFilter filter;
gtk_css_filter_init_identity (&filter, larger->filters[i].type);
if (!gtk_css_filter_equal (&larger->filters[i], &filter))
{
gtk_css_filter_clear (&filter);
return FALSE;
}
gtk_css_filter_clear (&filter);
}
return TRUE;
}
static void
gtk_css_filter_transition (GtkCssFilter *result,
const GtkCssFilter *start,
const GtkCssFilter *end,
guint property_id,
double progress)
{
result->type = start->type;
switch (start->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
result->brightness.value = _gtk_css_value_transition (start->brightness.value, end->brightness.value, property_id, progress);
break;
case GTK_CSS_FILTER_CONTRAST:
result->contrast.value = _gtk_css_value_transition (start->contrast.value, end->contrast.value, property_id, progress);
break;
case GTK_CSS_FILTER_GRAYSCALE:
result->grayscale.value = _gtk_css_value_transition (start->grayscale.value, end->grayscale.value, property_id, progress);
break;
case GTK_CSS_FILTER_HUE_ROTATE:
result->hue_rotate.value = _gtk_css_value_transition (start->hue_rotate.value, end->hue_rotate.value, property_id, progress);
break;
case GTK_CSS_FILTER_INVERT:
result->invert.value = _gtk_css_value_transition (start->invert.value, end->invert.value, property_id, progress);
break;
case GTK_CSS_FILTER_OPACITY:
result->opacity.value = _gtk_css_value_transition (start->opacity.value, end->opacity.value, property_id, progress);
break;
case GTK_CSS_FILTER_SATURATE:
result->saturate.value = _gtk_css_value_transition (start->saturate.value, end->saturate.value, property_id, progress);
break;
case GTK_CSS_FILTER_SEPIA:
result->sepia.value = _gtk_css_value_transition (start->sepia.value, end->sepia.value, property_id, progress);
break;
case GTK_CSS_FILTER_BLUR:
result->blur.value = _gtk_css_value_transition (start->blur.value, end->blur.value, property_id, progress);
break;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
break;
}
}
static GtkCssValue *
gtk_css_value_filter_transition (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
GtkCssValue *result;
guint i, n;
if (gtk_css_filter_value_is_none (start))
{
if (gtk_css_filter_value_is_none (end))
return _gtk_css_value_ref (start);
n = 0;
}
else if (gtk_css_filter_value_is_none (end))
{
n = 0;
}
else
{
n = MIN (start->n_filters, end->n_filters);
}
/* Check filters are compatible. If not, transition between
* their result matrices.
*/
for (i = 0; i < n; i++)
{
if (start->filters[i].type != end->filters[i].type)
{
/* XXX: can we improve this? */
return NULL;
}
}
result = gtk_css_filter_value_alloc (MAX (start->n_filters, end->n_filters));
for (i = 0; i < n; i++)
{
gtk_css_filter_transition (&result->filters[i],
&start->filters[i],
&end->filters[i],
property_id,
progress);
}
for (; i < start->n_filters; i++)
{
GtkCssFilter filter;
gtk_css_filter_init_identity (&filter, start->filters[i].type);
gtk_css_filter_transition (&result->filters[i],
&start->filters[i],
&filter,
property_id,
progress);
gtk_css_filter_clear (&filter);
}
for (; i < end->n_filters; i++)
{
GtkCssFilter filter;
gtk_css_filter_init_identity (&filter, end->filters[i].type);
gtk_css_filter_transition (&result->filters[i],
&filter,
&end->filters[i],
property_id,
progress);
gtk_css_filter_clear (&filter);
}
g_assert (i == MAX (start->n_filters, end->n_filters));
return result;
}
static void
gtk_css_filter_print (const GtkCssFilter *filter,
GString *string)
{
switch (filter->type)
{
case GTK_CSS_FILTER_BRIGHTNESS:
g_string_append (string, "brightness(");
_gtk_css_value_print (filter->brightness.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_CONTRAST:
g_string_append (string, "contrast(");
_gtk_css_value_print (filter->contrast.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_GRAYSCALE:
g_string_append (string, "grayscale(");
_gtk_css_value_print (filter->grayscale.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_HUE_ROTATE:
g_string_append (string, "hue-rotate(");
_gtk_css_value_print (filter->hue_rotate.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_INVERT:
g_string_append (string, "invert(");
_gtk_css_value_print (filter->invert.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_OPACITY:
g_string_append (string, "opacity(");
_gtk_css_value_print (filter->opacity.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_SATURATE:
g_string_append (string, "saturate(");
_gtk_css_value_print (filter->saturate.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_SEPIA:
g_string_append (string, "sepia(");
_gtk_css_value_print (filter->sepia.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_BLUR:
g_string_append (string, "blur(");
_gtk_css_value_print (filter->blur.value, string);
g_string_append (string, ")");
break;
case GTK_CSS_FILTER_NONE:
case GTK_CSS_FILTER_DROP_SHADOW:
default:
g_assert_not_reached ();
break;
}
}
static void
gtk_css_value_filter_print (const GtkCssValue *value,
GString *string)
{
guint i;
if (gtk_css_filter_value_is_none (value))
{
g_string_append (string, "none");
return;
}
for (i = 0; i < value->n_filters; i++)
{
if (i > 0)
g_string_append_c (string, ' ');
gtk_css_filter_print (&value->filters[i], string);
}
}
static const GtkCssValueClass GTK_CSS_VALUE_FILTER = {
gtk_css_value_filter_free,
gtk_css_value_filter_compute,
gtk_css_value_filter_equal,
gtk_css_value_filter_transition,
gtk_css_value_filter_print
};
static GtkCssValue none_singleton = { &GTK_CSS_VALUE_FILTER, 1, 0, { { GTK_CSS_FILTER_NONE } } };
static GtkCssValue *
gtk_css_filter_value_alloc (guint n_filters)
{
GtkCssValue *result;
g_return_val_if_fail (n_filters > 0, NULL);
result = _gtk_css_value_alloc (&GTK_CSS_VALUE_FILTER, sizeof (GtkCssValue) + sizeof (GtkCssFilter) * (n_filters - 1));
result->n_filters = n_filters;
return result;
}
GtkCssValue *
gtk_css_filter_value_new_none (void)
{
return _gtk_css_value_ref (&none_singleton);
}
static gboolean
gtk_css_filter_value_is_none (const GtkCssValue *value)
{
return value->n_filters == 0;
}
static gboolean
gtk_css_filter_parse (GtkCssFilter *filter,
GtkCssParser *parser)
{
if (_gtk_css_parser_try (parser, "brightness(", TRUE))
{
filter->type = GTK_CSS_FILTER_BRIGHTNESS;
filter->brightness.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->brightness.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "contrast(", TRUE))
{
filter->type = GTK_CSS_FILTER_CONTRAST;
filter->contrast.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->contrast.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "grayscale(", TRUE))
{
filter->type = GTK_CSS_FILTER_GRAYSCALE;
filter->grayscale.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->grayscale.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "hue-rotate(", TRUE))
{
filter->type = GTK_CSS_FILTER_HUE_ROTATE;
filter->hue_rotate.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE);
if (filter->hue_rotate.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "invert(", TRUE))
{
filter->type = GTK_CSS_FILTER_INVERT;
filter->invert.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->invert.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "opacity(", TRUE))
{
filter->type = GTK_CSS_FILTER_OPACITY;
filter->opacity.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->opacity.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "saturate(", TRUE))
{
filter->type = GTK_CSS_FILTER_SATURATE;
filter->saturate.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->saturate.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "sepia(", TRUE))
{
filter->type = GTK_CSS_FILTER_SEPIA;
filter->sepia.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_PARSE_PERCENT);
if (filter->sepia.value == NULL)
return FALSE;
}
else if (_gtk_css_parser_try (parser, "blur(", TRUE))
{
filter->type = GTK_CSS_FILTER_BLUR;
filter->blur.value = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH);
if (filter->blur.value == NULL)
return FALSE;
}
else
{
_gtk_css_parser_error (parser, "unknown syntax for filter");
return FALSE;
}
if (!_gtk_css_parser_try (parser, ")", TRUE))
{
gtk_css_filter_clear (filter);
_gtk_css_parser_error (parser, "Expected closing ')'");
return FALSE;
}
return TRUE;
}
GtkCssValue *
gtk_css_filter_value_parse (GtkCssParser *parser)
{
GtkCssValue *value;
GArray *array;
guint i;
if (_gtk_css_parser_try (parser, "none", TRUE))
return gtk_css_filter_value_new_none ();
array = g_array_new (FALSE, FALSE, sizeof (GtkCssFilter));
do {
GtkCssFilter filter;
if (!gtk_css_filter_parse (&filter, parser))
{
for (i = 0; i < array->len; i++)
{
gtk_css_filter_clear (&g_array_index (array, GtkCssFilter, i));
}
g_array_free (array, TRUE);
return NULL;
}
g_array_append_val (array, filter);
} while (!_gtk_css_parser_begins_with (parser, ';'));
value = gtk_css_filter_value_alloc (array->len);
memcpy (value->filters, array->data, sizeof (GtkCssFilter) * array->len);
g_array_free (array, TRUE);
return value;
}
void
gtk_css_filter_value_push_snapshot (const GtkCssValue *filter,
GtkSnapshot *snapshot)
{
graphene_matrix_t matrix;
graphene_vec4_t offset;
int i, j;
double radius;
if (gtk_css_filter_value_is_none (filter))
return;
i = 0;
while (i < filter->n_filters)
{
j = gtk_css_filter_value_compute_matrix (filter, i, &matrix, &offset);
if (i < j)
gtk_snapshot_push_color_matrix (snapshot,
&matrix,
&offset,
"CssFilter ColorMatrix<%d-%d>", i, j);
if (j < filter->n_filters)
{
if (filter->filters[j].type == GTK_CSS_FILTER_BLUR)
{
radius = _gtk_css_number_value_get (filter->filters[j].blur.value, 100.0);
gtk_snapshot_push_blur (snapshot, radius, "CssFilter Blur<%d, radius %g>", j, radius);
}
else
g_warning ("Don't know how to handle filter type %d", filter->filters[j].type);
}
i = j + 1;
}
}
void
gtk_css_filter_value_pop_snapshot (const GtkCssValue *filter,
GtkSnapshot *snapshot)
{
int i, j;
if (gtk_css_filter_value_is_none (filter))
return;
i = 0;
while (i < filter->n_filters)
{
for (j = i; j < filter->n_filters; j++)
{
if (filter->filters[j].type == GTK_CSS_FILTER_BLUR)
break;
}
if (i < j)
gtk_snapshot_pop (snapshot);
if (j < filter->n_filters)
gtk_snapshot_pop (snapshot);
i = j + 1;
}
}