1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 #include "gtkcsseasevalueprivate.h"
25 GTK_CSS_EASE_CUBIC_BEZIER,
47 gtk_css_value_ease_free (GtkCssValue *value)
49 g_slice_free (GtkCssValue, value);
53 gtk_css_value_ease_compute (GtkCssValue *value,
55 GtkStyleProviderPrivate *provider,
56 GtkCssComputedValues *values,
57 GtkCssComputedValues *parent_values,
58 GtkCssDependencies *dependencies)
60 return _gtk_css_value_ref (value);
64 gtk_css_value_ease_equal (const GtkCssValue *ease1,
65 const GtkCssValue *ease2)
67 if (ease1->type != ease2->type)
72 case GTK_CSS_EASE_CUBIC_BEZIER:
73 return ease1->u.cubic.x1 == ease2->u.cubic.x1 &&
74 ease1->u.cubic.y1 == ease2->u.cubic.y1 &&
75 ease1->u.cubic.x2 == ease2->u.cubic.x2 &&
76 ease1->u.cubic.y2 == ease2->u.cubic.y2;
77 case GTK_CSS_EASE_STEPS:
78 return ease1->u.steps.steps == ease2->u.steps.steps &&
79 ease1->u.steps.start == ease2->u.steps.start;
81 g_assert_not_reached ();
87 gtk_css_value_ease_transition (GtkCssValue *start,
96 gtk_css_value_ease_print (const GtkCssValue *ease,
101 case GTK_CSS_EASE_CUBIC_BEZIER:
102 if (ease->u.cubic.x1 == 0.25 && ease->u.cubic.y1 == 0.1 &&
103 ease->u.cubic.x2 == 0.25 && ease->u.cubic.y2 == 1.0)
104 g_string_append (string, "ease");
105 else if (ease->u.cubic.x1 == 0.0 && ease->u.cubic.y1 == 0.0 &&
106 ease->u.cubic.x2 == 1.0 && ease->u.cubic.y2 == 1.0)
107 g_string_append (string, "linear");
108 else if (ease->u.cubic.x1 == 0.42 && ease->u.cubic.y1 == 0.0 &&
109 ease->u.cubic.x2 == 1.0 && ease->u.cubic.y2 == 1.0)
110 g_string_append (string, "ease-in");
111 else if (ease->u.cubic.x1 == 0.0 && ease->u.cubic.y1 == 0.0 &&
112 ease->u.cubic.x2 == 0.58 && ease->u.cubic.y2 == 1.0)
113 g_string_append (string, "ease-out");
114 else if (ease->u.cubic.x1 == 0.42 && ease->u.cubic.y1 == 0.0 &&
115 ease->u.cubic.x2 == 0.58 && ease->u.cubic.y2 == 1.0)
116 g_string_append (string, "ease-in-out");
118 g_string_append_printf (string, "cubic-bezier(%g,%g,%g,%g)",
119 ease->u.cubic.x1, ease->u.cubic.y1,
120 ease->u.cubic.x2, ease->u.cubic.y2);
122 case GTK_CSS_EASE_STEPS:
123 if (ease->u.steps.steps == 1)
125 g_string_append (string, ease->u.steps.start ? "step-start" : "step-end");
129 g_string_append_printf (string, "steps(%u%s)", ease->u.steps.steps, ease->u.steps.start ? ",start" : "");
133 g_assert_not_reached ();
138 static const GtkCssValueClass GTK_CSS_VALUE_EASE = {
139 gtk_css_value_ease_free,
140 gtk_css_value_ease_compute,
141 gtk_css_value_ease_equal,
142 gtk_css_value_ease_transition,
143 gtk_css_value_ease_print
147 _gtk_css_ease_value_new_cubic_bezier (double x1,
154 g_return_val_if_fail (x1 >= 0.0, NULL);
155 g_return_val_if_fail (x1 <= 1.0, NULL);
156 g_return_val_if_fail (x2 >= 0.0, NULL);
157 g_return_val_if_fail (x2 <= 1.0, NULL);
159 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_EASE);
161 value->type = GTK_CSS_EASE_CUBIC_BEZIER;
162 value->u.cubic.x1 = x1;
163 value->u.cubic.y1 = y1;
164 value->u.cubic.x2 = x2;
165 value->u.cubic.y2 = y2;
171 _gtk_css_ease_value_new_steps (guint n_steps,
176 g_return_val_if_fail (n_steps > 0, NULL);
178 value = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_EASE);
180 value->type = GTK_CSS_EASE_STEPS;
181 value->u.steps.steps = n_steps;
182 value->u.steps.start = start;
187 static const struct {
190 guint needs_custom :1;
192 } parser_values[] = {
193 { "linear", TRUE, FALSE, { 0.0, 0.0, 1.0, 1.0 } },
194 { "ease-in-out", TRUE, FALSE, { 0.42, 0.0, 0.58, 1.0 } },
195 { "ease-in", TRUE, FALSE, { 0.42, 0.0, 1.0, 1.0 } },
196 { "ease-out", TRUE, FALSE, { 0.0, 0.0, 0.58, 1.0 } },
197 { "ease", TRUE, FALSE, { 0.25, 0.1, 0.25, 1.0 } },
198 { "step-start", FALSE, FALSE, { 1.0, 1.0, 0.0, 0.0 } },
199 { "step-end", FALSE, FALSE, { 1.0, 0.0, 0.0, 0.0 } },
200 { "steps", FALSE, TRUE, { 0.0, 0.0, 0.0, 0.0 } },
201 { "cubic-bezier", TRUE, TRUE, { 0.0, 0.0, 0.0, 0.0 } }
205 _gtk_css_ease_value_can_parse (GtkCssParser *parser)
209 for (i = 0; i < G_N_ELEMENTS (parser_values); i++)
211 if (_gtk_css_parser_has_prefix (parser, parser_values[i].name))
219 gtk_css_ease_value_parse_cubic_bezier (GtkCssParser *parser)
224 for (i = 0; i < 4; i++)
226 if (!_gtk_css_parser_try (parser, i ? "," : "(", TRUE))
228 _gtk_css_parser_error (parser, "Expected '%s'", i ? "," : "(");
231 if (!_gtk_css_parser_try_double (parser, &values[i]))
233 _gtk_css_parser_error (parser, "Expected a number");
236 if ((i == 0 || i == 2) &&
237 (values[i] < 0 || values[i] > 1.0))
239 _gtk_css_parser_error (parser, "value %g out of range. Must be from 0.0 to 1.0", values[i]);
244 if (!_gtk_css_parser_try (parser, ")", TRUE))
246 _gtk_css_parser_error (parser, "Missing closing ')' for cubic-bezier");
250 return _gtk_css_ease_value_new_cubic_bezier (values[0], values[1], values[2], values[3]);
254 gtk_css_ease_value_parse_steps (GtkCssParser *parser)
259 if (!_gtk_css_parser_try (parser, "(", TRUE))
261 _gtk_css_parser_error (parser, "Expected '('");
265 if (!_gtk_css_parser_try_uint (parser, &n_steps))
267 _gtk_css_parser_error (parser, "Expected number of steps");
271 if (_gtk_css_parser_try (parser, ",", TRUE))
273 if (_gtk_css_parser_try (parser, "start", TRUE))
275 else if (_gtk_css_parser_try (parser, "end", TRUE))
279 _gtk_css_parser_error (parser, "Only allowed values are 'start' and 'end'");
286 if (!_gtk_css_parser_try (parser, ")", TRUE))
288 _gtk_css_parser_error (parser, "Missing closing ')' for steps");
292 return _gtk_css_ease_value_new_steps (n_steps, start);
296 _gtk_css_ease_value_parse (GtkCssParser *parser)
300 g_return_val_if_fail (parser != NULL, NULL);
302 for (i = 0; i < G_N_ELEMENTS (parser_values); i++)
304 if (_gtk_css_parser_try (parser, parser_values[i].name, FALSE))
306 if (parser_values[i].needs_custom)
308 if (parser_values[i].is_bezier)
309 return gtk_css_ease_value_parse_cubic_bezier (parser);
311 return gtk_css_ease_value_parse_steps (parser);
314 _gtk_css_parser_skip_whitespace (parser);
316 if (parser_values[i].is_bezier)
317 return _gtk_css_ease_value_new_cubic_bezier (parser_values[i].values[0],
318 parser_values[i].values[1],
319 parser_values[i].values[2],
320 parser_values[i].values[3]);
322 return _gtk_css_ease_value_new_steps (parser_values[i].values[0],
323 parser_values[i].values[1] != 0.0);
327 _gtk_css_parser_error (parser, "Unknown value");
332 _gtk_css_ease_value_transform (const GtkCssValue *ease,
335 g_return_val_if_fail (ease->class == >K_CSS_VALUE_EASE, 1.0);
344 case GTK_CSS_EASE_CUBIC_BEZIER:
346 static const double epsilon = 0.00001;
347 double tmin, t, tmax;
356 sample = (((1.0 + 3 * ease->u.cubic.x1 - 3 * ease->u.cubic.x2) * t
357 + -6 * ease->u.cubic.x1 + 3 * ease->u.cubic.x2) * t
358 + 3 * ease->u.cubic.x1 ) * t;
359 if (fabs(sample - progress) < epsilon)
362 if (progress > sample)
366 t = (tmax + tmin) * .5;
369 return (((1.0 + 3 * ease->u.cubic.y1 - 3 * ease->u.cubic.y2) * t
370 + -6 * ease->u.cubic.y1 + 3 * ease->u.cubic.y2) * t
371 + 3 * ease->u.cubic.y1 ) * t;
373 case GTK_CSS_EASE_STEPS:
374 progress *= ease->u.steps.steps;
375 progress = floor (progress) + (ease->u.steps.start ? 0 : 1);
376 return progress / ease->u.steps.steps;
378 g_assert_not_reached ();