/* GTK - The GIMP Toolkit
* Copyright ( C ) 2011 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 "gtkcsskeyframesprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkcssarrayvalueprivate.h"
#include "gtkcsscustompropertypoolprivate.h"
#include "gtkcssreferencevalueprivate.h"
#include "gtkcssshorthandpropertyprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkstylepropertyprivate.h"
#include "gtkprivate.h"
#include <stdlib.h>
#include <string.h>
struct _GtkCssKeyframes {
int ref_count; /* ref count */
int n_keyframes; /* number of keyframes (at least 2 for 0% and 100% */
double *keyframe_progress; /* ordered array of n_keyframes of [0..1] */
int n_properties; /* number of properties used by keyframes */
guint *property_ids; /* ordered array of n_properties property ids */
GtkCssValue **values; /* 2D array: n_keyframes * n_properties of (value or NULL) for all the keyframes */
GtkCssVariableSet **variables; /* array of variable sets for each keyframe */
int *variable_ids; /* ordered array of variable ids */
int n_variables; /* number of variable used by keyframes */
};
#define KEYFRAMES_VALUE(keyframes, k, p) ((keyframes)->values[(k) * (keyframes)->n_properties + (p)])
GtkCssKeyframes *
_gtk_css_keyframes_ref (GtkCssKeyframes *keyframes)
{
g_return_val_if_fail (keyframes != NULL, NULL);
keyframes->ref_count++;
return keyframes;
}
void
_gtk_css_keyframes_unref (GtkCssKeyframes *keyframes)
{
guint k, p;
g_return_if_fail (keyframes != NULL);
keyframes->ref_count--;
if (keyframes->ref_count > 0 )
return ;
g_free (keyframes->keyframe_progress);
g_free (keyframes->property_ids);
for (k = 0 ; k < keyframes->n_keyframes; k++)
{
for (p = 0 ; p < keyframes->n_properties; p++)
{
gtk_css_value_unref (KEYFRAMES_VALUE (keyframes, k, p));
KEYFRAMES_VALUE (keyframes, k, p) = NULL;
}
if (keyframes->variables && keyframes->variables[k])
gtk_css_variable_set_unref (keyframes->variables[k]);
}
g_free (keyframes->values);
g_free (keyframes->variables);
g_free (keyframes->variable_ids);
g_free (keyframes);
}
static guint
gtk_css_keyframes_add_keyframe (GtkCssKeyframes *keyframes,
double progress)
{
guint k, p;
for (k = 0 ; k < keyframes->n_keyframes; k++)
{
if (keyframes->keyframe_progress[k] == progress)
{
for (p = 0 ; p < keyframes->n_properties; p++)
{
if (KEYFRAMES_VALUE (keyframes, k, p) == NULL)
continue ;
gtk_css_value_unref (KEYFRAMES_VALUE (keyframes, k, p));
KEYFRAMES_VALUE (keyframes, k, p) = NULL;
/* XXX: GC properties that are now unset
* in all keyframes ?
*/
}
return k;
}
else if (keyframes->keyframe_progress[k] > progress)
break ;
}
keyframes->n_keyframes++;
keyframes->keyframe_progress = g_realloc (keyframes->keyframe_progress, sizeof (double ) * keyframes->n_keyframes);
memmove (keyframes->keyframe_progress + k + 1 , keyframes->keyframe_progress + k, sizeof (double ) * (keyframes->n_keyframes - k - 1 ));
keyframes->keyframe_progress[k] = progress;
if (keyframes->n_properties)
{
gsize size = sizeof (GtkCssValue *) * keyframes->n_properties;
keyframes->values = g_realloc (keyframes->values, sizeof (GtkCssValue *) * keyframes->n_keyframes * keyframes->n_properties);
memmove (&KEYFRAMES_VALUE (keyframes, k + 1 , 0 ), &KEYFRAMES_VALUE (keyframes, k, 0 ), size * (keyframes->n_keyframes - k - 1 ));
memset (&KEYFRAMES_VALUE (keyframes, k, 0 ), 0 , size);
}
if (keyframes->variables)
{
keyframes->variables = g_realloc (keyframes->variables, sizeof (GtkCssVariableSet *) * keyframes->n_keyframes);
keyframes->variables[keyframes->n_keyframes - 1 ] = NULL;
}
return k;
}
static guint
gtk_css_keyframes_lookup_property (GtkCssKeyframes *keyframes,
guint property_id)
{
guint p;
for (p = 0 ; p < keyframes->n_properties; p++)
{
if (keyframes->property_ids[p] == property_id)
return p;
else if (keyframes->property_ids[p] > property_id)
break ;
}
keyframes->n_properties++;
keyframes->property_ids = g_realloc (keyframes->property_ids, sizeof (guint) * keyframes->n_properties);
memmove (keyframes->property_ids + p + 1 , keyframes->property_ids + p, sizeof (guint) * (keyframes->n_properties - p - 1 ));
keyframes->property_ids[p] = property_id;
if (keyframes->n_properties > 1 )
{
guint old_n_properties = keyframes->n_properties - 1 ;
int k;
keyframes->values = g_realloc (keyframes->values, sizeof (GtkCssValue *) * keyframes->n_keyframes * keyframes->n_properties);
if (p + 1 < keyframes->n_properties)
{
memmove (&KEYFRAMES_VALUE (keyframes, keyframes->n_keyframes - 1 , p + 1 ),
&keyframes->values[(keyframes->n_keyframes - 1 ) * old_n_properties + p],
sizeof (GtkCssValue *) * (keyframes->n_properties - p - 1 ));
}
KEYFRAMES_VALUE (keyframes, keyframes->n_keyframes - 1 , p) = NULL;
for (k = keyframes->n_keyframes - 2 ; k >= 0 ; k--)
{
memmove (&KEYFRAMES_VALUE (keyframes, k, p + 1 ),
&keyframes->values[k * old_n_properties + p],
sizeof (GtkCssValue *) * old_n_properties);
KEYFRAMES_VALUE (keyframes, k, p) = NULL;
}
}
else
{
keyframes->values = g_new0 (GtkCssValue *, keyframes->n_keyframes);
}
return p;
}
static void
gtk_css_keyframes_register_variable (GtkCssKeyframes *keyframes,
int variable_id)
{
guint p;
for (p = 0 ; p < keyframes->n_variables; p++)
{
if (keyframes->variable_ids[p] == variable_id)
return ;
else if (keyframes->variable_ids[p] > variable_id)
break ;
}
keyframes->n_variables++;
keyframes->variable_ids = g_realloc (keyframes->variable_ids, sizeof (int ) * keyframes->n_variables);
memmove (keyframes->variable_ids + p + 1 , keyframes->variable_ids + p, sizeof (int ) * (keyframes->n_variables - p - 1 ));
keyframes->variable_ids[p] = variable_id;
}
static GtkCssKeyframes *
gtk_css_keyframes_alloc (void )
{
GtkCssKeyframes *keyframes;
keyframes = g_new0 (GtkCssKeyframes, 1 );
keyframes->ref_count = 1 ;
return keyframes;
}
static GtkCssKeyframes *
gtk_css_keyframes_new (void )
{
GtkCssKeyframes *keyframes;
keyframes = gtk_css_keyframes_alloc ();
gtk_css_keyframes_add_keyframe (keyframes, 0 );
gtk_css_keyframes_add_keyframe (keyframes, 1 );
return keyframes;
}
static gboolean
keyframes_set_value (GtkCssKeyframes *keyframes,
guint k,
GtkCssStyleProperty *property,
GtkCssValue *value)
{
guint p;
if (!_gtk_css_style_property_is_animated (property))
return FALSE ;
p = gtk_css_keyframes_lookup_property (keyframes, _gtk_css_style_property_get_id (property));
if (KEYFRAMES_VALUE (keyframes, k, p))
gtk_css_value_unref (KEYFRAMES_VALUE (keyframes, k, p));
KEYFRAMES_VALUE (keyframes, k, p) = gtk_css_value_ref (value);
return TRUE ;
}
static gboolean
gtk_css_keyframes_parse_declaration (GtkCssKeyframes *keyframes,
guint k,
GtkCssParser *parser)
{
GtkStyleProperty *property;
GtkCssValue *value;
char *name;
name = gtk_css_parser_consume_ident (parser);
if (name == NULL)
{
if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
gtk_css_parser_error_syntax (parser, "Expected a property name" );
return FALSE ;
}
/* This is a custom property */
if (name[0 ] == '-' && name[1 ] == '-' )
{
GtkCssVariableValue *var_value;
GtkCssCustomPropertyPool *pool;
int id;
if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
{
gtk_css_parser_error_syntax (parser, "Expected a ':'" );
g_free (name);
return FALSE ;
}
var_value = gtk_css_parser_parse_value_into_token_stream (parser);
if (var_value == NULL)
{
g_free (name);
return FALSE ;
}
if (!keyframes->variables)
keyframes->variables = g_new0 (GtkCssVariableSet *, keyframes->n_keyframes);
if (!keyframes->variables[k])
keyframes->variables[k] = gtk_css_variable_set_new ();
pool = gtk_css_custom_property_pool_get ();
id = gtk_css_custom_property_pool_add (pool, name);
gtk_css_keyframes_register_variable (keyframes, id);
gtk_css_variable_value_taint (var_value);
gtk_css_variable_set_add (keyframes->variables[k], id, var_value);
gtk_css_custom_property_pool_unref (pool, id);
g_free (name);
return TRUE ;
}
property = _gtk_style_property_lookup (name);
if (property == NULL)
{
gtk_css_parser_error_value (parser, "No property named '%s'" , name);
g_free (name);
return FALSE ;
}
g_free (name);
if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
{
gtk_css_parser_error_syntax (parser, "Expected a ':'" );
return FALSE ;
}
if (gtk_css_parser_has_references (parser))
{
GtkCssVariableValue *var_value;
var_value = gtk_css_parser_parse_value_into_token_stream (parser);
if (var_value == NULL)
return FALSE ;
if (GTK_IS_CSS_SHORTHAND_PROPERTY (property))
{
GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property);
guint i, n;
GtkCssValue **values;
n = _gtk_css_shorthand_property_get_n_subproperties (shorthand);
values = g_new (GtkCssValue *, n);
for (i = 0 ; i < n; i++)
{
GtkCssValue *child =
_gtk_css_reference_value_new (property,
var_value,
gtk_css_parser_get_file (parser));
_gtk_css_reference_value_set_subproperty (child, i);
values[i] = _gtk_css_array_value_get_nth (child, i);
}
value = _gtk_css_array_value_new_from_array (values, n);
g_free (values);
}
else
{
value = _gtk_css_reference_value_new (property,
var_value,
gtk_css_parser_get_file (parser));
}
gtk_css_variable_value_unref (var_value);
}
else
{
value = _gtk_style_property_parse_value (property, parser);
if (value == NULL)
return FALSE ;
}
if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
{
gtk_css_parser_error_syntax (parser, "Junk at end of value" );
gtk_css_value_unref (value);
return FALSE ;
}
if (GTK_IS_CSS_SHORTHAND_PROPERTY (property))
{
GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property);
gboolean animatable = FALSE ;
guint i;
for (i = 0 ; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++)
{
GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
GtkCssValue *sub = _gtk_css_array_value_get_nth (value, i);
animatable |= keyframes_set_value (keyframes, k, child, sub);
}
if (!animatable)
gtk_css_parser_error_value (parser, "shorthand '%s' cannot be animated" , _gtk_style_property_get_name (property));
}
else if (GTK_IS_CSS_STYLE_PROPERTY (property))
{
if (!keyframes_set_value (keyframes, k, GTK_CSS_STYLE_PROPERTY (property), value))
gtk_css_parser_error_value (parser, "Cannot animate property '%s'" , _gtk_style_property_get_name (property));
}
else
{
g_assert_not_reached ();
}
gtk_css_value_unref (value);
return TRUE ;
}
static guint
gtk_css_keyframes_copy_keyframe (GtkCssKeyframes *keyframes,
guint k,
double progress)
{
guint l = gtk_css_keyframes_add_keyframe (keyframes, progress);
if (l <= k)
k++;
if (keyframes->variables && keyframes->variables[k])
keyframes->variables[l] = gtk_css_variable_set_copy (keyframes->variables[k]);
for (guint p = 0 ; p < keyframes->n_properties; p++)
{
GtkCssValue *value = KEYFRAMES_VALUE (keyframes, k, p);
if (value)
KEYFRAMES_VALUE (keyframes, l, p) = gtk_css_value_ref (value);
}
return l;
}
static gboolean
gtk_css_keyframes_parse_block (GtkCssKeyframes *keyframes,
guint k,
GtkCssParser *parser)
{
if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_OPEN_CURLY))
{
gtk_css_parser_error_syntax (parser, "Expected '{'" );
return FALSE ;
}
gtk_css_parser_start_block (parser);
while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
{
gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_EOF);
gtk_css_keyframes_parse_declaration (keyframes, k, parser);
gtk_css_parser_end_block (parser);
}
if (keyframes->variables && keyframes->variables[k])
gtk_css_variable_set_resolve_cycles (keyframes->variables[k]);
gtk_css_parser_end_block (parser);
return TRUE ;
}
GtkCssKeyframes *
_gtk_css_keyframes_parse (GtkCssParser *parser)
{
GtkCssKeyframes *keyframes;
double progress;
guint k;
GArray *selectors;
g_return_val_if_fail (parser != NULL, NULL);
keyframes = gtk_css_keyframes_new ();
selectors = g_array_new (FALSE , FALSE , sizeof (double ));
while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
{
g_array_set_size (selectors, 0 );
while (TRUE )
{
if (gtk_css_parser_try_ident (parser, "from" ))
progress = 0 ;
else if (gtk_css_parser_try_ident (parser, "to" ))
progress = 1 ;
else if (gtk_css_parser_consume_percentage (parser, &progress))
{
if (progress < 0 || progress > 100 )
{
/* XXX: should we skip over the block here? */
gtk_css_parser_error_value (parser, "percentages must be between 0%% and 100%%" );
g_clear_pointer (&keyframes, _gtk_css_keyframes_unref);
goto done;
}
progress /= 100 ;
}
else
{
gtk_css_parser_error_value (parser, "failed to parse keyframe selector" );
g_clear_pointer (&keyframes, _gtk_css_keyframes_unref);
goto done;
}
g_array_append_val (selectors, progress);
if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA))
break ;
gtk_css_parser_consume_token (parser);
}
k = gtk_css_keyframes_add_keyframe (keyframes, g_array_index (selectors, double , 0 ));
if (!gtk_css_keyframes_parse_block (keyframes, k, parser))
{
g_clear_pointer (&keyframes, _gtk_css_keyframes_unref);
goto done;
}
for (guint i = 1 ; i < selectors->len; i++)
k = gtk_css_keyframes_copy_keyframe (keyframes, k, g_array_index (selectors, double , i));
}
done:
g_array_unref (selectors);
return keyframes;
}
static int
compare_property_by_name (gconstpointer a,
gconstpointer b,
gpointer data)
{
GtkCssKeyframes *keyframes = data;
return strcmp (_gtk_style_property_get_name (GTK_STYLE_PROPERTY (
_gtk_css_style_property_lookup_by_id (keyframes->property_ids[*(const guint *) a]))),
_gtk_style_property_get_name (GTK_STYLE_PROPERTY (
_gtk_css_style_property_lookup_by_id (keyframes->property_ids[*(const guint *) b]))));
}
static int
compare_custom_property_ids (gconstpointer a, gconstpointer b, gpointer user_data)
{
GtkCssCustomPropertyPool *pool = user_data;
int id1 = GPOINTER_TO_INT (*((const int *) a));
int id2 = GPOINTER_TO_INT (*((const int *) b));
const char *name1, *name2;
name1 = gtk_css_custom_property_pool_get_name (pool, id1);
name2 = gtk_css_custom_property_pool_get_name (pool, id2);
return strcmp (name1, name2);
}
void
_gtk_css_keyframes_print (GtkCssKeyframes *keyframes,
GString *string)
{
GtkCssCustomPropertyPool *pool = gtk_css_custom_property_pool_get ();
guint k, p;
guint *sorted, *sorted_variable_ids = NULL;
g_return_if_fail (keyframes != NULL);
g_return_if_fail (string != NULL);
sorted = g_new (guint, keyframes->n_properties);
for (p = 0 ; p < keyframes->n_properties; p++)
sorted[p] = p;
g_sort_array (sorted, keyframes->n_properties, sizeof (guint), compare_property_by_name, keyframes);
if (keyframes->variable_ids)
{
sorted_variable_ids = g_memdup2 (keyframes->variable_ids,
sizeof (int ) * keyframes->n_variables);
g_sort_array (sorted_variable_ids, keyframes->n_variables, sizeof (int ), compare_custom_property_ids, pool);
}
for (k = 0 ; k < keyframes->n_keyframes; k++)
{
/* useful for 0% and 100% which might be empty */
gboolean opened = FALSE ;
for (p = 0 ; p < keyframes->n_properties; p++)
{
if (KEYFRAMES_VALUE (keyframes, k, sorted[p]) == NULL)
continue ;
if (!opened)
{
if (keyframes->keyframe_progress[k] == 0 .0 )
g_string_append (string, " from {\n" );
else if (keyframes->keyframe_progress[k] == 1 .0 )
g_string_append (string, " to {\n" );
else
g_string_append_printf (string, " %g%% {\n" , keyframes->keyframe_progress[k] * 100 );
opened = TRUE ;
}
g_string_append_printf (string, " %s: " , _gtk_style_property_get_name (
GTK_STYLE_PROPERTY (
_gtk_css_style_property_lookup_by_id (
keyframes->property_ids[sorted[p]]))));
gtk_css_value_print (KEYFRAMES_VALUE (keyframes, k, sorted[p]), string);
g_string_append (string, ";\n" );
}
if (keyframes->variables && keyframes->variables[k])
{
for (p = 0 ; p < keyframes->n_variables; p++)
{
int variable_id = sorted_variable_ids[p];
GtkCssVariableValue *value =
gtk_css_variable_set_lookup (keyframes->variables[k], variable_id, NULL);
const char *name;
if (value == NULL)
continue ;
if (!opened)
{
if (keyframes->keyframe_progress[k] == 0 .0 )
g_string_append (string, " from {\n" );
else if (keyframes->keyframe_progress[k] == 1 .0 )
g_string_append (string, " to {\n" );
else
g_string_append_printf (string, " %g%% {\n" , keyframes->keyframe_progress[k] * 100 );
opened = TRUE ;
}
name = gtk_css_custom_property_pool_get_name (pool, variable_id);
g_string_append_printf (string, " %s: " , name);
gtk_css_variable_value_print (value, string);
g_string_append (string, ";\n" );
}
}
if (opened)
g_string_append (string, " }\n" );
}
g_free (sorted);
g_free (sorted_variable_ids);
}
GtkCssKeyframes *
_gtk_css_keyframes_compute (GtkCssKeyframes *keyframes,
GtkStyleProvider *provider,
GtkCssStyle *style,
GtkCssStyle *parent_style)
{
GtkCssComputeContext context = { NULL, };
GtkCssKeyframes *resolved;
guint k, p;
g_return_val_if_fail (keyframes != NULL, NULL);
g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
g_return_val_if_fail (parent_style == NULL || GTK_IS_CSS_STYLE (parent_style), NULL);
resolved = gtk_css_keyframes_alloc ();
resolved->n_keyframes = keyframes->n_keyframes;
resolved->keyframe_progress = g_memdup2 (keyframes->keyframe_progress, keyframes->n_keyframes * sizeof (double ));
resolved->n_properties = keyframes->n_properties;
resolved->property_ids = g_memdup2 (keyframes->property_ids, keyframes->n_properties * sizeof (guint));
resolved->values = g_new0 (GtkCssValue *, resolved->n_keyframes * resolved->n_properties);
context.provider = provider;
context.style = style;
context.parent_style = parent_style;
for (p = 0 ; p < resolved->n_properties; p++)
{
for (k = 0 ; k < resolved->n_keyframes; k++)
{
if (KEYFRAMES_VALUE (keyframes, k, p) == NULL)
continue ;
context.variables = keyframes->variables ? keyframes->variables[k] : NULL;
KEYFRAMES_VALUE (resolved, k, p) = gtk_css_value_compute (KEYFRAMES_VALUE (keyframes, k, p),
resolved->property_ids[p],
&context);
}
}
if (keyframes->variables)
{
resolved->variables = g_new0 (GtkCssVariableSet *, resolved->n_keyframes);
for (k = 0 ; k < resolved->n_keyframes; k++)
{
if (keyframes->variables[k])
resolved->variables[k] = gtk_css_variable_set_ref (keyframes->variables[k]);
}
}
else
resolved->variables = NULL;
resolved->variable_ids = g_memdup2 (keyframes->variable_ids, keyframes->n_variables * sizeof (int ));
resolved->n_variables = keyframes->n_variables;
return resolved;
}
guint
_gtk_css_keyframes_get_n_properties (GtkCssKeyframes *keyframes)
{
g_return_val_if_fail (keyframes != NULL, 0 );
return keyframes->n_properties;
}
guint
_gtk_css_keyframes_get_property_id (GtkCssKeyframes *keyframes,
guint id)
{
g_return_val_if_fail (keyframes != NULL, 0 );
g_return_val_if_fail (id < keyframes->n_properties, 0 );
return keyframes->property_ids[id];
}
GtkCssValue *
_gtk_css_keyframes_get_value (GtkCssKeyframes *keyframes,
guint id,
double progress,
GtkCssValue *default_value)
{
GtkCssValue *start_value, *end_value, *result;
double start_progress, end_progress;
guint k;
g_return_val_if_fail (keyframes != NULL, 0 );
g_return_val_if_fail (id < keyframes->n_properties, 0 );
start_value = default_value;
start_progress = 0 .0 ;
end_value = default_value;
end_progress = 1 .0 ;
for (k = 0 ; k < keyframes->n_keyframes; k++)
{
if (KEYFRAMES_VALUE (keyframes, k, id) == NULL)
continue ;
if (keyframes->keyframe_progress[k] == progress)
{
return gtk_css_value_ref (KEYFRAMES_VALUE (keyframes, k, id));
}
else if (keyframes->keyframe_progress[k] < progress)
{
start_value = KEYFRAMES_VALUE (keyframes, k, id);
start_progress = keyframes->keyframe_progress[k];
}
else
{
end_value = KEYFRAMES_VALUE (keyframes, k, id);
end_progress = keyframes->keyframe_progress[k];
break ;
}
}
progress = (progress - start_progress) / (end_progress - start_progress);
result = gtk_css_value_transition (start_value,
end_value,
keyframes->property_ids[id],
progress);
/* XXX: Dear spec, what's the correct thing to do here? */
if (result == NULL)
return gtk_css_value_ref (start_value);
return result;
}
guint
_gtk_css_keyframes_get_n_variables (GtkCssKeyframes *keyframes)
{
g_return_val_if_fail (keyframes != NULL, 0 );
return keyframes->n_variables;
}
int
_gtk_css_keyframes_get_variable_id (GtkCssKeyframes *keyframes,
guint id)
{
g_return_val_if_fail (keyframes != NULL, 0 );
g_return_val_if_fail (id < keyframes->n_variables, 0 );
return keyframes->variable_ids[id];
}
GtkCssVariableValue *
_gtk_css_keyframes_get_variable (GtkCssKeyframes *keyframes,
guint id,
double progress,
GtkCssVariableValue *default_value)
{
GtkCssVariableValue *start_value, *end_value, *result;
double start_progress, end_progress;
int variable_id;
guint k;
g_return_val_if_fail (keyframes != NULL, 0 );
g_return_val_if_fail (id < keyframes->n_variables, 0 );
start_value = default_value;
start_progress = 0 .0 ;
end_value = default_value;
end_progress = 1 .0 ;
variable_id = keyframes->variable_ids[id];
for (k = 0 ; k < keyframes->n_keyframes; k++)
{
GtkCssVariableValue *value;
if (keyframes->variables[k] == NULL)
continue ;
value = gtk_css_variable_set_lookup (keyframes->variables[k], variable_id, NULL);
if (value == NULL)
continue ;
if (keyframes->keyframe_progress[k] == progress)
{
return gtk_css_variable_value_ref (value);
}
else if (keyframes->keyframe_progress[k] < progress)
{
start_value = value;
start_progress = keyframes->keyframe_progress[k];
}
else
{
end_value = value;
end_progress = keyframes->keyframe_progress[k];
break ;
}
}
progress = (progress - start_progress) / (end_progress - start_progress);
result = gtk_css_variable_value_transition (start_value,
end_value,
progress);
/* XXX: Dear spec, what's the correct thing to do here? */
if (result == NULL)
return start_value ? gtk_css_variable_value_ref (start_value) : NULL;
return result;
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland