Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gtksvganimation.c

  Sprache: C
 

/*
 * Copyright © 2025 Red Hat, Inc
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * 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.1 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/>.
 *
 * Authors: Matthias Clasen <mclasen@redhat.com>
 */


#include "gtksvganimationprivate.h"
#include "gtksvgvalueprivate.h"
#include "gtksvgelementinternal.h"
#include "gtksvgtimespecprivate.h"
#include "gtksvgutilsprivate.h"
#include "gtksvgkeywordprivate.h"
#include "gtksvgclipprivate.h"
#include "gtksvgmaskprivate.h"
#include "gtksvgfilterfunctionsprivate.h"
#include "gtksvgpaintprivate.h"
#include "gtksvghrefprivate.h"


CalcMode
svg_animation_type_default_calc_mode (AnimationType type)
{
  switch ((unsigned int) type)
    {
    case ANIMATION_TYPE_SET: return CALC_MODE_DISCRETE;
    case ANIMATION_TYPE_MOTION: return CALC_MODE_PACED;
    defaultreturn CALC_MODE_LINEAR;
    }
}

static void
svg_animation_init (SvgAnimation  *animation,
                    AnimationType  type)
{
  animation->status = ANIMATION_STATUS_INACTIVE;

  animation->type = type;

  animation->simple_duration = INDEFINITE;
  animation->repeat_count = REPEAT_FOREVER;
  animation->repeat_duration = INDEFINITE;

  animation->fill = ANIMATION_FILL_REMOVE;
  animation->restart = ANIMATION_RESTART_ALWAYS;
  animation->calc_mode = svg_animation_type_default_calc_mode (type);
  animation->additive = ANIMATION_ADDITIVE_REPLACE;
  animation->accumulate = ANIMATION_ACCUMULATE_NONE;

  animation->color_interpolation = COLOR_INTERPOLATION_SRGB;

  animation->begin = g_ptr_array_new ();
  animation->end = g_ptr_array_new ();

  animation->current.begin = INDEFINITE;
  animation->current.end = INDEFINITE;
  animation->previous.begin = 0;
  animation->previous.end = 0;

  if (type == ANIMATION_TYPE_MOTION)
    {
      animation->attr = SVG_PROPERTY_TRANSFORM;
      animation->motion.rotate = ROTATE_FIXED;
      animation->motion.angle = 0;
    }
}

void
svg_animation_add_dep (SvgAnimation *base,
                       SvgAnimation *anim)
{
  if (!base->deps)
    base->deps = g_ptr_array_new ();

  g_ptr_array_add (base->deps, anim);
}

SvgAnimation *
svg_animation_new (AnimationType type)
{
  SvgAnimation *animation = g_new0 (SvgAnimation, 1);
  svg_animation_init (animation, type);
  return animation;
}

void
svg_animation_free (SvgAnimation *animation)
{
  g_free (animation->id);
  g_free (animation->href);

  g_clear_pointer (&animation->begin, g_ptr_array_unref);
  g_clear_pointer (&animation->end, g_ptr_array_unref);

  if (animation->type != ANIMATION_TYPE_MOTION)
    {
      for (unsigned int i = 0; i < animation->n_frames; i++)
        svg_value_unref (animation->frames[i].value);
    }
  g_free (animation->frames);

  g_free (animation->motion.path_ref);
  g_clear_pointer (&animation->motion.path, gsk_path_unref);
  g_clear_pointer (&animation->motion.measure, gsk_path_measure_unref);

  g_clear_pointer (&animation->deps, g_ptr_array_unref);

  g_free (animation);
}

void
svg_animation_drop_and_free (SvgAnimation *animation)
{
  for (unsigned int k = 0; k < 2; k++)
    {
      GPtrArray *specs = k == 0 ? animation->begin : animation->end;
      for (unsigned int i = 0; i < specs->len; i++)
        {
          TimeSpec *spec = g_ptr_array_index (specs, i);
          time_spec_drop_animation (spec, animation);
        }
    }
  svg_animation_free (animation);
}

