[PATCH weston 4/4 v1] Implement animated zoom transitions.

Scott Moreau oreaus at gmail.com
Sun May 20 23:33:21 PDT 2012


This patch introduces animated transitions for zoom. It uses springs to drive
the animations but there were some problems, so there are a couple of things
that have to be done to make this happen. First, the spring timestamp is set
with weston_compositor_get_time() but when the first frame handler is called,
the msec value is less than the timestamp and this causes an infinite loop
when calling weston_spring_update(). To solve the problem and make this
function more robust, a cast was added to ensure a negative value doesn't
wreak havoc when the resulting value is promoted to unsigned. Second, when
transitioning from text cursor position to pointer position, the spring would
not end quickly enough when the pointer was on the move. This caused the
spring animation to continue until the pointer stopped moving for some time,
all the while causing undesired behavior. The best solution I found was to
set the spring's 'done threshold' higher to end the spring early, in addition
to tweaking the sping current and k values. This patch quietly adds a float
to the spring struct and initializes it to the original value while allowing
adjustment of the 'done threshold' as needed. The result is that the spring
ends quickly and the user doesn't experience buggy behavior in this case.

 src/compositor.c |  198 ++++++++++++++++++++++++++++++++++++++++++++++++------
 src/compositor.h |   45 ++++++++----
 src/shell.c      |   14 +---
 src/util.c       |    7 +-
 4 files changed, 214 insertions(+), 50 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index b0b0f75..475b1ac 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -2439,6 +2439,118 @@ weston_output_destroy(struct weston_output *output)
 	wl_display_remove_global(c->wl_display, output->global);
 }
 
+static void
+weston_zoom_frame_xy(struct weston_animation *animation,
+		struct weston_output *output, uint32_t msecs)
+{
+	weston_spring_update(&output->zoom.spring.x, msecs);
+	weston_spring_update(&output->zoom.spring.y, msecs);
+
+	if (output->zoom.spring.x.current < output->zoom.trans_min) {
+		output->zoom.spring.x.current = output->zoom.trans_min;
+		output->zoom.spring.x.target = output->zoom.trans_min;
+	}
+	if (output->zoom.spring.x.current > output->zoom.trans_max) {
+		output->zoom.spring.x.current = output->zoom.trans_max;
+		output->zoom.spring.x.target = output->zoom.trans_max;
+	}
+	if (output->zoom.spring.y.current < output->zoom.trans_min) {
+		output->zoom.spring.y.current = output->zoom.trans_min;
+		output->zoom.spring.y.target = output->zoom.trans_min;
+	}
+	if (output->zoom.spring.y.current > output->zoom.trans_max) {
+		output->zoom.spring.y.current = output->zoom.trans_max;
+		output->zoom.spring.y.target = output->zoom.trans_max;
+	}
+
+	if (weston_spring_done(&output->zoom.spring.x))
+		output->zoom.spring.x.current = output->zoom.trans_x;
+
+	if (weston_spring_done(&output->zoom.spring.y))
+		output->zoom.spring.y.current = output->zoom.trans_y;
+
+	if (weston_spring_done(&output->zoom.spring.x) ||
+	    weston_spring_done(&output->zoom.spring.y)) {
+		wl_list_remove(&output->zoom.animation.xy.link);
+		output->zoom.animation.moving = 0;
+	} else {
+		output->dirty = 1;
+		weston_output_damage(output);
+	}
+}
+
+static void
+weston_zoom_frame_z(struct weston_animation *animation,
+		struct weston_output *output, uint32_t msecs)
+{
+	weston_spring_update(&output->zoom.spring.z, msecs);
+
+	if (output->zoom.spring.z.current < 0.0) {
+		output->zoom.spring.z.current = 0.0;
+		output->zoom.spring.z.target = 0.0;
+	}
+
+	if (output->zoom.spring.z.current > output->zoom.maximum_level) {
+		output->zoom.spring.z.current = output->zoom.maximum_level;
+		output->zoom.spring.z.target = output->zoom.maximum_level;
+	}
+
+	if (weston_spring_done(&output->zoom.spring.z)) {
+		output->zoom.animation.from_level = output->zoom.level;
+		output->zoom.spring.z.current = output->zoom.level;
+		wl_list_remove(&output->zoom.animation.z.link);
+		output->zoom.animation.zooming = 0;
+		if (output->zoom.level <= 0.0)
+			output->zoom.active = 0;
+	} else {
+		output->dirty = 1;
+		weston_output_damage(output);
+	}
+}
+
+static void
+weston_zoom_transition(struct weston_output *output, int xy_transition)
+{
+	if (!output->zoom.animation.moving && xy_transition) {
+		if (output->zoom.animation.from_x != output->zoom.trans_x)
+			weston_spring_init(&output->zoom.spring.x, 250.0,
+						output->zoom.animation.from_x,
+						output->zoom.trans_x);
+		if (output->zoom.animation.from_y != output->zoom.trans_y)
+			weston_spring_init(&output->zoom.spring.y, 250.0,
+						output->zoom.animation.from_y,
+						output->zoom.trans_y);
+		output->zoom.spring.x.friction = 1000;
+		output->zoom.spring.y.friction = 1000;
+
+		output->zoom.spring.x.timestamp = weston_compositor_get_time();
+		output->zoom.spring.y.timestamp = output->zoom.spring.x.timestamp;
+
+		wl_list_insert(&output->compositor->animation_list,
+			       &output->zoom.animation.xy.link);
+
+		output->zoom.animation.moving = 1;
+	}
+
+	if (!output->zoom.animation.zooming) {
+		weston_spring_init(&output->zoom.spring.z, 250.0,
+					output->zoom.animation.from_level,
+					output->zoom.level);
+		output->zoom.spring.z.friction = 1000;
+		output->zoom.animation.from_level = output->zoom.level;
+		output->zoom.spring.z.timestamp = weston_compositor_get_time();
+		wl_list_insert(&output->compositor->animation_list,
+			       &output->zoom.animation.z.link);
+		output->zoom.animation.zooming = 1;
+	}
+
+	output->zoom.animation.from_x = output->zoom.trans_x;
+	output->zoom.animation.from_y = output->zoom.trans_y;
+
+	output->dirty = 1;
+	weston_output_damage(output);
+}
+
 WL_EXPORT void
 weston_text_cursor_position_notify(struct weston_surface *surface,
 						int32_t x, int32_t y)
