Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-transition-animation.c
Go to the documentation of this file.
1 /* ev-transition-animation.c
2  * this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include <cairo.h>
23 #include <gdk/gdk.h>
25 #include "ev-timeline.h"
26 
27 #define EV_TRANSITION_ANIMATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_TRANSITION_ANIMATION, EvTransitionAnimationPriv))
28 #define N_BLINDS 6
29 
31 
34  cairo_surface_t *origin_surface;
35  cairo_surface_t *dest_surface;
36 };
37 
38 enum {
43 };
44 
45 
46 G_DEFINE_TYPE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
47 
48 
49 static void
51 {
52 }
53 
54 static void
56  guint prop_id,
57  const GValue *value,
58  GParamSpec *pspec)
59 {
61 
63 
64  switch (prop_id) {
65  case PROP_EFFECT:
66  if (priv->effect)
67  g_object_unref (priv->effect);
68 
69  priv->effect = g_value_dup_object (value);
70  break;
73  g_value_get_pointer (value));
74  break;
75  case PROP_DEST_SURFACE:
77  g_value_get_pointer (value));
78  break;
79  default:
80  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81  }
82 }
83 
84 static void
86  guint prop_id,
87  GValue *value,
88  GParamSpec *pspec)
89 {
91 
93 
94  switch (prop_id) {
95  case PROP_EFFECT:
96  g_value_set_object (value, priv->effect);
97  break;
99  g_value_set_pointer (value, priv->origin_surface);
100  break;
101  case PROP_DEST_SURFACE:
102  g_value_set_pointer (value, priv->dest_surface);
103  break;
104  default:
105  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106  }
107 }
108 
109 static void
111 {
113 
114  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
115 
116  if (priv->effect)
117  g_object_unref (priv->effect);
118 
119  if (priv->origin_surface)
120  cairo_surface_destroy (priv->origin_surface);
121 
122  if (priv->dest_surface)
123  cairo_surface_destroy (priv->dest_surface);
124 
125  G_OBJECT_CLASS (ev_transition_animation_parent_class)->finalize (object);
126 }
127 
128 static GObject *
130  guint n_construct_properties,
131  GObjectConstructParam *construct_params)
132 {
133  GObject *object;
135  EvTransitionEffect *effect;
136  gint duration;
137 
138  object = G_OBJECT_CLASS (ev_transition_animation_parent_class)->constructor (type,
139  n_construct_properties,
140  construct_params);
141 
142  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
143  effect = priv->effect;
144 
145  g_object_get (effect, "duration", &duration, NULL);
146  ev_timeline_set_duration (EV_TIMELINE (object), duration * 1000);
147 
148  return object;
149 }
150 
151 static void
153 {
154  GObjectClass *object_class = G_OBJECT_CLASS (klass);
155 
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;
160 
161  g_object_class_install_property (object_class,
162  PROP_EFFECT,
163  g_param_spec_object ("effect",
164  "Effect",
165  "Transition effect description",
167  G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
168  G_PARAM_STATIC_STRINGS));
169  g_object_class_install_property (object_class,
171  g_param_spec_pointer ("origin-surface",
172  "Origin surface",
173  "Cairo surface from which the animation will happen",
174  G_PARAM_READWRITE |
175  G_PARAM_STATIC_STRINGS));
176  g_object_class_install_property (object_class,
178  g_param_spec_pointer ("dest-surface",
179  "Destination surface",
180  "Cairo surface to which the animation will happen",
181  G_PARAM_READWRITE |
182  G_PARAM_STATIC_STRINGS));
183 
184  g_type_class_add_private (klass, sizeof (EvTransitionAnimationPriv));
185 }
186 
187 static void
188 paint_surface (cairo_t *cr,
189  cairo_surface_t *surface,
190  gdouble x_offset,
191  gdouble y_offset,
192  gdouble alpha,
193  GdkRectangle page_area)
194 {
195  cairo_save (cr);
196 
197  gdk_cairo_rectangle (cr, &page_area);
198  cairo_clip (cr);
199  cairo_surface_set_device_offset (surface, x_offset, y_offset);
200  cairo_set_source_surface (cr, surface, 0, 0);
201 
202  if (alpha == 1.)
203  cairo_paint (cr);
204  else
205  cairo_paint_with_alpha (cr, alpha);
206 
207  cairo_restore (cr);
208 }
209 
210 /* animations */
211 static void
213  EvTransitionAnimation *animation,
214  EvTransitionEffect *effect,
215  gdouble progress,
216  GdkRectangle page_area)
217 {
219  EvTransitionEffectAlignment alignment;
220  EvTransitionEffectDirection direction;
221  gint width, height;
222 
223  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
224  width = page_area.width;
225  height = page_area.height;
226 
227  g_object_get (effect,
228  "alignment", &alignment,
229  "direction", &direction,
230  NULL);
231 
232  if (direction == EV_TRANSITION_DIRECTION_INWARD) {
233  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
234 
235  if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
236  cairo_rectangle (cr,
237  0,
238  height * progress / 2,
239  width,
240  height * (1 - progress));
241  } else {
242  cairo_rectangle (cr,
243  width * progress / 2,
244  0,
245  width * (1 - progress),
246  height);
247  }
248 
249  cairo_clip (cr);
250 
251  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
252  } else {
253  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
254 
255  if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
256  cairo_rectangle (cr,
257  0,
258  (height / 2) - (height * progress / 2),
259  width,
260  height * progress);
261  } else {
262  cairo_rectangle (cr,
263  (width / 2) - (width * progress / 2),
264  0,
265  width * progress,
266  height);
267  }
268 
269  cairo_clip (cr);
270 
271  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
272  }
273 }
274 
275 static void
277  EvTransitionAnimation *animation,
278  EvTransitionEffect *effect,
279  gdouble progress,
280  GdkRectangle page_area)
281 {
283  EvTransitionEffectAlignment alignment;
284  gint width, height, i;
285 
286  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
287  width = page_area.width;
288  height = page_area.height;
289 
290  g_object_get (effect,
291  "alignment", &alignment,
292  NULL);
293 
294  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
295 
296  for (i = 0; i < N_BLINDS; i++) {
297  cairo_save (cr);
298 
299  if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
300  cairo_rectangle (cr,
301  0,
302  height / N_BLINDS * i,
303  width,
304  height / N_BLINDS * progress);
305  } else {
306  cairo_rectangle (cr,
307  width / N_BLINDS * i,
308  0,
309  width / N_BLINDS * progress,
310  height);
311  }
312 
313  cairo_clip (cr);
314  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
315  cairo_restore (cr);
316  }
317 }
318 
319 static void
321  EvTransitionAnimation *animation,
322  EvTransitionEffect *effect,
323  gdouble progress,
324  GdkRectangle page_area)
325 {
327  EvTransitionEffectDirection direction;
328  gint width, height;
329 
330  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
331  width = page_area.width;
332  height = page_area.height;
333 
334  g_object_get (effect,
335  "direction", &direction,
336  NULL);
337 
338  if (direction == EV_TRANSITION_DIRECTION_INWARD) {
339  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
340 
341  cairo_rectangle (cr,
342  width * progress / 2,
343  height * progress / 2,
344  width * (1 - progress),
345  height * (1 - progress));
346  cairo_clip (cr);
347 
348  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
349  } else {
350  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
351 
352  cairo_rectangle (cr,
353  (width / 2) - (width * progress / 2),
354  (height / 2) - (height * progress / 2),
355  width * progress,
356  height * progress);
357  cairo_clip (cr);
358 
359  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
360  }
361 }
362 
363 static void
365  EvTransitionAnimation *animation,
366  EvTransitionEffect *effect,
367  gdouble progress,
368  GdkRectangle page_area)
369 {
371  gint width, height;
372  gint angle;
373 
374  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
375  width = page_area.width;
376  height = page_area.height;
377 
378  g_object_get (effect,
379  "angle", &angle,
380  NULL);
381 
382  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
383 
384  if (angle == 0) {
385  /* left to right */
386  cairo_rectangle (cr,
387  0, 0,
388  width * progress,
389  height);
390  } else if (angle <= 90) {
391  /* bottom to top */
392  cairo_rectangle (cr,
393  0,
394  height * (1 - progress),
395  width,
396  height * progress);
397  } else if (angle <= 180) {
398  /* right to left */
399  cairo_rectangle (cr,
400  width * (1 - progress),
401  0,
402  width * progress,
403  height);
404  } else if (angle <= 270) {
405  /* top to bottom */
406  cairo_rectangle (cr,
407  0, 0,
408  width,
409  height * progress);
410  }
411 
412  cairo_clip (cr);
413 
414  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
415 }
416 
417 static void
419  EvTransitionAnimation *animation,
420  EvTransitionEffect *effect,
421  gdouble progress,
422  GdkRectangle page_area)
423 {
425 
426  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
427 
428  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
429  paint_surface (cr, priv->origin_surface, 0, 0, 1 - progress, page_area);
430 }
431 
432 static void
434  EvTransitionAnimation *animation,
435  EvTransitionEffect *effect,
436  gdouble progress,
437  GdkRectangle page_area)
438 {
440  gint width, height;
441  gint angle;
442 
443  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
444  width = page_area.width;
445  height = page_area.height;
446 
447  g_object_get (effect,
448  "angle", &angle,
449  NULL);
450 
451  if (angle == 0) {
452  /* left to right */
453  paint_surface (cr, priv->origin_surface, - (width * progress), 0, 1., page_area);
454  paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 1., page_area);
455  } else {
456  /* top to bottom */
457  paint_surface (cr, priv->origin_surface, 0, - (height * progress), 1., page_area);
458  paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 1., page_area);
459  }
460 }
461 
462 static void
464  EvTransitionAnimation *animation,
465  EvTransitionEffect *effect,
466  gdouble progress,
467  GdkRectangle page_area)
468 {
470  gint width, height;
471  gint angle;
472 
473  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
474  width = page_area.width;
475  height = page_area.height;
476 
477  g_object_get (effect,
478  "angle", &angle,
479  NULL);
480 
481  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
482 
483  if (angle == 0) {
484  /* left to right */
485  paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 1., page_area);
486  } else {
487  /* top to bottom */
488  paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 1., page_area);
489  }
490 }
491 
492 static void
494  EvTransitionAnimation *animation,
495  EvTransitionEffect *effect,
496  gdouble progress,
497  GdkRectangle page_area)
498 {
500  gint width, height;
501  gint angle;
502 
503  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
504  width = page_area.width;
505  height = page_area.height;
506 
507  g_object_get (effect,
508  "angle", &angle,
509  NULL);
510 
511  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
512 
513  if (angle == 0) {
514  /* left to right */
515  paint_surface (cr, priv->origin_surface, - (width * progress), 0, 1., page_area);
516  } else {
517  /* top to bottom */
518  paint_surface (cr, priv->origin_surface, 0, - (height * progress), 1., page_area);
519  }
520 }
521 
522 static void
524  EvTransitionAnimation *animation,
525  EvTransitionEffect *effect,
526  gdouble progress,
527  GdkRectangle page_area)
528 {
530 
531  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
532 
533  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
534  paint_surface (cr, priv->dest_surface, 0, 0, progress, page_area);
535 }
536 
537 void
539  cairo_t *cr,
540  GdkRectangle page_area)
541 {
544  gdouble progress;
545 
546  g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
547 
548  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
549 
550  if (!priv->dest_surface) {
551  /* animation is still not ready, paint the origin surface */
552  paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
553  return;
554  }
555 
556  g_object_get (priv->effect, "type", &type, NULL);
557  progress = ev_timeline_get_progress (EV_TIMELINE (animation));
558 
559  switch (type) {
561  /* just paint the destination slide */
562  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
563  break;
565  ev_transition_animation_split (cr, animation, priv->effect, progress, page_area);
566  break;
568  ev_transition_animation_blinds (cr, animation, priv->effect, progress, page_area);
569  break;
571  ev_transition_animation_box (cr, animation, priv->effect, progress, page_area);
572  break;
574  ev_transition_animation_wipe (cr, animation, priv->effect, progress, page_area);
575  break;
577  ev_transition_animation_dissolve (cr, animation, priv->effect, progress, page_area);
578  break;
580  ev_transition_animation_push (cr, animation, priv->effect, progress, page_area);
581  break;
583  ev_transition_animation_cover (cr, animation, priv->effect, progress, page_area);
584  break;
586  ev_transition_animation_uncover (cr, animation, priv->effect, progress, page_area);
587  break;
589  ev_transition_animation_fade (cr, animation, priv->effect, progress, page_area);
590  break;
591  default: {
592  GEnumValue *enum_value;
593 
594  enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_TRANSITION_EFFECT_TYPE), type);
595 
596  g_warning ("Unimplemented transition animation: '%s', "
597  "please post a bug report in Evince bugzilla "
598  "(http://bugzilla.gnome.org) with a testcase.",
599  enum_value->value_nick);
600 
601  /* just paint the destination slide */
602  paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
603  }
604  }
605 }
606 
609 {
610  g_return_val_if_fail (EV_IS_TRANSITION_EFFECT (effect), NULL);
611 
612  return g_object_new (EV_TYPE_TRANSITION_ANIMATION,
613  "effect", effect,
614  NULL);
615 }
616 
617 void
619  cairo_surface_t *origin_surface)
620 {
622  cairo_surface_t *surface;
623 
624  g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
625 
626  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
627 
628  if (priv->origin_surface == origin_surface)
629  return;
630 
631  surface = cairo_surface_reference (origin_surface);
632 
633  if (priv->origin_surface)
634  cairo_surface_destroy (priv->origin_surface);
635 
636  priv->origin_surface = surface;
637  g_object_notify (G_OBJECT (animation), "origin-surface");
638 
639  if (priv->origin_surface && priv->dest_surface)
640  ev_timeline_start (EV_TIMELINE (animation));
641 }
642 
643 void
645  cairo_surface_t *dest_surface)
646 {
648  cairo_surface_t *surface;
649 
650  g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
651 
652  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
653 
654  if (priv->dest_surface == dest_surface)
655  return;
656 
657  surface = cairo_surface_reference (dest_surface);
658 
659  if (priv->dest_surface)
660  cairo_surface_destroy (priv->dest_surface);
661 
662  priv->dest_surface = surface;
663  g_object_notify (G_OBJECT (animation), "dest-surface");
664 
665  if (priv->origin_surface && priv->dest_surface)
666  ev_timeline_start (EV_TIMELINE (animation));
667 }
668 
669 gboolean
671 {
673 
674  g_return_val_if_fail (EV_IS_TRANSITION_ANIMATION (animation), FALSE);
675 
676  priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
677 
678  return (priv->origin_surface != NULL);
679 }