TimeSpec *
svg_animation_add_begin (SvgAnimation *animation,
                         TimeSpec     *spec)
{
  g_ptr_array_add (animation->begin, spec);
  return spec;
}

TimeSpec *
svg_animation_add_end (SvgAnimation *animation,
                       TimeSpec     *spec)
{
  g_ptr_array_add (animation->end, spec);
  return spec;
}

gboolean
svg_animation_has_begin (SvgAnimation *animation,
                         TimeSpec     *spec)
{
  return g_ptr_array_find (animation->begin, spec, NULL);
}

gboolean
svg_animation_has_end (SvgAnimation *animation,
                       TimeSpec     *spec)
{
  return g_ptr_array_find (animation->end, spec, NULL);
}

void
svg_animation_fill_from_values (SvgAnimation  *animation,
                                double        *times,
                                unsigned int   n_frames,
                                SvgValue     **values,
                                double        *params,
                                double        *points)
{
  double linear[] = { 0011 };

  g_assert (n_frames > 1);

  animation->n_frames = n_frames;
  animation->frames = g_new0 (Frame, n_frames);

  for (unsigned int i = 0; i < n_frames; i++)
    {
      animation->frames[i].time = times[i];
      if (values)
        animation->frames[i].value = svg_value_ref (values[i]);
      else
        animation->frames[i].value = NULL;
      if (i + 1 < n_frames && params)
        memcpy (animation->frames[i].params, ¶ms[4 * i], sizeof (double) * 4);
      else
        memcpy (animation->frames[i].params, linear, sizeof (double) * 4);
      if (points)
        animation->frames[i].point = points[i];
      else
        animation->frames[i].point = i / (double) (n_frames - 1);
    }
}

void
svg_animation_motion_fill_from_path (SvgAnimation *animation,
                                     GskPath      *path)
{
  GArray *frames;
  Frame frame = { .value = NULL, .params = { 0011 } };
  Frame *last;
  GskPathPoint point;
  GskPathMeasure *measure;
  double length;
  double distance, previous_distance;

  g_assert (animation->type == ANIMATION_TYPE_MOTION);

  if (!gsk_path_get_start_point (path, &point))
    return;

  measure = gsk_path_measure_new (path);

  frames = g_array_new (FALSEFALSEsizeof (Frame));

  frame.time = 0;
  frame.point = 0;
  g_array_append_val (frames, frame);

  length = gsk_path_measure_get_length (measure);

  previous_distance = 0;
  while (gsk_path_get_next (path, &point))
    {
      distance = gsk_path_point_get_distance (&point, measure);
      frame.point = distance / length;
      frame.time += (distance - previous_distance) / length;
      g_array_append_val (frames, frame);
      previous_distance = distance;
    }

  last = &g_array_index (frames, Frame, frames->len - 1);
  last->point = 1;
  last->time = 1;

  gsk_path_measure_unref (measure);

  animation->n_frames = frames->len;
  animation->frames = (Frame *) g_array_free (frames, FALSE);
}

static GskPath *
animation_motion_get_path (SvgAnimation          *animation,
                           const graphene_rect_t *viewport,
                           gboolean               current)
{
  g_assert (animation->type == ANIMATION_TYPE_MOTION);

  if (animation->motion.path_shape)
    return svg_element_get_path (animation->motion.path_shape, viewport, current);
  else if (animation->motion.path)
    return gsk_path_ref (animation->motion.path);
  else
    return NULL;
}

GskPath *
svg_animation_motion_get_base_path (SvgAnimation          *animation,
                                    const graphene_rect_t *viewport)
{
  return animation_motion_get_path (animation, viewport, FALSE);
}

GskPath *
svg_animation_motion_get_current_path (SvgAnimation          *animation,
                                       const graphene_rect_t *viewport)
{
  return animation_motion_get_path (animation, viewport, TRUE);
}

