1
0
mirror of https://gitlab.gnome.org/GNOME/evince synced 2024-06-30 22:54:23 +00:00
evince/libview/ev-transition-animation.c
Qiu Wenbo 48f4198625 libview: Remove trailing whitespaces
Signed-off-by: Qiu Wenbo <qiuwenbo@kylinos.com.cn>
Signed-off-by: Pablo Correa Gomez <ablocorrea@hotmail.com>
2023-03-22 20:15:51 +00:00

678 lines
19 KiB
C

/* ev-transition-animation.c
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <cairo.h>
#include <gdk/gdk.h>
#include "ev-transition-animation.h"
#include "ev-timeline.h"
#define N_BLINDS 6
typedef struct EvTransitionAnimationPrivate EvTransitionAnimationPrivate;
struct EvTransitionAnimationPrivate {
EvTransitionEffect *effect;
cairo_surface_t *origin_surface;
cairo_surface_t *dest_surface;
};
enum {
PROP_0,
PROP_EFFECT,
PROP_ORIGIN_SURFACE,
PROP_DEST_SURFACE
};
G_DEFINE_TYPE_WITH_PRIVATE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
static void
ev_transition_animation_init (EvTransitionAnimation *animation)
{
}
static void
ev_transition_animation_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EvTransitionAnimationPrivate *priv;
priv = ev_transition_animation_get_instance_private (EV_TRANSITION_ANIMATION (object));
switch (prop_id) {
case PROP_EFFECT:
if (priv->effect)
g_object_unref (priv->effect);
priv->effect = g_value_dup_object (value);
break;
case PROP_ORIGIN_SURFACE:
ev_transition_animation_set_origin_surface (EV_TRANSITION_ANIMATION (object),
g_value_get_pointer (value));
break;
case PROP_DEST_SURFACE:
ev_transition_animation_set_dest_surface (EV_TRANSITION_ANIMATION (object),
g_value_get_pointer (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ev_transition_animation_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EvTransitionAnimationPrivate *priv;
priv = ev_transition_animation_get_instance_private (EV_TRANSITION_ANIMATION (object));
switch (prop_id) {
case PROP_EFFECT:
g_value_set_object (value, priv->effect);
break;
case PROP_ORIGIN_SURFACE:
g_value_set_pointer (value, priv->origin_surface);
break;
case PROP_DEST_SURFACE:
g_value_set_pointer (value, priv->dest_surface);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ev_transition_animation_finalize (GObject *object)
{
EvTransitionAnimationPrivate *priv;
priv = ev_transition_animation_get_instance_private (EV_TRANSITION_ANIMATION (object));
if (priv->effect)
g_object_unref (priv->effect);
if (priv->origin_surface)
cairo_surface_destroy (priv->origin_surface);
if (priv->dest_surface)
cairo_surface_destroy (priv->dest_surface);
G_OBJECT_CLASS (ev_transition_animation_parent_class)->finalize (object);
}
static GObject *
ev_transition_animation_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
EvTransitionAnimationPrivate *priv;
EvTransitionEffect *effect;
gdouble duration;
object = G_OBJECT_CLASS (ev_transition_animation_parent_class)->constructor (type,
n_construct_properties,
construct_params);
priv = ev_transition_animation_get_instance_private (EV_TRANSITION_ANIMATION (object));
effect = priv->effect;
g_object_get (effect, "duration-real", &duration, NULL);
ev_timeline_set_duration (EV_TIMELINE (object), duration * 1000);
return object;
}
static void
ev_transition_animation_class_init (EvTransitionAnimationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = ev_transition_animation_set_property;
object_class->get_property = ev_transition_animation_get_property;
object_class->finalize = ev_transition_animation_finalize;
object_class->constructor = ev_transition_animation_constructor;
g_object_class_install_property (object_class,
PROP_EFFECT,
g_param_spec_object ("effect",
"Effect",
"Transition effect description",
EV_TYPE_TRANSITION_EFFECT,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_ORIGIN_SURFACE,
g_param_spec_pointer ("origin-surface",
"Origin surface",
"Cairo surface from which the animation will happen",
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_DEST_SURFACE,
g_param_spec_pointer ("dest-surface",
"Destination surface",
"Cairo surface to which the animation will happen",
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
}
static void
paint_surface (cairo_t *cr,
cairo_surface_t *surface,
gdouble x_offset,
gdouble y_offset,
gdouble alpha,
GdkRectangle page_area)
{
cairo_save (cr);
gdk_cairo_rectangle (cr, &page_area);
cairo_clip (cr);
cairo_surface_set_device_offset (surface, x_offset, y_offset);
cairo_set_source_surface (cr, surface, 0, 0);
if (alpha == 1.)
cairo_paint (cr);
else
cairo_paint_with_alpha (cr, alpha);
cairo_restore (cr);
}
/* animations */
static void
ev_transition_animation_split (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
EvTransitionEffectAlignment alignment;
EvTransitionEffectDirection direction;
gint width, height;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"alignment", &alignment,
"direction", &direction,
NULL);
if (direction == EV_TRANSITION_DIRECTION_INWARD) {
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
cairo_rectangle (cr,
0,
height * progress / 2,
width,
height * (1 - progress));
} else {
cairo_rectangle (cr,
width * progress / 2,
0,
width * (1 - progress),
height);
}
cairo_clip (cr);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
} else {
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
cairo_rectangle (cr,
0,
(height / 2) - (height * progress / 2),
width,
height * progress);
} else {
cairo_rectangle (cr,
(width / 2) - (width * progress / 2),
0,
width * progress,
height);
}
cairo_clip (cr);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
}
}
static void
ev_transition_animation_blinds (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
EvTransitionEffectAlignment alignment;
gint width, height, i;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"alignment", &alignment,
NULL);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
for (i = 0; i < N_BLINDS; i++) {
cairo_save (cr);
if (alignment == EV_TRANSITION_ALIGNMENT_HORIZONTAL) {
cairo_rectangle (cr,
0,
height / N_BLINDS * i,
width,
height / N_BLINDS * progress);
} else {
cairo_rectangle (cr,
width / N_BLINDS * i,
0,
width / N_BLINDS * progress,
height);
}
cairo_clip (cr);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
cairo_restore (cr);
}
}
static void
ev_transition_animation_box (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
EvTransitionEffectDirection direction;
gint width, height;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"direction", &direction,
NULL);
if (direction == EV_TRANSITION_DIRECTION_INWARD) {
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
cairo_rectangle (cr,
width * progress / 2,
height * progress / 2,
width * (1 - progress),
height * (1 - progress));
cairo_clip (cr);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
} else {
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
cairo_rectangle (cr,
(width / 2) - (width * progress / 2),
(height / 2) - (height * progress / 2),
width * progress,
height * progress);
cairo_clip (cr);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
}
}
static void
ev_transition_animation_wipe (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
gint width, height;
gint angle;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"angle", &angle,
NULL);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
if (angle == 0) {
/* left to right */
cairo_rectangle (cr,
0, 0,
width * progress,
height);
} else if (angle <= 90) {
/* bottom to top */
cairo_rectangle (cr,
0,
height * (1 - progress),
width,
height * progress);
} else if (angle <= 180) {
/* right to left */
cairo_rectangle (cr,
width * (1 - progress),
0,
width * progress,
height);
} else if (angle <= 270) {
/* top to bottom */
cairo_rectangle (cr,
0, 0,
width,
height * progress);
}
cairo_clip (cr);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
}
static void
ev_transition_animation_dissolve (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
priv = ev_transition_animation_get_instance_private (animation);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
paint_surface (cr, priv->origin_surface, 0, 0, 1 - progress, page_area);
}
static void
ev_transition_animation_push (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
gint width, height;
gint angle;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"angle", &angle,
NULL);
if (angle == 0) {
/* left to right */
paint_surface (cr, priv->origin_surface, - (width * progress), 0, 1., page_area);
paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 1., page_area);
} else {
/* top to bottom */
paint_surface (cr, priv->origin_surface, 0, - (height * progress), 1., page_area);
paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 1., page_area);
}
}
static void
ev_transition_animation_cover (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
gint width, height;
gint angle;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"angle", &angle,
NULL);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
if (angle == 0) {
/* left to right */
paint_surface (cr, priv->dest_surface, width * (1 - progress), 0, 1., page_area);
} else {
/* top to bottom */
paint_surface (cr, priv->dest_surface, 0, height * (1 - progress), 1., page_area);
}
}
static void
ev_transition_animation_uncover (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
gint width, height;
gint angle;
priv = ev_transition_animation_get_instance_private (animation);
width = page_area.width;
height = page_area.height;
g_object_get (effect,
"angle", &angle,
NULL);
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
if (angle == 0) {
/* left to right */
paint_surface (cr, priv->origin_surface, - (width * progress), 0, 1., page_area);
} else {
/* top to bottom */
paint_surface (cr, priv->origin_surface, 0, - (height * progress), 1., page_area);
}
}
static void
ev_transition_animation_fade (cairo_t *cr,
EvTransitionAnimation *animation,
EvTransitionEffect *effect,
gdouble progress,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
priv = ev_transition_animation_get_instance_private (animation);
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
paint_surface (cr, priv->dest_surface, 0, 0, progress, page_area);
}
void
ev_transition_animation_paint (EvTransitionAnimation *animation,
cairo_t *cr,
GdkRectangle page_area)
{
EvTransitionAnimationPrivate *priv;
EvTransitionEffectType type;
gdouble progress;
g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
priv = ev_transition_animation_get_instance_private (animation);
if (!priv->dest_surface) {
/* animation is still not ready, paint the origin surface */
paint_surface (cr, priv->origin_surface, 0, 0, 1., page_area);
return;
}
g_object_get (priv->effect, "type", &type, NULL);
progress = ev_timeline_get_progress (EV_TIMELINE (animation));
switch (type) {
case EV_TRANSITION_EFFECT_REPLACE:
/* just paint the destination slide */
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
break;
case EV_TRANSITION_EFFECT_SPLIT:
ev_transition_animation_split (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_BLINDS:
ev_transition_animation_blinds (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_BOX:
ev_transition_animation_box (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_WIPE:
ev_transition_animation_wipe (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_DISSOLVE:
ev_transition_animation_dissolve (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_PUSH:
ev_transition_animation_push (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_COVER:
ev_transition_animation_cover (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_UNCOVER:
ev_transition_animation_uncover (cr, animation, priv->effect, progress, page_area);
break;
case EV_TRANSITION_EFFECT_FADE:
ev_transition_animation_fade (cr, animation, priv->effect, progress, page_area);
break;
default: {
GEnumValue *enum_value;
enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_TRANSITION_EFFECT_TYPE), type);
g_warning ("Unimplemented transition animation: '%s', "
"please post a bug report in the issue tracker "
"(https://gitlab.gnome.org/GNOME/evince/issues/) "
"with a testcase.",
enum_value->value_nick);
/* just paint the destination slide */
paint_surface (cr, priv->dest_surface, 0, 0, 1., page_area);
}
}
}
EvTransitionAnimation *
ev_transition_animation_new (EvTransitionEffect *effect)
{
g_return_val_if_fail (EV_IS_TRANSITION_EFFECT (effect), NULL);
return g_object_new (EV_TYPE_TRANSITION_ANIMATION,
"effect", effect,
NULL);
}
void
ev_transition_animation_set_origin_surface (EvTransitionAnimation *animation,
cairo_surface_t *origin_surface)
{
EvTransitionAnimationPrivate *priv;
cairo_surface_t *surface;
g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
priv = ev_transition_animation_get_instance_private (animation);
if (priv->origin_surface == origin_surface)
return;
surface = cairo_surface_reference (origin_surface);
if (priv->origin_surface)
cairo_surface_destroy (priv->origin_surface);
priv->origin_surface = surface;
g_object_notify (G_OBJECT (animation), "origin-surface");
if (priv->origin_surface && priv->dest_surface)
ev_timeline_start (EV_TIMELINE (animation));
}
void
ev_transition_animation_set_dest_surface (EvTransitionAnimation *animation,
cairo_surface_t *dest_surface)
{
EvTransitionAnimationPrivate *priv;
cairo_surface_t *surface;
g_return_if_fail (EV_IS_TRANSITION_ANIMATION (animation));
priv = ev_transition_animation_get_instance_private (animation);
if (priv->dest_surface == dest_surface)
return;
surface = cairo_surface_reference (dest_surface);
if (priv->dest_surface)
cairo_surface_destroy (priv->dest_surface);
priv->dest_surface = surface;
g_object_notify (G_OBJECT (animation), "dest-surface");
if (priv->origin_surface && priv->dest_surface)
ev_timeline_start (EV_TIMELINE (animation));
}
gboolean
ev_transition_animation_ready (EvTransitionAnimation *animation)
{
EvTransitionAnimationPrivate *priv;
g_return_val_if_fail (EV_IS_TRANSITION_ANIMATION (animation), FALSE);
priv = ev_transition_animation_get_instance_private (animation);
return (priv->origin_surface != NULL);
}