]> www.fi.muni.cz Git - evince.git/blob - shell/ev-transition-animation.c
Implement "split" effect.
[evince.git] / shell / 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 void
286 ev_transition_animation_paint (EvTransitionAnimation *animation,
287                                cairo_t               *cr,
288                                GdkRectangle           page_area)
289 {
290         EvTransitionAnimationPriv *priv;
291         EvTransitionEffectType type;
292         gdouble progress;
293
294         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
295
296         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
297         g_object_get (priv->effect, "type", &type, NULL);
298         progress = ev_timeline_get_progress (EV_TIMELINE (animation));
299
300         if (!priv->dest_surface) {
301                 /* animation is still not ready, paint the origin surface */
302                 paint_surface (cr, priv->origin_surface, 0, 0, 0, page_area);
303                 return;
304         }
305
306         switch (type) {
307         case EV_TRANSITION_EFFECT_REPLACE:
308                 /* just paint the destination slide */
309                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
310                 break;
311         case EV_TRANSITION_EFFECT_SPLIT:
312                 ev_transition_animation_split (cr, animation, priv->effect, progress, page_area);
313                 break;
314         default: {
315                 GEnumValue *enum_value;
316
317                 enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_TRANSITION_EFFECT_TYPE), type);
318
319                 g_warning ("Unimplemented transition animation: '%s', "
320                            "please post a bug report in Evince bugzilla "
321                            "(http://bugzilla.gnome.org) with a testcase.",
322                            enum_value->value_nick);
323
324                 /* just paint the destination slide */
325                 paint_surface (cr, priv->dest_surface, 0, 0, 0, page_area);
326                 }
327         }
328 }
329
330 EvTransitionAnimation *
331 ev_transition_animation_new (EvTransitionEffect *effect)
332 {
333         g_return_val_if_fail (EV_IS_TRANSITION_EFFECT (effect), NULL);
334
335         return g_object_new (EV_TYPE_TRANSITION_ANIMATION,
336                              "effect", effect,
337                              NULL);
338 }
339
340 void
341 ev_transition_animation_set_origin_surface (EvTransitionAnimation *animation,
342                                             cairo_surface_t       *origin_surface)
343 {
344         EvTransitionAnimationPriv *priv;
345         cairo_surface_t *surface;
346
347         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
348
349         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
350
351         surface = cairo_surface_reference (origin_surface);
352
353         if (priv->origin_surface)
354                 cairo_surface_destroy (priv->origin_surface);
355
356         priv->origin_surface = surface;
357         g_object_notify (G_OBJECT (animation), "origin-surface");
358
359         if (priv->origin_surface && priv->dest_surface)
360                 ev_timeline_start (EV_TIMELINE (animation));
361 }
362
363 void
364 ev_transition_animation_set_dest_surface (EvTransitionAnimation *animation,
365                                           cairo_surface_t       *dest_surface)
366 {
367         EvTransitionAnimationPriv *priv;
368         cairo_surface_t *surface;
369
370         g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
371
372         priv = EV_TRANSITION_ANIMATION_GET_PRIVATE (animation);
373
374         surface = cairo_surface_reference (dest_surface);
375
376         if (priv->dest_surface)
377                 cairo_surface_destroy (priv->dest_surface);
378
379         priv->dest_surface = surface;
380         g_object_notify (G_OBJECT (animation), "dest-surface");
381
382         if (priv->origin_surface && priv->dest_surface)
383                 ev_timeline_start (EV_TIMELINE (animation));
384 }