GskPathMeasure *
svg_animation_motion_get_current_measure (SvgAnimation          *animation,
                                          const graphene_rect_t *viewport)
{
  g_assert (animation->type == ANIMATION_TYPE_MOTION);

  if (animation->motion.path_shape)
    {
      return svg_element_get_current_measure (animation->motion.path_shape, viewport);
    }
  else if (animation->motion.path)
    {
      if (!animation->motion.measure)
        animation->motion.measure = gsk_path_measure_new (animation->motion.path);
      return gsk_path_measure_ref (animation->motion.measure);
    }
  else
    {
      return NULL;
    }
}

void
svg_animation_update_for_pause (SvgAnimation *animation,
                                int64_t       duration)
{
  if (animation->current.begin != INDEFINITE)
    animation->current.begin += duration;
  if (animation->current.end != INDEFINITE)
    animation->current.end += duration;
  if (animation->previous.begin != INDEFINITE)
    animation->previous.begin += duration;
  if (animation->previous.end != INDEFINITE)
    animation->previous.end += duration;
}

static gboolean
frame_equal (Frame *f1,
             Frame *f2)
{
  return svg_value_equal (f1->value, f2->value) &&
         f1->time == f2->time &&
         f1->point == f2->point &&
         memcmp (f1->params, f2->params, sizeof (double) * 4) == 0;
}

gboolean
svg_animation_equal (SvgAnimation *animation1,
                     SvgAnimation *animation2)
{
  if (animation1->type != animation2->type ||
      g_strcmp0 (animation1->id, animation2->id) != 0 ||
      animation1->attr != animation2->attr ||
      animation1->simple_duration != animation2->simple_duration ||
      animation1->repeat_count != animation2->repeat_count ||
      animation1->repeat_duration != animation2->repeat_duration ||
      animation1->fill != animation2->fill ||
      animation1->restart != animation2->restart ||
      animation1->additive != animation2->additive ||
      animation1->accumulate != animation2->accumulate ||
      animation1->calc_mode != animation2->calc_mode ||
      animation1->n_frames != animation2->n_frames)
    return FALSE;

  for (unsigned int i = 0; i < animation1->n_frames; i++)
    {
      if (!frame_equal (&animation1->frames[i], &animation2->frames[i]))
        return FALSE;
    }

  if (animation1->type == ANIMATION_TYPE_MOTION)
    {
      if (g_strcmp0 (animation1->motion.path_ref, animation2->motion.path_ref) != 0 ||
          !(animation1->motion.path == animation2->motion.path ||
            (animation1->motion.path && animation2->motion.path &&
             gsk_path_equal (animation1->motion.path, animation2->motion.path))) ||
          animation1->motion.rotate != animation2->motion.rotate ||
          animation1->motion.angle != animation2->motion.angle)
        return FALSE;
    }

  return TRUE;
}

