[gnome-photos/wip/rishi/zoom: 10/10] image-view: Implement smooth zooming
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/zoom: 10/10] image-view: Implement smooth zooming
- Date: Mon, 5 Jun 2017 18:34:05 +0000 (UTC)
commit ff5570ec63a9ab91bc7f0aa6a2bfcc80daa563f8
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 | 136 ++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 116 insertions(+), 20 deletions(-)
---
diff --git a/src/photos-image-view.c b/src/photos-image-view.c
index 8a3b603..fa159df 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
@@ -88,6 +93,24 @@ static void photos_image_view_computed (PhotosImageView *self, GeglRectangle *re
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 +279,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 +297,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),
+ EGG_ANIMATION_EASE_OUT_CUBIC,
+ 250,
+ 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 +455,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 +475,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 +542,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 +630,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 +965,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 +1003,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 +1036,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),
+ EGG_ANIMATION_EASE_OUT_CUBIC,
+ 250,
+ 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]