]> www.fi.muni.cz Git - evince.git/blob - libview/ev-transition-animation.c
Remove dest, mode and search_string from EvJobLoad since they are not
[evince.git] / libview / ev-transition-animation.c
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <cairo.h>
23 #include <gdk/gdk.h>
24 #include "ev-transition-animation.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
30 typedef struct EvTransitionAnimationPriv EvTransitionAnimationPriv;
31
32 struct EvTransitionAnimationPriv {
33         EvTransitionEffect *effect;
34         cairo_surface_t *origin_surface;
35         cairo_surface_t *dest_surface;
36 };
37
38 enum {
39         PROP_0,
40         PROP_EFFECT,
41         PROP_ORIGIN_SURFACE,
42         PROP_DEST_SURFACE
43 };
44
45
46 G_DEFINE_TYPE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
47
48
49 static void
50 ev_transition_animation_init (EvTransitionAnimation *animation)
51 {
52 }
53
54 static void
55 ev_transition_animation_set_property (GObject      *object,
56                                       guint         prop_id,
57                                       const GValue *value,
58                                       GParamSpec   *pspec)
59 {
60         EvTransitionAnimationPriv *priv;
61
62         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
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;
71         case PROP_ORIGIN_SURFACE:
72                 ev_transition_animation_set_origin_surface (EV_TRANSITION_ANIMATION (object),
73                                                             g_value_get_pointer (value));
74                 break;
75         case PROP_DEST_SURFACE:
76                 ev_transition_animation_set_dest_surface (EV_TRANSITION_ANIMATION (object),
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
85 ev_transition_animation_get_property (GObject      *object,
86                                       guint         prop_id,
87                                       GValue       *value,
88                                       GParamSpec   *pspec)
89 {
90         EvTransitionAnimationPriv *priv;
91
92         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (object);
93
94         switch (prop_id) {
95         case PROP_EFFECT:
96                 g_value_set_object (value, priv->effect);
97                 break;
98         case PROP_ORIGIN_SURFACE:
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
110 ev_transition_animation_finalize (GObject *object)
111 {
112         EvTransitionAnimationPriv *priv;
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 *
129 ev_transition_animation_constructor (GType                  type,
130                                      guint                  n_construct_properties,
131                                      GObjectConstructParam *construct_params)
132 {
133         GObject *object;
134         EvTransitionAnimationPriv *priv;
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
152 ev_transition_animation_class_init (EvTransitionAnimationClass *klass)
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",
166                                                               EV_TYPE_TRANSITION_EFFECT,
167                                                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
168         g_object_class_install_property (object_class,
169                                          PROP_ORIGIN_SURFACE,
170                                          g_param_spec_pointer ("origin-surface",
171                                                                "Origin surface",
172                                                                "Cairo surface from which the animation will happen",
173                                                                G_PARAM_READWRITE));
174         g_object_class_install_property (object_class,
175                                          PROP_DEST_SURFACE,
176                                          g_param_spec_pointer ("dest-surface",
177                                                                "Destination surface",
178                                                                "Cairo surface to which the animation will happen",
179                                                                G_PARAM_READWRITE));
180
181         g_type_class_add_private (klass, sizeof (EvTransitionAnimationPriv));
182 }
183
184 static void
185 paint_surface (cairo_t         *cr,
186                cairo_surface_t *surface,
187                gdouble          x_offset,
188                gdouble          y_offset,
189                gdouble          alpha,
190                GdkRectangle     page_area)
191 {
192         gint width, height;
193
194         gdk_cairo_rectangle (cr, &page_area);
195         cairo_clip (cr);
196
197         width = cairo_image_surface_get_width (surface);
198         height = cairo_image_surface_get_height (surface);
199
200         cairo_save (cr);
201
202         if (width != page_area.width || height != page_area.height) {
203                 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
204                 cairo_scale (cr,
205                              (gdouble) page_area.width / width,
206                              (gdouble) page_area.height / height);
207         }
208
209         cairo_surface_set_device_offset (surface, x_offset, y_offset);
210         cairo_set_source_surface (cr, surface, 0, 0);
211
212         if (alpha == 0.)
213                 cairo_paint (cr);
214         else
215                 cairo_paint_with_alpha (cr, alpha);
216
217         cairo_restore (cr);
218 }
219
220 /* animations */
221 static void
222 ev_transition_animation_split (cairo_t               *cr,
223                                EvTransitionAnimation *animation,
224                                EvTransitionEffect    *effect,
225                                gdouble                progress,
226                                GdkRectangle           page_area)
227 {
228         EvTransitionAnimationPriv *priv;
229         EvTransitionEffectAlignment alignment;
230         EvTransitionEffectDirection direction;
231         gint width, height;
232
233         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
234         width = page_area.width;
235         height = page_area.height;
236
237         g_object_get (effect,
238                       "alignment", &alignment,
239                       "direction", &direction,
240                       NULL);
241
242         if (direction == EV_TRANSITION_DIRECTION_INWARD) {
243                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
244
245                 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
246                         cairo_rectangle (cr,
247                                          0,
248                                          height * progress / 2,
249                                          width,
250                                          height * (1 - progress));
251                 } else {
252                         cairo_rectangle (cr,
253                                          width * progress / 2,
254                                          0,
255                                          width * (1 - progress),
256                                          height);
257                 }
258
259                 cairo_clip (cr);
260
261                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
262         } else {
263                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
264
265                 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
266                         cairo_rectangle (cr,
267                                          0,
268                                          (height / 2) - (height * progress / 2),
269                                          width,
270                                          height * progress);
271                 } else {
272                         cairo_rectangle (cr,
273                                          (width / 2) - (width * progress / 2),
274                                          0,
275                                          width * progress,
276                                          height);
277                 }
278
279                 cairo_clip (cr);
280
281                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
282         }
283 }
284
285 static void
286 ev_transition_animation_blinds (cairo_t               *cr,
287                                 EvTransitionAnimation *animation,
288                                 EvTransitionEffect    *effect,
289                                 gdouble                progress,
290                                 GdkRectangle           page_area)
291 {
292         EvTransitionAnimationPriv *priv;
293         EvTransitionEffectAlignment alignment;
294         gint width, height, i;
295
296         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
297         width = page_area.width;
298         height = page_area.height;
299
300         g_object_get (effect,
301                       "alignment", &alignment,
302                       NULL);
303
304         paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
305
306         for (i = 0; i < N_BLINDS; i++) {
307                 cairo_save (cr);
308
309                 if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
310                         cairo_rectangle (cr,
311                                          0,
312                                          height / N_BLINDS * i,
313                                          width,
314                                          height / N_BLINDS * progress);
315                 } else {
316                         cairo_rectangle (cr,
317                                          width / N_BLINDS * i,
318                                          0,
319                                          width / N_BLINDS * progress,
320                                          height);
321                 }
322
323                 cairo_clip (cr);
324                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
325                 cairo_restore (cr);
326         }
327 }
328
329 static void
330 ev_transition_animation_box (cairo_t               *cr,
331                              EvTransitionAnimation *animation,
332                              EvTransitionEffect    *effect,
333                              gdouble                progress,
334                              GdkRectangle           page_area)
335 {
336         EvTransitionAnimationPriv *priv;
337         EvTransitionEffectDirection direction;
338         gint width, height;
339
340         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
341         width = page_area.width;
342         height = page_area.height;
343
344         g_object_get (effect,
345                       "direction", &direction,
346                       NULL);
347
348         if (direction == EV_TRANSITION_DIRECTION_INWARD) {
349                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
350
351                 cairo_rectangle (cr,
352                                  width * progress / 2,
353                                  height * progress / 2,
354                                  width * (1 - progress),
355                                  height * (1 - progress));
356                 cairo_clip (cr);
357
358                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
359         } else {
360                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
361
362                 cairo_rectangle (cr,
363                                  (width / 2) - (width * progress / 2),
364                                  (height / 2) - (height * progress / 2),
365                                  width * progress,
366                                  height * progress);
367                 cairo_clip (cr);
368
369                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
370         }
371 }
372
373 static void
374 ev_transition_animation_wipe (cairo_t               *cr,
375                               EvTransitionAnimation *animation,
376                               EvTransitionEffect    *effect,
377                               gdouble                progress,
378                               GdkRectangle           page_area)
379 {
380         EvTransitionAnimationPriv *priv;
381         gint width, height;
382         gint angle;
383
384         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
385         width = page_area.width;
386         height = page_area.height;
387
388         g_object_get (effect,
389                       "angle", &angle,
390                       NULL);
391
392         paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
393
394         if (angle == 0) {
395                 /* left to right */
396                 cairo_rectangle (cr,
397                                  0, 0,
398                                  width * progress,
399                                  height);
400         } else if (angle <= 90) {
401                 /* bottom to top */
402                 cairo_rectangle (cr,
403                                  0,
404                                  height * (1 - progress),
405                                  width,
406                                  height * progress);
407         } else if (angle <= 180) {
408                 /* right to left */
409                 cairo_rectangle (cr,
410                                  width * (1 - progress),
411                                  0,
412                                  width * progress,
413                                  height);
414         } else if (angle <= 270) {
415                 /* top to bottom */
416                 cairo_rectangle (cr,
417                                  0, 0,
418                                  width,
419                                  height * progress);
420         }
421
422         cairo_clip (cr);
423
424         paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
425 }
426
427 static void
428 ev_transition_animation_dissolve (cairo_t               *cr,
429                                   EvTransitionAnimation *animation,
430                                   EvTransitionEffect    *effect,
431                                   gdouble                progress,
432                                   GdkRectangle           page_area)
433 {
434         EvTransitionAnimationPriv *priv;
435
436         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
437
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);
440 }
441
442 static void
443 ev_transition_animation_push (cairo_t               *cr,
444                               EvTransitionAnimation *animation,
445                               EvTransitionEffect    *effect,
446                               gdouble                progress,
447                               GdkRectangle           page_area)
448 {
449         EvTransitionAnimationPriv *priv;
450         gint width, height;
451         gint angle;
452
453         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
454         width = page_area.width;
455         height = page_area.height;
456
457         g_object_get (effect,
458                       "angle", &angle,
459                       NULL);
460
461         if (angle == 0) {
462                 /* left to right */
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);
465         } else {
466                 /* top to bottom */
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);
469         }
470 }
471
472 static void
473 ev_transition_animation_cover (cairo_t               *cr,
474                                EvTransitionAnimation *animation,
475                                EvTransitionEffect    *effect,
476                                gdouble                progress,
477                                GdkRectangle           page_area)
478 {
479         EvTransitionAnimationPriv *priv;
480         gint width, height;
481         gint angle;
482
483         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
484         width = page_area.width;
485         height = page_area.height;
486
487         g_object_get (effect,
488                       "angle", &angle,
489                       NULL);
490
491         paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
492
493         if (angle == 0) {
494                 /* left to right */
495                 paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 0, page_area);
496         } else {
497                 /* top to bottom */
498                 paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 0, page_area);
499         }
500 }
501
502 static void
503 ev_transition_animation_uncover (cairo_t               *cr,
504                                  EvTransitionAnimation *animation,
505                                  EvTransitionEffect    *effect,
506                                  gdouble                progress,
507                                  GdkRectangle           page_area)
508 {
509         EvTransitionAnimationPriv *priv;
510         gint width, height;
511         gint angle;
512
513         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
514         width = page_area.width;
515         height = page_area.height;
516
517         g_object_get (effect,
518                       "angle", &angle,
519                       NULL);
520
521         paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
522
523         if (angle == 0) {
524                 /* left to right */
525                 paint_surface (cr, priv->origin_surface, - (width * progress), 0, 0, page_area);
526         } else {
527                 /* top to bottom */
528                 paint_surface (cr, priv->origin_surface, 0, - (height * progress), 0, page_area);
529         }
530 }
531
532 static void
533 ev_transition_animation_fade (cairo_t               *cr,
534                               EvTransitionAnimation *animation,
535                               EvTransitionEffect    *effect,
536                               gdouble                progress,
537                               GdkRectangle           page_area)
538 {
539         EvTransitionAnimationPriv *priv;
540
541         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
542
543         paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
544         paint_surface (cr, priv->dest_surface, 0, 0, progress, page_area);
545 }
546
547 void
548 ev_transition_animation_paint (EvTransitionAnimation *animation,
549                                cairo_t               *cr,
550                                GdkRectangle           page_area)
551 {
552         EvTransitionAnimationPriv *priv;
553         EvTransitionEffectType type;
554         gdouble progress;
555
556         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
557
558         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
559         g_object_get (priv->effect, "type", &type, NULL);
560         progress = ev_timeline_get_progress (EV_TIMELINE (animation));
561
562         if (!priv->dest_surface) {
563                 /* animation is still not ready, paint the origin surface */
564                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
565                 return;
566         }
567
568         switch (type) {
569         case EV_TRANSITION_EFFECT_REPLACE:
570                 /* just paint the destination slide */
571                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
572                 break;
573         case EV_TRANSITION_EFFECT_SPLIT:
574                 ev_transition_animation_split (cr, animation, priv->effect, progress, page_area);
575                 break;
576         case EV_TRANSITION_EFFECT_BLINDS:
577                 ev_transition_animation_blinds (cr, animation, priv->effect, progress, page_area);
578                 break;
579         case EV_TRANSITION_EFFECT_BOX:
580                 ev_transition_animation_box (cr, animation, priv->effect, progress, page_area);
581                 break;
582         case EV_TRANSITION_EFFECT_WIPE:
583                 ev_transition_animation_wipe (cr, animation, priv->effect, progress, page_area);
584                 break;
585         case EV_TRANSITION_EFFECT_DISSOLVE:
586                 ev_transition_animation_dissolve (cr, animation, priv->effect, progress, page_area);
587                 break;
588         case EV_TRANSITION_EFFECT_PUSH:
589                 ev_transition_animation_push (cr, animation, priv->effect, progress, page_area);
590                 break;
591         case EV_TRANSITION_EFFECT_COVER:
592                 ev_transition_animation_cover (cr, animation, priv->effect, progress, page_area);
593                 break;
594         case EV_TRANSITION_EFFECT_UNCOVER:
595                 ev_transition_animation_uncover (cr, animation, priv->effect, progress, page_area);
596                 break;
597         case EV_TRANSITION_EFFECT_FADE:
598                 ev_transition_animation_fade (cr, animation, priv->effect, progress, page_area);
599                 break;
600         default: {
601                 GEnumValue *enum_value;
602
603                 enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_TRANSITION_EFFECT_TYPE), type);
604
605                 g_warning ("Unimplemented transition animation: '%s', "
606                            "please post a bug report in Evince bugzilla "
607                            "(http://bugzilla.gnome.org) with a testcase.",
608                            enum_value->value_nick);
609
610                 /* just paint the destination slide */
611                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
612                 }
613         }
614 }
615
616 EvTransitionAnimation *
617 ev_transition_animation_new (EvTransitionEffect *effect)
618 {
619         g_return_val_if_fail (EV_IS_TRANSITION_EFFECT (effect), NULL);
620
621         return g_object_new (EV_TYPE_TRANSITION_ANIMATION,
622                              "effect", effect,
623                              NULL);
624 }
625
626 void
627 ev_transition_animation_set_origin_surface (EvTransitionAnimation *animation,
628                                             cairo_surface_t       *origin_surface)
629 {
630         EvTransitionAnimationPriv *priv;
631         cairo_surface_t *surface;
632
633         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
634
635         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
636
637         surface = cairo_surface_reference (origin_surface);
638
639         if (priv->origin_surface)
640                 cairo_surface_destroy (priv->origin_surface);
641
642         priv->origin_surface = surface;
643         g_object_notify (G_OBJECT (animation), "origin-surface");
644
645         if (priv->origin_surface && priv->dest_surface)
646                 ev_timeline_start (EV_TIMELINE (animation));
647 }
648
649 void
650 ev_transition_animation_set_dest_surface (EvTransitionAnimation *animation,
651                                           cairo_surface_t       *dest_surface)
652 {
653         EvTransitionAnimationPriv *priv;
654         cairo_surface_t *surface;
655
656         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
657
658         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
659
660         surface = cairo_surface_reference (dest_surface);
661
662         if (priv->dest_surface)
663                 cairo_surface_destroy (priv->dest_surface);
664
665         priv->dest_surface = surface;
666         g_object_notify (G_OBJECT (animation), "dest-surface");
667
668         if (priv->origin_surface && priv->dest_surface)
669                 ev_timeline_start (EV_TIMELINE (animation));
670 }
671
672 gboolean
673 ev_transition_animation_ready (EvTransitionAnimation *animation)
674 {
675         EvTransitionAnimationPriv *priv;
676
677         g_return_val_if_fail (EV_IS_TRANSITION_ANIMATION (animation), FALSE);
678
679         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
680
681         return (priv->origin_surface && priv->dest_surface);
682 }