SvgAnimation *
svg_animation_clone (SvgAnimation *a,
                     SvgElement   *parent,
                     Timeline     *timeline)
{
  SvgAnimation *clone = g_new0 (SvgAnimation, 1);

  clone->type = a->type;
  clone->status = ANIMATION_STATUS_INACTIVE;

  clone->id = NULL;
  clone->href = NULL;
  clone->line = a->line;

  clone->shape = parent;
  clone->attr = a->attr;
  clone->idx = a->idx;

  clone->has_simple_duration = a->has_simple_duration;
  clone->has_repeat_count = a->has_repeat_count;
  clone->has_repeat_duration = a->has_repeat_duration;
  clone->has_begin = a->has_begin;
  clone->has_end = a->has_end;

  clone->begin = g_ptr_array_new ();
  for (unsigned int i = 0; i < a->begin->len; i++)
    {
      TimeSpec *spec = g_ptr_array_index (a->begin, i);
      TimeSpec *p, *tmp;
      switch (spec->type)
        {
        case TIME_SPEC_TYPE_INDEFINITE:
        case TIME_SPEC_TYPE_OFFSET:
        case TIME_SPEC_TYPE_STATES:
          p = spec;
          break;
        case TIME_SPEC_TYPE_SYNC:
        case TIME_SPEC_TYPE_EVENT:
          tmp = time_spec_copy (spec);
          tmp->offset++; /* force a copy */
          p = timeline_get_time_spec (timeline, tmp);
          g_assert (p != spec);
          p->offset = spec->offset;
          time_spec_free (tmp);
          break;
        default:
          g_assert_not_reached ();
        }
      time_spec_add_animation (p, clone);
      svg_animation_add_begin (clone, p);
    }

  clone->end = g_ptr_array_new ();
  for (unsigned int i = 0; i < a->end->len; i++)
    {
      TimeSpec *spec = g_ptr_array_index (a->end, i);
      TimeSpec *p, *tmp;
      switch (spec->type)
        {
        case TIME_SPEC_TYPE_INDEFINITE:
        case TIME_SPEC_TYPE_OFFSET:
        case TIME_SPEC_TYPE_SYNC:
        case TIME_SPEC_TYPE_STATES:
          p = spec;
          break;
        case TIME_SPEC_TYPE_EVENT:
          tmp = time_spec_copy (spec);
          p = timeline_get_time_spec (timeline, tmp);
          time_spec_free (tmp);
          break;
        default:
          g_assert_not_reached ();
        }
      time_spec_add_animation (p, clone);
      svg_animation_add_end (clone, p);
    }

  clone->current.begin = INDEFINITE;
  clone->current.end = INDEFINITE;
  clone->previous.begin = 0;
  clone->previous.end = 0;

  clone->simple_duration = a->simple_duration;
  clone->repeat_count = a->repeat_count;
  clone->repeat_duration = a->repeat_duration;
  clone->color_interpolation = a->color_interpolation;

  clone->fill = a->fill;
  clone->restart = a->restart;
  clone->additive = a->additive;
  clone->accumulate = a->accumulate;

  clone->calc_mode = a->calc_mode;
  clone->frames = g_new (Frame, a->n_frames);
  memcpy (clone->frames, a->frames, sizeof (Frame) * a->n_frames);
  for (unsigned int i = 0; i < a->n_frames; i++)
    svg_value_ref (clone->frames[i].value);
  clone->n_frames = a->n_frames;

  if (a->type == ANIMATION_TYPE_MOTION)
    {
      clone->motion.path_ref = g_strdup (a->motion.path_ref);
      clone->motion.path_shape = a->motion.path_shape;
      if (a->motion.path)
        clone->motion.path = gsk_path_ref (a->motion.path);
      if (a->motion.measure)
        clone->motion.measure = gsk_path_measure_ref (a->motion.measure);
      clone->motion.rotate = a->motion.rotate;
      clone->motion.angle = a->motion.angle;
    }

  clone->deps = NULL;

  clone->gpa.transition = a->gpa.transition;
  clone->gpa.animation = a->gpa.animation;
  clone->gpa.easing = a->gpa.easing;
  clone->gpa.origin = a->gpa.origin;
  clone->gpa.segment = a->gpa.segment;
  clone->gpa.attach_pos = a->gpa.attach_pos;

  return clone;
}

static gboolean
animation_can_start (SvgAnimation *a)
{
  /* Handling restart */
  switch (a->status)
    {
    case ANIMATION_STATUS_INACTIVE:
      break;
    case ANIMATION_STATUS_RUNNING:
      if (a->restart != ANIMATION_RESTART_ALWAYS)
        return FALSE;
      break;
    case ANIMATION_STATUS_DONE:
      if (a->restart == ANIMATION_RESTART_NEVER)
        return FALSE;
      break;
    default:
      g_assert_not_reached ();
    }

  return TRUE;
}

static void
time_specs_update_for_base (GPtrArray    *specs,
                            SvgAnimation *base)
{
  for (unsigned int i = 0; i < specs->len; i++)
    {
      TimeSpec *spec = g_ptr_array_index (specs, i);
      time_spec_update_for_base (spec, base);
    }
}