@@ -2460,38 +2572,62 @@ weston_text_cursor_position_notify(struct weston_surface *surface,
 }
 
 WL_EXPORT void
-weston_output_update_zoom(struct weston_output *output, wl_fixed_t fx, wl_fixed_t fy, int text_cursor)
+weston_output_update_zoom(struct weston_output *output, wl_fixed_t fx,
+							wl_fixed_t fy, int text_cursor)
 {
 	int32_t x, y;
-	float trans_min, trans_max;
+	float level = output->zoom.spring.z.current;
 
 	if (output->zoom.level >= 1.0)
 		return;
 
+	output->zoom.fx = fx;
+	output->zoom.fy = fy;
 	x = wl_fixed_to_int(fx);
 	y = wl_fixed_to_int(fy);
 
-	trans_max = output->zoom.level * 2 - output->zoom.level;
-	trans_min = -trans_max;
+	output->zoom.trans_max = level * 2 - level;
+	output->zoom.trans_min = -output->zoom.trans_max;
 
 	output->zoom.trans_x = ((((float)(x - output->x) / output->current->width) *
-					(output->zoom.level * 2)) - output->zoom.level) *
-					(text_cursor ? (1 / output->zoom.level) : 1);
+					(level * 2)) - level) *
+					(text_cursor ? (1 / level) : 1);
 	output->zoom.trans_y = ((((float)(y - output->y) / output->current->height) *
-					(output->zoom.level * 2)) - output->zoom.level) *
-					(text_cursor ? (1 / output->zoom.level) : 1);
-
-	if (output->zoom.trans_x > trans_max)
-		output->zoom.trans_x = trans_max;
-	else if (output->zoom.trans_x < trans_min)
-		output->zoom.trans_x = trans_min;
-	if (output->zoom.trans_y > trans_max)
-		output->zoom.trans_y = trans_max;
-	else if (output->zoom.trans_y < trans_min)
-		output->zoom.trans_y = trans_min;
+					(level * 2)) - level) *
+					(text_cursor ? (1 / level) : 1);
+
+	if (!text_cursor && output->zoom.animation.moving) {
+		/* This condition means we're transitioning from text cursor
+		 * position to mouse pointer. Here we want to get back to tracking
+		 * the pointer ASAP. */
+		output->zoom.spring.x.current +=
+			(output->zoom.trans_x - output->zoom.spring.x.current) * 0.005;
+		output->zoom.spring.y.current +=
+			(output->zoom.trans_y - output->zoom.spring.y.current) * 0.005;
+		if (output->zoom.spring.x.k < 600 ||
+		    output->zoom.spring.y.k < 600) {
+			output->zoom.spring.x.k += 10;
+			output->zoom.spring.y.k += 10;
+		}
+		output->zoom.spring.x.done = 0.02;
+		output->zoom.spring.y.done = 0.02;
+	}
 
