1 /* ev-transition-animation.c
2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "ev-transition-animation.h"
25 #include "ev-timeline.h"
27 #define EV_TRANSITION_ANIMATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_TRANSITION_ANIMATION, EvTransitionAnimationPriv))
30 typedef struct EvTransitionAnimationPriv EvTransitionAnimationPriv;
32 struct EvTransitionAnimationPriv {
33 EvTransitionEffect *effect;
34 cairo_surface_t *origin_surface;
35 cairo_surface_t *dest_surface;
46 G_DEFINE_TYPE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
50 ev_transition_animation_init (EvTransitionAnimation *animation)
55 ev_transition_animation_set_property (GObject *object,
60 EvTransitionAnimationPriv *priv;
62 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
67 g_object_unref (priv->effect);
69 priv->effect = g_value_dup_object (value);
71 case PROP_ORIGIN_SURFACE:
72 ev_transition_animation_set_origin_surface (EV_TRANSITION_ANIMATION (object),
73 g_value_get_pointer (value));
75 case PROP_DEST_SURFACE:
76 ev_transition_animation_set_dest_surface (EV_TRANSITION_ANIMATION (object),
77 g_value_get_pointer (value));
80 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
85 ev_transition_animation_get_property (GObject *object,
90 EvTransitionAnimationPriv *priv;
92 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
96 g_value_set_object (value, priv->effect);
98 case PROP_ORIGIN_SURFACE:
99 g_value_set_pointer (value, priv->origin_surface);
101 case PROP_DEST_SURFACE:
102 g_value_set_pointer (value, priv->dest_surface);
105 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
110 ev_transition_animation_finalize (GObject *object)
112 EvTransitionAnimationPriv *priv;
114 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
117 g_object_unref (priv->effect);
119 if (priv->origin_surface)
120 cairo_surface_destroy (priv->origin_surface);
122 if (priv->dest_surface)
123 cairo_surface_destroy (priv->dest_surface);
125 G_OBJECT_CLASS (ev_transition_animation_parent_class)->finalize (object);
129 ev_transition_animation_constructor (GType type,
130 guint n_construct_properties,
131 GObjectConstructParam *construct_params)
134 EvTransitionAnimationPriv *priv;
135 EvTransitionEffect *effect;
138 object = G_OBJECT_CLASS (ev_transition_animation_parent_class)->constructor (type,
139 n_construct_properties,
142 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
143 effect = priv->effect;
145 g_object_get (effect, "duration", &duration, NULL);
146 ev_timeline_set_duration (EV_TIMELINE (object), duration * 1000);
152 ev_transition_animation_class_init (EvTransitionAnimationClass *klass)
154 GObjectClass *object_class = G_OBJECT_CLASS (klass);
156 object_class->set_property = ev_transition_animation_set_property;
157 object_class->get_property = ev_transition_animation_get_property;
158 object_class->finalize = ev_transition_animation_finalize;
159 object_class->constructor = ev_transition_animation_constructor;
161 g_object_class_install_property (object_class,
163 g_param_spec_object ("effect",
165 "Transition effect description",
166 EV_TYPE_TRANSITION_EFFECT,
167 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
168 g_object_class_install_property (object_class,
170 g_param_spec_pointer ("origin-surface",
172 "Cairo surface from which the animation will happen",
174 g_object_class_install_property (object_class,
176 g_param_spec_pointer ("dest-surface",
177 "Destination surface",
178 "Cairo surface to which the animation will happen",
181 g_type_class_add_private (klass, sizeof (EvTransitionAnimationPriv));
185 paint_surface (cairo_t *cr,
186 cairo_surface_t *surface,
190 GdkRectangle page_area)
194 gdk_cairo_rectangle (cr, &page_area);
197 width = cairo_image_surface_get_width (surface);
198 height = cairo_image_surface_get_height (surface);
202 if (width != page_area.width || height != page_area.height) {
203 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
205 (gdouble) page_area.width / width,
206 (gdouble) page_area.height / height);
209 cairo_surface_set_device_offset (surface, x_offset, y_offset);
210 cairo_set_source_surface (cr, surface, 0, 0);
215 cairo_paint_with_alpha (cr, alpha);
222 ev_transition_animation_split (cairo_t *cr,
223 EvTransitionAnimation *animation,
224 EvTransitionEffect *effect,
226 GdkRectangle page_area)
228 EvTransitionAnimationPriv *priv;
229 EvTransitionEffectAlignment alignment;
230 EvTransitionEffectDirection direction;
233 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
234 width = page_area.width;
235 height = page_area.height;
237 g_object_get (effect,
238 "alignment", &alignment,
239 "direction", &direction,
242 if (direction == EV_TRANSITION_DIRECTION_INWARD) {
243 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
245 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
248 height * progress / 2,
250 height * (1 - progress));
253 width * progress / 2,
255 width * (1 - progress),
261 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
263 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
265 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
268 (height / 2) - (height * progress / 2),
273 (width / 2) - (width * progress / 2),
281 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
286 ev_transition_animation_blinds (cairo_t *cr,
287 EvTransitionAnimation *animation,
288 EvTransitionEffect *effect,
290 GdkRectangle page_area)
292 EvTransitionAnimationPriv *priv;
293 EvTransitionEffectAlignment alignment;
294 gint width, height, i;
296 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
297 width = page_area.width;
298 height = page_area.height;
300 g_object_get (effect,
301 "alignment", &alignment,
304 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
306 for (i = 0; i < N_BLINDS; i++) {
309 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
312 height / N_BLINDS * i,
314 height / N_BLINDS * progress);
317 width / N_BLINDS * i,
319 width / N_BLINDS * progress,
324 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
330 ev_transition_animation_box (cairo_t *cr,
331 EvTransitionAnimation *animation,
332 EvTransitionEffect *effect,
334 GdkRectangle page_area)
336 EvTransitionAnimationPriv *priv;
337 EvTransitionEffectDirection direction;
340 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
341 width = page_area.width;
342 height = page_area.height;
344 g_object_get (effect,
345 "direction", &direction,
348 if (direction == EV_TRANSITION_DIRECTION_INWARD) {
349 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
352 width * progress / 2,
353 height * progress / 2,
354 width * (1 - progress),
355 height * (1 - progress));
358 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
360 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
363 (width / 2) - (width * progress / 2),
364 (height / 2) - (height * progress / 2),
369 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
374 ev_transition_animation_wipe (cairo_t *cr,
375 EvTransitionAnimation *animation,
376 EvTransitionEffect *effect,
378 GdkRectangle page_area)
380 EvTransitionAnimationPriv *priv;
384 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
385 width = page_area.width;
386 height = page_area.height;
388 g_object_get (effect,
392 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
400 } else if (angle <= 90) {
404 height * (1 - progress),
407 } else if (angle <= 180) {
410 width * (1 - progress),
414 } else if (angle <= 270) {
424 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
428 ev_transition_animation_dissolve (cairo_t *cr,
429 EvTransitionAnimation *animation,
430 EvTransitionEffect *effect,
432 GdkRectangle page_area)
434 EvTransitionAnimationPriv *priv;
436 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
438 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
439 paint_surface (cr, priv->origin_surface, 0, 0, 1 - progress, page_area);
443 ev_transition_animation_push (cairo_t *cr,
444 EvTransitionAnimation *animation,
445 EvTransitionEffect *effect,
447 GdkRectangle page_area)
449 EvTransitionAnimationPriv *priv;
453 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
454 width = page_area.width;
455 height = page_area.height;
457 g_object_get (effect,
463 paint_surface (cr, priv->origin_surface, - (width * progress), 0, 0, page_area);
464 paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 0, page_area);
467 paint_surface (cr, priv->origin_surface, 0, - (height * progress), 0, page_area);
468 paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 0, page_area);
473 ev_transition_animation_cover (cairo_t *cr,
474 EvTransitionAnimation *animation,
475 EvTransitionEffect *effect,
477 GdkRectangle page_area)
479 EvTransitionAnimationPriv *priv;
483 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
484 width = page_area.width;
485 height = page_area.height;
487 g_object_get (effect,
491 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
495 paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 0, page_area);
498 paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 0, page_area);
503 ev_transition_animation_uncover (cairo_t *cr,
504 EvTransitionAnimation *animation,
505 EvTransitionEffect *effect,
507 GdkRectangle page_area)
509 EvTransitionAnimationPriv *priv;
513 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
514 width = page_area.width;
515 height = page_area.height;
517 g_object_get (effect,
521 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
525 paint_surface (cr, priv->origin_surface, - (width * progress), 0, 0, page_area);
528 paint_surface (cr, priv->origin_surface, 0, - (height * progress), 0, page_area);
533 ev_transition_animation_fade (cairo_t *cr,
534 EvTransitionAnimation *animation,
535 EvTransitionEffect *effect,
537 GdkRectangle page_area)
539 EvTransitionAnimationPriv *priv;
541 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
543 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
544 paint_surface (cr, priv->dest_surface, 0, 0, progress, page_area);
548 ev_transition_animation_paint (EvTransitionAnimation *animation,
550 GdkRectangle page_area)
552 EvTransitionAnimationPriv *priv;
553 EvTransitionEffectType type;
556 g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
558 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
560 if (!priv->dest_surface) {
561 /* animation is still not ready, paint the origin surface */
562 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
566 g_object_get (priv->effect, "type", &type, NULL);
567 progress = ev_timeline_get_progress (EV_TIMELINE (animation));
570 case EV_TRANSITION_EFFECT_REPLACE:
571 /* just paint the destination slide */
572 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
574 case EV_TRANSITION_EFFECT_SPLIT:
575 ev_transition_animation_split (cr, animation, priv->effect, progress, page_area);
577 case EV_TRANSITION_EFFECT_BLINDS:
578 ev_transition_animation_blinds (cr, animation, priv->effect, progress, page_area);
580 case EV_TRANSITION_EFFECT_BOX:
581 ev_transition_animation_box (cr, animation, priv->effect, progress, page_area);
583 case EV_TRANSITION_EFFECT_WIPE:
584 ev_transition_animation_wipe (cr, animation, priv->effect, progress, page_area);
586 case EV_TRANSITION_EFFECT_DISSOLVE:
587 ev_transition_animation_dissolve (cr, animation, priv->effect, progress, page_area);
589 case EV_TRANSITION_EFFECT_PUSH:
590 ev_transition_animation_push (cr, animation, priv->effect, progress, page_area);
592 case EV_TRANSITION_EFFECT_COVER:
593 ev_transition_animation_cover (cr, animation, priv->effect, progress, page_area);
595 case EV_TRANSITION_EFFECT_UNCOVER:
596 ev_transition_animation_uncover (cr, animation, priv->effect, progress, page_area);
598 case EV_TRANSITION_EFFECT_FADE:
599 ev_transition_animation_fade (cr, animation, priv->effect, progress, page_area);
602 GEnumValue *enum_value;
604 enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_TRANSITION_EFFECT_TYPE), type);
606 g_warning ("Unimplemented transition animation: '%s', "
607 "please post a bug report in Evince bugzilla "
608 "(http://bugzilla.gnome.org) with a testcase.",
609 enum_value->value_nick);
611 /* just paint the destination slide */
612 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
617 EvTransitionAnimation *
618 ev_transition_animation_new (EvTransitionEffect *effect)
620 g_return_val_if_fail (EV_IS_TRANSITION_EFFECT (effect), NULL);
622 return g_object_new (EV_TYPE_TRANSITION_ANIMATION,
628 ev_transition_animation_set_origin_surface (EvTransitionAnimation *animation,
629 cairo_surface_t *origin_surface)
631 EvTransitionAnimationPriv *priv;
632 cairo_surface_t *surface;
634 g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
636 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
638 if (priv->origin_surface == origin_surface)
641 surface = cairo_surface_reference (origin_surface);
643 if (priv->origin_surface)
644 cairo_surface_destroy (priv->origin_surface);
646 priv->origin_surface = surface;
647 g_object_notify (G_OBJECT (animation), "origin-surface");
649 if (priv->origin_surface && priv->dest_surface)
650 ev_timeline_start (EV_TIMELINE (animation));
654 ev_transition_animation_set_dest_surface (EvTransitionAnimation *animation,
655 cairo_surface_t *dest_surface)
657 EvTransitionAnimationPriv *priv;
658 cairo_surface_t *surface;
660 g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
662 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
664 if (priv->dest_surface == dest_surface)
667 surface = cairo_surface_reference (dest_surface);
669 if (priv->dest_surface)
670 cairo_surface_destroy (priv->dest_surface);
672 priv->dest_surface = surface;
673 g_object_notify (G_OBJECT (animation), "dest-surface");
675 if (priv->origin_surface && priv->dest_surface)
676 ev_timeline_start (EV_TIMELINE (animation));
680 ev_transition_animation_ready (EvTransitionAnimation *animation)
682 EvTransitionAnimationPriv *priv;
684 g_return_val_if_fail (EV_IS_TRANSITION_ANIMATION (animation), FALSE);
686 priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
688 return (priv->origin_surface != NULL);