static gboolean
animation_set_current_end (SvgAnimation *a,
                           int64_t       time)
{
  /* FIXME take min, max into account */
  if (time < a->current.begin)
    time = a->current.begin;

  if (a->current.begin < INDEFINITE && a->repeat_duration < INDEFINITE)
    time = MIN (time, a->current.begin + a->repeat_duration);

  if (a->current.end == time)
    return FALSE;

  dbg_print ("times""current end time of %s set to %s", a->id, format_time (time));
  a->current.end = time;
  return TRUE;
}

void
svg_animation_update_for_spec (SvgAnimation *animation,
                               TimeSpec     *spec)
{
  gboolean changed = FALSE;

  if (svg_animation_has_begin (animation, spec))
    {
      if (!animation_can_start (animation))
        {
          /* nothing to do */
        }
      else if (animation->status == ANIMATION_STATUS_RUNNING)
        {
          if (animation->current.begin < spec->time && spec->time < INDEFINITE)
            {
              dbg_print ("status""Restarting %s at %s", animation->id, format_time (spec->time));
              animation->current.begin = spec->time;
              animation_set_current_end (animation, INDEFINITE);
              changed = TRUE;
            }
        }
      else
        {
          int64_t time;

          /* We need to allow starting in the past. But we don't allow
           * a post-dated start to fall into a previous activation.
           */


         time = find_first_time (animation->begin, animation->previous.end);

          if (animation->current.begin != time)
            {
              dbg_print ("times""Current start time of %s now %s", animation->id, format_time (time));
              animation->current.begin = time;
              changed = TRUE;

              animation_set_current_end (animation, animation->current.end);
            }
        }
    }
  else if (svg_animation_has_end (animation, spec))
    {
      int64_t end = find_first_time (animation->end, animation->current.begin);

      changed = animation_set_current_end (animation, end);
    }

  if (changed && animation->deps)
    {
      dbg_print ("status""Updating deps of %s", animation->id);
      for (unsigned int i = 0; i < animation->deps->len; i++)
        {
          SvgAnimation *dep = g_ptr_array_index (animation->deps, i);
          time_specs_update_for_base (dep->begin, animation);
          time_specs_update_for_base (dep->end, animation);
        }
    }
}

void
svg_animation_start (SvgAnimation *animation,
                     int64_t       current_time)
{
  gboolean changed = FALSE;

  if (!animation_can_start (animation))
    return;

  if (animation->status == ANIMATION_STATUS_RUNNING)
    {
      if (animation->current.begin < current_time)
        {
          dbg_print ("status""Restarting %s at %s", animation->id, format_time (current_time));
          animation->current.begin = current_time;
          changed = TRUE;
        }
    }
  else
    {
      if (animation->current.begin != current_time)
        {
          dbg_print ("times""current start time of %s now %s", animation->id, format_time (current_time));
          animation->current.begin = current_time;
          changed = TRUE;

          animation_set_current_end (animation, animation->current.end);
        }
    }

  if (!changed)
    return;

  if (animation->deps)
    {
      for (unsigned int i = 0; i < animation->deps->len; i++)
        {
          SvgAnimation *dep = g_ptr_array_index (animation->deps, i);
          time_specs_update_for_base (dep->begin, animation);
          time_specs_update_for_base (dep->end, animation);
        }
    }
}

int64_t
svg_animation_get_current_begin (SvgAnimation *animation)
{
  return animation->current.begin;
}

int64_t
svg_animation_get_current_end (SvgAnimation *animation)
{
  return animation->current.end;
}

static int64_t
compute_repeat_duration (SvgAnimation *a)
{
  if (a->repeat_duration < INDEFINITE)
    return a->repeat_duration;
  else if (a->simple_duration < INDEFINITE && a->repeat_count != REPEAT_FOREVER)
    return a->simple_duration * a->repeat_count;
  else if (a->current.end < INDEFINITE)
    return a->current.end - a->current.begin;
  else if (a->simple_duration < INDEFINITE)
    return a->simple_duration;

  return INDEFINITE;
}

static int64_t
compute_simple_duration (SvgAnimation *a)
{
  int64_t repeat_duration;

  if (a->simple_duration < INDEFINITE)
    return a->simple_duration;

  repeat_duration = compute_repeat_duration (a);

  if (repeat_duration < INDEFINITE && a->repeat_count != REPEAT_FOREVER)
    return (int64_t) (repeat_duration / a->repeat_count);

  return INDEFINITE;
}