-	output->dirty = 1;
-	weston_output_damage(output);
+	if (output->zoom.trans_x > output->zoom.trans_max)
+		output->zoom.trans_x = output->zoom.trans_max;
+	else if (output->zoom.trans_x < output->zoom.trans_min)
+		output->zoom.trans_x = output->zoom.trans_min;
+	if (output->zoom.trans_y > output->zoom.trans_max)
+		output->zoom.trans_y = output->zoom.trans_max;
+	else if (output->zoom.trans_y < output->zoom.trans_min)
+		output->zoom.trans_y = output->zoom.trans_min;
+
+	output->zoom.spring.z.target = output->zoom.level;
+	output->zoom.spring.x.target = output->zoom.trans_x;
+	output->zoom.spring.y.target = output->zoom.trans_y;
+
+	weston_zoom_transition(output, text_cursor != output->zoom.last_transition);
+	output->zoom.last_transition = text_cursor;
 }
 
 WL_EXPORT void
@@ -2513,10 +2649,19 @@ weston_output_update_matrix(struct weston_output *output)
 			    flip * 2.0 / (output->current->height + output->border.top + output->border.bottom), 1);
 
 	if (output->zoom.active) {
-		magnification = 1 / (1 - output->zoom.level);
+		magnification = 1 / (1 - output->zoom.spring.z.current);
 		weston_matrix_init(&camera);
 		weston_matrix_init(&modelview);
-		weston_matrix_translate(&camera, output->zoom.trans_x, flip * output->zoom.trans_y, 0);
+		if (output->zoom.animation.moving)
+			weston_matrix_translate(&camera, output->zoom.spring.x.current,
+						  flip * output->zoom.spring.y.current, 0);
+		else {
+			weston_output_update_zoom(output, output->zoom.fx,
+							  output->zoom.fy,
+							  output->zoom.last_transition);
+			weston_matrix_translate(&camera, output->zoom.trans_x,
+						  flip * output->zoom.trans_y, 0);
+		}
 		weston_matrix_invert(&modelview, &camera);
 		weston_matrix_scale(&modelview, magnification, magnification, 1.0);
 		weston_matrix_multiply(&output->matrix, &modelview);
@@ -2554,10 +2699,19 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 	wl_list_init(&output->read_pixels_list);
 
 	output->zoom.active = 0;
-	output->zoom.increment = 0.05;
+	output->zoom.animation.moving = 0;
+	output->zoom.animation.zooming = 0;
+	output->zoom.last_transition = 0;
+	output->zoom.increment = 0.1;
+	output->zoom.maximum_level = 0.95;
 	output->zoom.level = 0.0;
+	output->zoom.animation.from_level = 0.0;
+	output->zoom.animation.from_x = 0.0;
+	output->zoom.animation.from_y = 0.0;
 	output->zoom.trans_x = 0.0;
 	output->zoom.trans_y = 0.0;
+	output->zoom.animation.z.frame = weston_zoom_frame_z;
+	output->zoom.animation.xy.frame = weston_zoom_frame_xy;
 
 	output->flags = flags;
 	weston_output_move(output, x, y);
diff --git a/src/compositor.h b/src/compositor.h
index 82bfb26..be5a2ac 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -73,11 +73,41 @@ struct weston_border {
 	int32_t left, right, top, bottom;
 };
 
+struct weston_animation {
+	void (*frame)(struct weston_animation *animation,
+		      struct weston_output *output, uint32_t msecs);
+	struct wl_list link;
+};
+
+struct weston_spring {
+	double k;
+	double friction;
+	double current;
+	double target;
+	double previous;
+	double done;
+	uint32_t timestamp;
+};
+
 struct weston_output_zoom {
 	int active;
 	float increment;
+	float maximum_level;
 	float level;
+	int fx, fy;
 	float trans_x, trans_y;
+	float trans_min, trans_max;
+	int last_transition;
+	struct {
+		struct weston_animation xy, z;
+		float from_level;
+		float from_x, from_y;
+		int moving;
+		int zooming;
+	} animation;
+	struct {
+		struct weston_spring x, y, z;
+	} spring;
 };
 
 /* bit compatible with drm definitions. */
@@ -173,21 +203,6 @@ struct weston_shader {
 	GLint opaque_uniform;
 };
 
-struct weston_animation {
-	void (*frame)(struct weston_animation *animation,
-		      struct weston_output *output, uint32_t msecs);
-	struct wl_list link;
-};
-
-struct weston_spring {
-	double k;
-	double friction;
-	double current;
-	double target;
-	double previous;
-	uint32_t timestamp;
-};
-
 enum {
 	WESTON_COMPOSITOR_ACTIVE,
 	WESTON_COMPOSITOR_IDLE,		/* shell->unlock called on activity */
diff --git a/src/shell.c b/src/shell.c
index 1df9c64..d038c84 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -391,8 +391,7 @@ unresponsive_fade_frame(struct weston_animation *animation,
 			wl_list_remove(&shsurf->unresponsive_animation.current.link);
 			shsurf->unresponsive_animation.exists = 0;
 		}
-	}
-	else {
+	} else {
 		while (step < msecs - shsurf->unresponsive_animation.timestamp) {
 			if (surface->saturation < 255)
 				surface->saturation += 5;
@@ -1635,7 +1634,6 @@ zoom_binding(struct wl_seat *seat, uint32_t time,
 	struct weston_seat *ws = (struct weston_seat *) seat;
 	struct weston_compositor *compositor = ws->compositor;
 	struct weston_output *output;
-	float maximum_level;
 
 	wl_list_for_each(output, &compositor->output_list, link) {
 		if (pixman_region32_contains_point(&output->region,
@@ -1645,15 +1643,11 @@ zoom_binding(struct wl_seat *seat, uint32_t time,
 			output->zoom.active = 1;
 			output->zoom.level += output->zoom.increment * value;
 
-			if (output->zoom.level <= 0.0) {
-				output->zoom.active = 0;
+			if (output->zoom.level < 0.0)
 				output->zoom.level = 0.0;
-			}
-
-			maximum_level = 1 - output->zoom.increment;
 
-			if (output->zoom.level > maximum_level)
-				output->zoom.level = maximum_level;
+			if (output->zoom.level > output->zoom.maximum_level)
+				output->zoom.level = output->zoom.maximum_level;
 
 			weston_output_update_zoom(output,
 			                          seat->pointer->x,
diff --git a/src/util.c b/src/util.c
index b06efd0..44910f1 100644
--- a/src/util.c
+++ b/src/util.c
@@ -39,6 +39,7 @@ weston_spring_init(struct weston_spring *spring,
 	spring->current = current;
 	spring->previous = current;
 	spring->target = target;
+	spring->done = 0.0002;
 }
 
 WL_EXPORT void
@@ -47,7 +48,7 @@ weston_spring_update(struct weston_spring *spring, uint32_t msec)
 	double force, v, current, step;
 
 	step = 0.01;
-	while (4 < msec - spring->timestamp) {
+	while (4 < (int)(msec - spring->timestamp)) {
 		current = spring->current;
 		v = current - spring->previous;
 		force = spring->k * (spring->target - current) / 10.0 +
@@ -81,8 +82,8 @@ weston_spring_update(struct weston_spring *spring, uint32_t msec)
 WL_EXPORT int
 weston_spring_done(struct weston_spring *spring)
 {
-	return fabs(spring->previous - spring->target) < 0.0002 &&
-		fabs(spring->current - spring->target) < 0.0002;
+	return fabs(spring->previous - spring->target) < spring->done &&
+		fabs(spring->current - spring->target) < spring->done;
 }
 
 struct weston_zoom {
-- 
1.7.7.6



More information about the wayland-devel mailing list