[gnome-photos/wip/rishi/zoom: 5/5] image-view: Implement smooth zooming
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/zoom: 5/5] image-view: Implement smooth zooming
- Date: Sat, 10 Jun 2017 16:59:54 +0000 (UTC)
commit dd5fdecf8616c070512fb557b4fe6012ea9c2ccf
Author: Debarshi Ray <debarshir gnome org>
Date: Fri Jun 2 19:52:59 2017 +0200
image-view: Implement smooth zooming
This is done by animating between the currently visible zoom level and
the new requested value. Since these intermediate zoom levels are not
meant to be exposed to users of the PhotosImageView widget, a small
helper type called PhotosImageViewHelper is used instead. The "zoom"
property of this helper is driven with EggAnimation, and the resulting
"notify" emissions are used by the widget for rendering.
https://bugzilla.gnome.org/show_bug.cgi?id=742662
src/photos-image-view.c | 140 ++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 120 insertions(+), 20 deletions(-)
---
diff --git a/src/photos-image-view.c b/src/photos-image-view.c
index 8a3b603..3f3cfd1 100644
--- a/src/photos-image-view.c
+++ b/src/photos-image-view.c
@@ -25,9 +25,11 @@
#include <cairo-gobject.h>
#include <glib.h>
+#include "egg-animation.h"
#include "photos-debug.h"
#include "photos-gegl.h"
#include "photos-image-view.h"
+#include "photos-image-view-helper.h"
#include "photos-marshalers.h"
#include "photos-utils.h"
@@ -35,6 +37,7 @@
struct _PhotosImageView
{
GtkDrawingArea parent_instance;
+ EggAnimation *zoom_animation;
GeglBuffer *buffer;
GeglNode *node;
GeglRectangle bbox_zoomed_old;
@@ -46,6 +49,7 @@ struct _PhotosImageView
cairo_region_t *bbox_region;
cairo_region_t *region;
gboolean best_fit;
+ gboolean queued_best_fit_animation;
gdouble height;
gdouble width;
gdouble x;
@@ -53,7 +57,8 @@ struct _PhotosImageView
gdouble y;
gdouble y_scaled;
gdouble zoom;
- gdouble zoom_scaled;
+ gdouble zoom_visible;
+ gdouble zoom_visible_scaled;
};
enum
@@ -84,10 +89,32 @@ G_DEFINE_TYPE_WITH_CODE (PhotosImageView, photos_image_view, GTK_TYPE_DRAWING_AR
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL));
+static const EggAnimationMode ZOOM_ANIMATION_MODE = EGG_ANIMATION_EASE_OUT_CUBIC;
+static const guint ZOOM_ANIMATION_DURATION = 250; /* ms */
+
+
static void photos_image_view_computed (PhotosImageView *self, GeglRectangle *rect);
static void
+photos_image_view_notify_zoom (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+ PhotosImageView *self = PHOTOS_IMAGE_VIEW (user_data);
+ PhotosImageViewHelper *helper = PHOTOS_IMAGE_VIEW_HELPER (object);
+ gint scale_factor;
+
+ g_return_if_fail (EGG_IS_ANIMATION (self->zoom_animation));
+
+ self->zoom_visible = photos_image_view_helper_get_zoom (helper);
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ self->zoom_visible_scaled = self->zoom_visible * scale_factor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+
+static void
photos_image_view_update_buffer (PhotosImageView *self)
{
const Babl *format;
@@ -256,7 +283,7 @@ photos_image_view_update (PhotosImageView *self)
viewport_height_real = allocation.height * scale_factor;
viewport_width_real = allocation.width * scale_factor;
- if (self->best_fit)
+ if (self->best_fit && self->zoom_animation == NULL)
{
gdouble zoom_scaled = 1.0;
@@ -274,22 +301,51 @@ photos_image_view_update (PhotosImageView *self)
bbox_zoomed.x = (gint) (zoom_scaled * bbox.x + 0.5);
bbox_zoomed.y = (gint) (zoom_scaled * bbox.y + 0.5);
- self->zoom_scaled = zoom_scaled;
- self->zoom = self->zoom_scaled / (gdouble) scale_factor;
+ self->zoom = zoom_scaled / (gdouble) scale_factor;
+ g_object_notify (G_OBJECT (self), "zoom");
+
+ if (self->zoom_visible > 0.0 && self->queued_best_fit_animation)
+ {
+ GdkFrameClock *frame_clock;
+ PhotosImageViewHelper *helper;
+
+ helper = photos_image_view_helper_new ();
+ photos_image_view_helper_set_zoom (helper, self->zoom_visible);
+ g_signal_connect (helper, "notify::zoom", G_CALLBACK (photos_image_view_notify_zoom), self);
+
+ frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
+
+ self->zoom_animation = egg_object_animate_full (g_object_ref (helper),
+ ZOOM_ANIMATION_MODE,
+ ZOOM_ANIMATION_DURATION,
+ frame_clock,
+ g_object_unref,
+ helper,
+ "zoom",
+ self->zoom,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (self->zoom_animation), (gpointer *) &self->zoom_animation);
+
+ g_object_unref (helper);
+ goto out;
+ }
+ else
+ {
+ self->zoom_visible = self->zoom;
+ self->zoom_visible_scaled = zoom_scaled;
+ }
self->x_scaled = (bbox_zoomed.width - viewport_width_real) / 2.0;
self->y_scaled = (bbox_zoomed.height - viewport_height_real) / 2.0;
-
- g_object_notify (G_OBJECT (self), "zoom");
}
else
{
gdouble ratio_old;
- bbox_zoomed.width = (gint) (self->zoom_scaled * bbox.width + 0.5);
- bbox_zoomed.height = (gint) (self->zoom_scaled * bbox.height + 0.5);
- bbox_zoomed.x = (gint) (self->zoom_scaled * bbox.x + 0.5);
- bbox_zoomed.y = (gint) (self->zoom_scaled * bbox.y + 0.5);
+ bbox_zoomed.width = (gint) (self->zoom_visible_scaled * bbox.width + 0.5);
+ bbox_zoomed.height = (gint) (self->zoom_visible_scaled * bbox.height + 0.5);
+ bbox_zoomed.x = (gint) (self->zoom_visible_scaled * bbox.x + 0.5);
+ bbox_zoomed.y = (gint) (self->zoom_visible_scaled * bbox.y + 0.5);
if (bbox_zoomed.width > viewport_width_real)
{
@@ -403,8 +459,8 @@ photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *r
gint64 start;
g_return_if_fail (GEGL_IS_BUFFER (self->buffer));
- g_return_if_fail (self->zoom > 0.0);
- g_return_if_fail (self->zoom_scaled > 0.0);
+ g_return_if_fail (self->zoom_visible > 0.0);
+ g_return_if_fail (self->zoom_visible_scaled > 0.0);
scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
@@ -423,7 +479,7 @@ photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *r
stride = bpp * roi.width;
gegl_buffer_get (self->buffer,
&roi,
- self->zoom_scaled,
+ self->zoom_visible_scaled,
format,
buf,
stride,
@@ -490,6 +546,8 @@ photos_image_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
self->allocation_scaled_old.height = allocation->height * scale_factor;
self->allocation_scaled_old.width = allocation->width * scale_factor;
+
+ self->queued_best_fit_animation = FALSE;
}
@@ -576,6 +634,12 @@ photos_image_view_dispose (GObject *object)
{
PhotosImageView *self = PHOTOS_IMAGE_VIEW (object);
+ if (self->zoom_animation != NULL)
+ {
+ egg_animation_stop (self->zoom_animation);
+ g_assert_null (self->zoom_animation);
+ }
+
g_clear_object (&self->buffer);
g_clear_object (&self->node);
g_clear_object (&self->hadjustment);
@@ -905,8 +969,11 @@ photos_image_view_set_best_fit (PhotosImageView *self, gboolean best_fit)
if (self->best_fit)
{
+ if (self->zoom_animation != NULL)
+ egg_animation_stop (self->zoom_animation);
+
self->zoom = 0.0;
- self->zoom_scaled = 0.0;
+ self->queued_best_fit_animation = TRUE;
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify (G_OBJECT (self), "zoom");
}
@@ -940,7 +1007,8 @@ photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
self->y = 0.0;
self->y_scaled = 0.0;
self->zoom = 0.0;
- self->zoom_scaled = 0.0;
+ self->zoom_visible = 0.0;
+ self->zoom_visible_scaled = 0.0;
g_clear_object (&self->buffer);
g_clear_object (&self->node);
g_clear_pointer (&self->bbox_region, (GDestroyNotify) cairo_region_destroy);
@@ -972,20 +1040,52 @@ photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
void
photos_image_view_set_zoom (PhotosImageView *self, gdouble zoom)
{
- gint scale_factor;
-
g_return_if_fail (PHOTOS_IS_IMAGE_VIEW (self));
if (photos_utils_equal_double (self->zoom, zoom))
return;
+ if (self->zoom_animation != NULL)
+ egg_animation_stop (self->zoom_animation);
+
self->best_fit = FALSE;
self->zoom = zoom;
- scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
- self->zoom_scaled = self->zoom * scale_factor;
+ if (self->zoom_visible > 0.0)
+ {
+ GdkFrameClock *frame_clock;
+ PhotosImageViewHelper *helper;
+
+ helper = photos_image_view_helper_new ();
+ photos_image_view_helper_set_zoom (helper, self->zoom_visible);
+ g_signal_connect (helper, "notify::zoom", G_CALLBACK (photos_image_view_notify_zoom), self);
+
+ frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
+
+ self->zoom_animation = egg_object_animate_full (g_object_ref (helper),
+ ZOOM_ANIMATION_MODE,
+ ZOOM_ANIMATION_DURATION,
+ frame_clock,
+ g_object_unref,
+ helper,
+ "zoom",
+ self->zoom,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (self->zoom_animation), (gpointer *) &self->zoom_animation);
+
+ g_object_unref (helper);
+ }
+ else
+ {
+ gint scale_factor;
- gtk_widget_queue_resize (GTK_WIDGET (self));
+ self->zoom_visible = self->zoom;
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ self->zoom_visible_scaled = self->zoom_visible * scale_factor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
g_object_notify (G_OBJECT (self), "best-fit");
g_object_notify (G_OBJECT (self), "zoom");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]