int64_t
svg_animation_get_simple_duration (SvgAnimation *animation)
{
  return compute_simple_duration (animation);
}

/* {{{ Updates */

/* Determine what repetition the time falls into,
 * relative to the animations current start time.
 * Also what frame we are on, and how far into that
 * frame we are.
 */

gboolean
svg_animation_get_progress (SvgAnimation *a,
                            int64_t       time,
                            int          *out_rep,
                            unsigned int *out_frame,
                            double       *out_frame_t,
                            int64_t      *out_frame_start,
                            int64_t      *out_frame_end)
{
  int64_t start;
  int64_t simple_duration;
  double t;
  int rep;
  int64_t cycle_start, cycle_end;
  int64_t frame_start, frame_end;
  unsigned int i;
  int64_t begin, end;

  if (a->status == ANIMATION_STATUS_RUNNING)
    {
      begin = a->current.begin;
      end = a->current.end;
    }
  else
    {
      begin = a->previous.begin;
      end = a->previous.end;
    }

  start = begin;
  //g_assert (start < INDEFINITE);

  simple_duration = compute_simple_duration (a);
  if (simple_duration == INDEFINITE)
    simple_duration = compute_repeat_duration (a);

  if (simple_duration == INDEFINITE || simple_duration == 0)
    {
      *out_rep = 0;
      *out_frame = 0;
      *out_frame_t = 0;
      *out_frame_start = begin;
      *out_frame_end = end;
      return FALSE;
    }

  t = (time - start) / (double) simple_duration;
  rep = floor (t); /* number of completed repetitions */

  cycle_start = start + rep * simple_duration;
  cycle_end = cycle_start + simple_duration;

  frame_start = frame_end = cycle_start;
  for (i = 0; i + 1 < a->n_frames; i++)
    {
      frame_start = frame_end;
      frame_end = lerp (a->frames[i + 1].time, cycle_start, cycle_end);

      if (time < frame_end)
        break;
    }

  t = (time - frame_start) / (double) (frame_end - frame_start);

  *out_rep = rep;
  *out_frame = i;
  *out_frame_t = t;
  *out_frame_start = frame_start;
  *out_frame_end = frame_end;

  return TRUE;
}

void
svg_animation_update_run_mode (SvgAnimation *a,
                               int64_t       current_time)
{
  if (a->status == ANIMATION_STATUS_INACTIVE)
    {
      a->run_mode = GTK_SVG_RUN_MODE_DISCRETE;
      a->next_invalidate = a->current.begin;
    }
  else if (a->status == ANIMATION_STATUS_RUNNING)
    {
      int rep;
      unsigned int frame;
      double frame_t;
      int64_t frame_start, frame_end;

      if (a->type == ANIMATION_TYPE_SET)
        {
          a->run_mode = GTK_SVG_RUN_MODE_DISCRETE;
          a->next_invalidate = a->current.end;
          return;
        }

      svg_animation_get_progress (a, current_time,
                                  &rep, &frame, &frame_t, &frame_start, &frame_end);

      if (a->calc_mode == CALC_MODE_DISCRETE)
        {
          a->run_mode = GTK_SVG_RUN_MODE_DISCRETE;
          a->next_invalidate = frame_end;
        }
      else if (svg_property_discrete (a->attr))
        {
          a->run_mode = GTK_SVG_RUN_MODE_DISCRETE;
          if (frame_t < 0.5)
            a->next_invalidate = (frame_start + frame_end) / 2;
          else
            a->next_invalidate = frame_end;
        }
      else
        {
          a->run_mode = GTK_SVG_RUN_MODE_CONTINUOUS;
          a->next_invalidate = a->current.end;
        }
    }
  else if (a->current.begin < INDEFINITE && current_time <= a->current.begin)
    {
      a->run_mode = GTK_SVG_RUN_MODE_DISCRETE;
      a->next_invalidate = a->current.begin;
    }
  else
    {
      a->run_mode = GTK_SVG_RUN_MODE_STOPPED;
      a->next_invalidate = INDEFINITE;
    }
}

void
svg_animation_update_state (SvgAnimation *a,
                            int64_t       current_time)
{
  AnimationStatus status = a->status;

  if (a->status == ANIMATION_STATUS_INACTIVE ||
      a->status == ANIMATION_STATUS_DONE)
    {
      if (current_time < a->current.begin)
        ;  /* remain inactive */
      else if (current_time <= a->current.end)
        status = ANIMATION_STATUS_RUNNING;
      else
        status = ANIMATION_STATUS_DONE;
    }
  else if (a->status == ANIMATION_STATUS_RUNNING)
    {
      if (current_time >= a->current.end)
        status = ANIMATION_STATUS_DONE;
    }

  if (a->status != status)
    {
      if (a->status == ANIMATION_STATUS_RUNNING) /* Ending this activation */
        a->previous = a->current;

      a->status = status;

      if (a->status != ANIMATION_STATUS_RUNNING)
        {
          a->current.begin = find_first_time (a->begin, current_time);
          animation_set_current_end (a, find_first_time (a->end, a->current.begin));
        }

      svg_animation_update_run_mode (a, current_time);
      a->state_changed = TRUE;

#ifdef DEBUG
      if (strstr (g_getenv ("SVG_DEBUG") ?:"""state"))
        {
          const char *name[] = { "INACTIVE""RUNNING ""DONE    " };
          GString *s = g_string_new ("");
          g_string_append_printf (s, "state of %s now %s [", a->id, name[status]);
          g_string_append_printf (s, "%s ", format_time (a->current.begin));
          g_string_append_printf (s, "%s] ", format_time (a->current.end));
          switch (a->run_mode)
            {
            case GTK_SVG_RUN_MODE_CONTINUOUS:
              g_string_append_printf (s, "--> %s\n", format_time (a->next_invalidate));
              break;
            case GTK_SVG_RUN_MODE_DISCRETE:
              g_string_append_printf (s, "> > %s\n", format_time (a->next_invalidate));
              break;
            case GTK_SVG_RUN_MODE_STOPPED:
              g_string_append (s, "\n");
              break;
            default:
              g_assert_not_reached ();
            }
          dbg_print ("state""%s", s->str);
          g_string_free (s, TRUE);
        }
#endif
    }
  else
    svg_animation_update_run_mode (a, current_time);
}

/* }}} */
/* ((( Reference resolution */

void
svg_animation_resolve_shadow_references (SvgAnimation *animation,
                                         GHashTable   *map)
{
  SvgValue *value;
  SvgElement *clone;
  unsigned int first;

  if (animation->motion.path_shape)
    {
      clone = g_hash_table_lookup (map, animation->motion.path_shape);
      if (clone)
        animation->motion.path_shape = clone;
    }

  for (unsigned int k = 0; k < 2; k++)
    {
      GPtrArray *specs = k == 0 ? animation->begin : animation->end;
      for (unsigned int i = 0; i < specs->len; i++)
        {
          TimeSpec *spec = g_ptr_array_index (specs, i);
          if (spec->type == TIME_SPEC_TYPE_SYNC)
            {
              SvgAnimation *anim = g_hash_table_lookup (map, spec->sync.base);
              if (anim)
                spec->sync.base = anim;
              if (spec->sync.base)
                svg_animation_add_dep (spec->sync.base, animation);
            }
          else if (spec->type == TIME_SPEC_TYPE_EVENT)
            {
              clone = g_hash_table_lookup (map, spec->event.shape);
              if (clone)
                spec->event.shape = clone;
            }
        }
    }

  /* Skip the 'current' keyword in frames[0] if present */
  if (animation->frames &&
      animation->frames[0].value &&
      svg_value_is_current (animation->frames[0].value))
    first = 1;
  else
    first = 0;

  if (animation->attr == SVG_PROPERTY_CLIP_PATH)
    {
      for (unsigned int j = first; j < animation->n_frames; j++)
        {
          value = animation->frames[j].value;
          if (svg_clip_get_kind (value) == CLIP_URL)
            {
              clone = g_hash_table_lookup (map, svg_clip_get_shape (value));
              if (clone)
                {
                  animation->frames[j].value = svg_clip_new_url_take (g_strdup (svg_clip_get_id (value)));
                  svg_clip_set_shape (animation->frames[j].value, clone);
                  svg_value_unref (value);
                }
            }
        }
    }
  else if (animation->attr == SVG_PROPERTY_MASK)
    {
      for (unsigned int j = first; j < animation->n_frames; j++)
        {
          value = animation->frames[j].value;
          if (svg_mask_get_kind (value) == MASK_URL)
            {
              clone = g_hash_table_lookup (map, svg_mask_get_shape (value));
              if (clone)
                {
                  animation->frames[j].value = svg_mask_new_url_take (g_strdup (svg_mask_get_id (value)));
                  svg_mask_set_shape (animation->frames[j].value, clone);
                  svg_value_unref (value);
                }
            }
        }
    }
  else if (animation->attr == SVG_PROPERTY_HREF ||
           animation->attr == SVG_PROPERTY_FE_IMAGE_HREF ||
           animation->attr == SVG_PROPERTY_MARKER_START ||
           animation->attr == SVG_PROPERTY_MARKER_MID ||
           animation->attr == SVG_PROPERTY_MARKER_END)
    {
      for (unsigned int j = first; j < animation->n_frames; j++)
        {
          value = animation->frames[j].value;
          clone = g_hash_table_lookup (map, svg_href_get_shape (value));
          if (clone)
            {
               if (svg_href_get_kind (value) == HREF_URL)
                 animation->frames[j].value = svg_href_new_url_take (g_strdup (svg_href_get_ref (value)));
               else
                 animation->frames[j].value = svg_href_new_plain (svg_href_get_ref (value));
               svg_href_set_shape (animation->frames[j].value, clone);
               svg_value_unref (value);
            }
        }
    }
  else if (animation->attr == SVG_PROPERTY_FILL ||
           animation->attr == SVG_PROPERTY_STROKE)
    {
      for (unsigned int j = first; j < animation->n_frames; j++)
        {
          value = animation->frames[j].value;
          PaintKind kind = svg_paint_get_kind (value);
          if (paint_is_server (kind))
            {
              clone = g_hash_table_lookup (map, svg_paint_get_server_shape (value));
              if (clone)
                {
                  const char *ref = svg_paint_get_server_ref (value);
                  const GdkColor *fallback = svg_paint_get_server_fallback (value);

                  if (kind == PAINT_SERVER_WITH_FALLBACK)
                    animation->frames[j].value = svg_paint_new_server_with_fallback (ref, fallback);
                  else if (kind == PAINT_SERVER_WITH_CURRENT_COLOR)
                    animation->frames[j].value = svg_paint_new_server_with_current_color (ref);
                  else
                    animation->frames[j].value = svg_paint_new_server (ref);

                  svg_paint_set_server_shape (animation->frames[j].value, clone);
                  svg_value_unref (value);
                }
            }
        }
    }
  else if (animation->attr == SVG_PROPERTY_FILTER)
    {
      for (unsigned int j = first; j < animation->n_frames; j++)
        {
          value = animation->frames[j].value;

          animation->frames[j].value = svg_filter_functions_copy (value);
          for (unsigned int k = 0; k < svg_filter_functions_get_length (value); k++)
            {
              if (svg_filter_functions_get_kind (value, k) == FILTER_REF)
                {
                  clone = g_hash_table_lookup (map, svg_filter_functions_get_shape (value, k));
                  if (clone)
                    svg_filter_functions_set_shape (animation->frames[j].value, k, clone);
                }
            }

          svg_value_unref (value);
        }
    }
}

/* }}} */

/* vim:set foldmethod=marker: */

Messung V0.5 in Prozent
C=98 H=95 G=96

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik