[PATCH 13/15] compositor: Support output/buffer scaling

alexl at redhat.com alexl at redhat.com
Wed May 22 05:41:37 PDT 2013


From: Alexander Larsson <alexl at redhat.com>

If you specify e.g. scale=2 in weston.ini an output section for the
X11 backend we automatically upscale all normal surfaces by this
amount. Additionally we respect a buffer_scale set on the buffer to
mean that the buffer is already in a scaled form.

This works with both the gl and the pixman renderer. The non-X
backends compile and work, but don't support changing the output
scale (they do downscale as needed due to buffer_scale though).

This also sends the new "scale" and "done" events on wl_output,
making clients aware of the scale.
---
 src/compositor-drm.c      |   7 +--
 src/compositor-fbdev.c    |   3 +-
 src/compositor-headless.c |   2 +-
 src/compositor-rpi.c      |   3 +-
 src/compositor-wayland.c  |   2 +-
 src/compositor-x11.c      |  60 ++++++++++++++++++-------
 src/compositor.c          | 110 +++++++++++++++++++++++++++++++++++++---------
 src/compositor.h          |  18 +++++++-
 src/gl-renderer.c         |  21 +++------
 src/pixman-renderer.c     |  48 +++++++++++++++++---
 10 files changed, 210 insertions(+), 64 deletions(-)

diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 35019e0..8b33256 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -858,7 +858,8 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base,
 	box = pixman_region32_extents(&dest_rect);
 	tbox = weston_transformed_rect(output_base->width,
 				       output_base->height,
-				       output_base->transform, *box);
+				       output_base->transform,
+				       1, *box);
 	s->dest_x = tbox.x1;
 	s->dest_y = tbox.y1;
 	s->dest_w = tbox.x2 - tbox.x1;
@@ -895,7 +896,7 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base,
 
 	tbox = weston_transformed_rect(wl_fixed_from_int(es->geometry.width),
 				       wl_fixed_from_int(es->geometry.height),
-				       es->buffer_transform, tbox);
+				       es->buffer_transform, 1, tbox);
 
 	s->src_x = tbox.x1 << 8;
 	s->src_y = tbox.y1 << 8;
@@ -1813,7 +1814,7 @@ create_output_for_connector(struct drm_compositor *ec,
 
 	weston_output_init(&output->base, &ec->base, x, y,
 			   connector->mmWidth, connector->mmHeight,
-			   o ? o->transform : WL_OUTPUT_TRANSFORM_NORMAL);
+			   o ? o->transform : WL_OUTPUT_TRANSFORM_NORMAL, 1);
 
 	if (ec->use_pixman) {
 		if (drm_output_init_pixman(output, ec) < 0) {
diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c
index 21028a5..c643c23 100644
--- a/src/compositor-fbdev.c
+++ b/src/compositor-fbdev.c
@@ -537,7 +537,8 @@ fbdev_output_create(struct fbdev_compositor *compositor,
 	weston_output_init(&output->base, &compositor->base,
 	                   0, 0, output->fb_info.width_mm,
 	                   output->fb_info.height_mm,
-	                   WL_OUTPUT_TRANSFORM_NORMAL);
+	                   WL_OUTPUT_TRANSFORM_NORMAL,
+			   1);
 
 	width = output->fb_info.x_resolution;
 	height = output->fb_info.y_resolution;
diff --git a/src/compositor-headless.c b/src/compositor-headless.c
index 0df0f7d..e4bd1be 100644
--- a/src/compositor-headless.c
+++ b/src/compositor-headless.c
@@ -112,7 +112,7 @@ headless_compositor_create_output(struct headless_compositor *c,
 
 	output->base.current = &output->mode;
 	weston_output_init(&output->base, &c->base, 0, 0, width, height,
-			   WL_OUTPUT_TRANSFORM_NORMAL);
+			   WL_OUTPUT_TRANSFORM_NORMAL, 1);
 
 	output->base.make = "weston";
 	output->base.model = "headless";
diff --git a/src/compositor-rpi.c b/src/compositor-rpi.c
index 3cb2b56..226c5ce 100644
--- a/src/compositor-rpi.c
+++ b/src/compositor-rpi.c
@@ -1080,7 +1080,8 @@ rpi_output_create(struct rpi_compositor *compositor)
 
 	weston_output_init(&output->base, &compositor->base,
 			   0, 0, round(mm_width), round(mm_height),
-			   WL_OUTPUT_TRANSFORM_NORMAL);
+			   WL_OUTPUT_TRANSFORM_NORMAL,
+			   1);
 
 	if (gl_renderer_output_create(&output->base,
 			(EGLNativeWindowType)&output->egl_window) < 0)
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index 4112401..511a12d 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -267,7 +267,7 @@ wayland_compositor_create_output(struct wayland_compositor *c,
 
 	output->base.current = &output->mode;
 	weston_output_init(&output->base, &c->base, 0, 0, width, height,
-						WL_OUTPUT_TRANSFORM_NORMAL);
+			   WL_OUTPUT_TRANSFORM_NORMAL, 1);
 
 	output->base.make = "waywayland";
 	output->base.model = "none";
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index ea0d4b9..58ccc16 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -59,6 +59,7 @@
 static char *output_name;
 static char *output_mode;
 static char *output_transform;
+static char *output_scale;
 static int option_width;
 static int option_height;
 static int option_count;
@@ -68,6 +69,7 @@ struct x11_configured_output {
 	char *name;
 	int width, height;
 	uint32_t transform;
+	unsigned int scale;
 	struct wl_list link;
 };
 
@@ -125,6 +127,7 @@ struct x11_output {
 	int			shm_id;
 	void		       *buf;
 	uint8_t			depth;
+	uint32_t                scale;
 };
 
 static struct xkb_keymap *
@@ -534,8 +537,12 @@ x11_output_wait_for_map(struct x11_compositor *c, struct x11_output *output)
 			configure_notify =
 				(xcb_configure_notify_event_t *) event;
 
-			output->mode.width = configure_notify->width;
-			output->mode.height = configure_notify->height;
+
+			if (configure_notify->width % output->scale != 0 ||
+			    configure_notify->height % output->scale != 0)
+				weston_log("Resolution is not a multiple of screen size, rounding\n");
+			output->mode.width = configure_notify->width / output->scale;
+			output->mode.height = configure_notify->height / output->scale;
 			configured = 1;
 			break;
 		}
@@ -677,7 +684,7 @@ static struct x11_output *
 x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 			     int width, int height, int fullscreen,
 			     int no_input, char *configured_name,
-			     uint32_t transform)
+			     uint32_t transform, uint32_t scale)
 {
 	static const char name[] = "Weston Compositor";
 	static const char class[] = "weston-1\0Weston Compositor";
@@ -686,6 +693,7 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 	xcb_screen_iterator_t iter;
 	struct wm_normal_hints normal_hints;
 	struct wl_event_loop *loop;
+	int output_width, output_height;
 	uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
 	xcb_atom_t atom_list[1];
 	uint32_t values[2] = {
@@ -694,6 +702,9 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 		0
 	};
 
+	output_width = width * scale;
+	output_height = height * scale;
+
 	if (configured_name)
 		sprintf(title, "%s - %s", name, configured_name);
 	else
@@ -719,9 +730,13 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 
 	output->mode.flags =
 		WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+	if (output->scale != 1)
+		output->mode.flags |= WL_OUTPUT_MODE_SCALED;
+
 	output->mode.width = width;
 	output->mode.height = height;
 	output->mode.refresh = 60000;
+	output->scale = scale;
 	wl_list_init(&output->base.mode_list);
 	wl_list_insert(&output->base.mode_list, &output->mode.link);
 
@@ -733,7 +748,7 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 			  output->window,
 			  iter.data->root,
 			  0, 0,
-			  width, height,
+			  output_width, output_height,
 			  0,
 			  XCB_WINDOW_CLASS_INPUT_OUTPUT,
 			  iter.data->root_visual,
@@ -751,10 +766,10 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 		memset(&normal_hints, 0, sizeof normal_hints);
 		normal_hints.flags =
 			WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
-		normal_hints.min_width = width;
-		normal_hints.min_height = height;
-		normal_hints.max_width = width;
-		normal_hints.max_height = height;
+		normal_hints.min_width = output_width;
+		normal_hints.min_height = output_height;
+		normal_hints.max_width = output_width;
+		normal_hints.max_height = output_height;
 		xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
 				    c->atom.wm_normal_hints,
 				    c->atom.wm_size_hints, 32,
@@ -794,10 +809,10 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 	output->base.make = "xwayland";
 	output->base.model = "none";
 	weston_output_init(&output->base, &c->base,
-			   x, y, width, height, transform);
+			   x, y, width, height, transform, scale);
 
 	if (c->use_pixman) {
-		if (x11_output_init_shm(c, output, width, height) < 0)
+		if (x11_output_init_shm(c, output, output_width, output_height) < 0)
 			return NULL;
 		if (pixman_renderer_output_create(&output->base) < 0) {
 			x11_output_deinit_shm(c, output);
@@ -960,8 +975,8 @@ x11_output_transform_coordinate(struct x11_output *x11_output,
 {
 	struct weston_output *output = &x11_output->base;
 	wl_fixed_t tx, ty;
-	wl_fixed_t width = wl_fixed_from_int(output->width - 1);
-	wl_fixed_t height = wl_fixed_from_int(output->height - 1);
+	wl_fixed_t width = wl_fixed_from_int(output->width * output->scale - 1);
+	wl_fixed_t height = wl_fixed_from_int(output->height * output->scale - 1);
 
 	switch(output->transform) {
 	case WL_OUTPUT_TRANSFORM_NORMAL:
@@ -999,6 +1014,9 @@ x11_output_transform_coordinate(struct x11_output *x11_output,
 		break;
 	}
 
+	tx /= output->scale;
+	ty /= output->scale;
+
 	tx += wl_fixed_from_int(output->x);
 	ty += wl_fixed_from_int(output->y);
 
@@ -1457,7 +1475,7 @@ x11_compositor_create(struct wl_display *display,
 						      option_height ? height :
 						      o->height,
 						      fullscreen, no_input,
-						      o->name, o->transform);
+						      o->name, o->transform, o->scale);
 		if (output == NULL)
 			goto err_x11_input;
 
@@ -1471,7 +1489,7 @@ x11_compositor_create(struct wl_display *display,
 	for (i = output_count; i < count; i++) {
 		output = x11_compositor_create_output(c, x, 0, width, height,
 						      fullscreen, no_input, NULL,
-						      WL_OUTPUT_TRANSFORM_NORMAL);
+						      WL_OUTPUT_TRANSFORM_NORMAL, wl_fixed_from_int(1));
 		if (output == NULL)
 			goto err_x11_input;
 		x = pixman_region32_extents(&output->base.region)->x2;
@@ -1536,7 +1554,7 @@ output_section_done(void *data)
 	output = malloc(sizeof *output);
 
 	if (!output || !output_name || (output_name[0] != 'X') ||
-				(!output_mode && !output_transform)) {
+				(!output_mode && !output_transform && !output_scale)) {
 		if (output_name)
 			free(output_name);
 		output_name = NULL;
@@ -1559,6 +1577,16 @@ output_section_done(void *data)
 		output->height = 640;
 	}
 
+	output->scale = 1;
+	if (output_scale) {
+		if (sscanf(output_scale, "%d", &output->scale) != 1) {
+			weston_log("Invalid scale \"%s\" for output %s\n",
+				   output_scale, output_name);
+			x11_free_configured_output(output);
+			goto err_free;
+		}
+	}
+
 	x11_output_set_transform(output);
 
 	wl_list_insert(configured_output_list.prev, &output->link);
@@ -1570,6 +1598,7 @@ err_free:
 		free(output_transform);
 	output_mode = NULL;
 	output_transform = NULL;
+	output_scale = NULL;
 }
 
 WL_EXPORT struct weston_compositor *
@@ -1597,6 +1626,7 @@ backend_init(struct wl_display *display, int *argc, char *argv[],
 		{ "name", CONFIG_KEY_STRING, &output_name },
 		{ "mode", CONFIG_KEY_STRING, &output_mode },
 		{ "transform", CONFIG_KEY_STRING, &output_transform },
+		{ "scale", CONFIG_KEY_STRING, &output_scale },
 	};
 
 	const struct config_section config_section[] = {
diff --git a/src/compositor.c b/src/compositor.c
index f67028e..99fff6d 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -93,6 +93,8 @@ sigchld_handler(int signal_number, void *data)
 
 static void
 weston_output_transform_init(struct weston_output *output, uint32_t transform);
+static void
+weston_output_scale_init(struct weston_output *output, uint32_t scale);
 
 WL_EXPORT int
 weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode)
@@ -113,6 +115,7 @@ weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode
 
 	/* Update output region and transformation matrix */
 	weston_output_transform_init(output, output->transform);
+	weston_output_scale_init(output, output->scale);
 
 	pixman_region32_init(&output->previous_damage);
 	pixman_region32_init_rect(&output->region, output->x, output->y,
@@ -291,7 +294,9 @@ weston_surface_create(struct weston_compositor *compositor)
 	}
 
 	surface->buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	surface->buffer_scale = 1;
 	surface->pending.buffer_transform = surface->buffer_transform;
+	surface->pending.buffer_scale = surface->buffer_scale;
 	surface->output = NULL;
 	surface->plane = &compositor->primary_plane;
 	surface->pending.newly_attached = 0;
@@ -360,6 +365,7 @@ weston_surface_to_global_float(struct weston_surface *surface,
 WL_EXPORT void
 weston_transformed_coord(int width, int height,
 			 enum wl_output_transform transform,
+			 uint32_t scale,
 			 float sx, float sy, float *bx, float *by)
 {
 	switch (transform) {
@@ -397,20 +403,24 @@ weston_transformed_coord(int width, int height,
 		*by = sx;
 		break;
 	}
+
+	*bx *= scale;
+	*by *= scale;
 }
 
 WL_EXPORT pixman_box32_t
 weston_transformed_rect(int width, int height,
 			enum wl_output_transform transform,
+			uint32_t scale,
 			pixman_box32_t rect)
 {
 	float x1, x2, y1, y2;
 
 	pixman_box32_t ret;
 
-	weston_transformed_coord(width, height, transform,
+	weston_transformed_coord(width, height, transform, scale,
 				 rect.x1, rect.y1, &x1, &y1);
-	weston_transformed_coord(width, height, transform,
+	weston_transformed_coord(width, height, transform, scale,
 				 rect.x2, rect.y2, &x2, &y2);
 
 	if (x1 <= x2) {
@@ -439,16 +449,34 @@ weston_surface_to_buffer_float(struct weston_surface *surface,
 	weston_transformed_coord(surface->geometry.width,
 				 surface->geometry.height,
 				 surface->buffer_transform,
+				 surface->buffer_scale,
 				 sx, sy, bx, by);
 }
 
+WL_EXPORT void
+weston_surface_to_buffer(struct weston_surface *surface,
+			 int sx, int sy, int *bx, int *by)
+{
+	float bxf, byf;
+
+	weston_transformed_coord(surface->geometry.width,
+				 surface->geometry.height,
+				 surface->buffer_transform,
+				 surface->buffer_scale,
+				 sx, sy, &bxf, &byf);
+	*bx = floorf(bxf);
+	*by = floorf(byf);
+}
+
 WL_EXPORT pixman_box32_t
 weston_surface_to_buffer_rect(struct weston_surface *surface,
 			      pixman_box32_t rect)
 {
 	return weston_transformed_rect(surface->geometry.width,
 				       surface->geometry.height,
-				       surface->buffer_transform, rect);
+				       surface->buffer_transform,
+				       surface->buffer_scale,
+				       rect);
 }
 
 WL_EXPORT void
@@ -877,29 +905,37 @@ weston_surface_is_mapped(struct weston_surface *surface)
 WL_EXPORT int32_t
 weston_surface_buffer_width(struct weston_surface *surface)
 {
+	int32_t width;
 	switch (surface->buffer_transform) {
 	case WL_OUTPUT_TRANSFORM_90:
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer_ref.buffer->height;
+		width = surface->buffer_ref.buffer->height;
+                break;
 	default:
-		return surface->buffer_ref.buffer->width;
+		width = surface->buffer_ref.buffer->width;
+                break;
 	}
+	return width / surface->buffer_scale;
 }
 
 WL_EXPORT int32_t
 weston_surface_buffer_height(struct weston_surface *surface)
 {
+	int32_t height;
 	switch (surface->buffer_transform) {
 	case WL_OUTPUT_TRANSFORM_90:
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer_ref.buffer->width;
+		height = surface->buffer_ref.buffer->width;
+                break;
 	default:
-		return surface->buffer_ref.buffer->height;
+		height = surface->buffer_ref.buffer->height;
+                break;
 	}
+	return height / surface->buffer_scale;
 }
 
 WL_EXPORT uint32_t
@@ -1485,25 +1521,28 @@ static void
 weston_surface_commit(struct weston_surface *surface)
 {
 	pixman_region32_t opaque;
-	int buffer_width = 0;
-	int buffer_height = 0;
+	int surface_width = 0;
+	int surface_height = 0;
 
-	/* wl_surface.set_buffer_rotation */
+	/* wl_surface.set_buffer_transform */
 	surface->buffer_transform = surface->pending.buffer_transform;
 
+	/* wl_surface.set_buffer_scale */
+	surface->buffer_scale = surface->pending.buffer_scale;
+
 	/* wl_surface.attach */
 	if (surface->pending.buffer || surface->pending.newly_attached)
 		weston_surface_attach(surface, surface->pending.buffer);
 
 	if (surface->buffer_ref.buffer) {
-		buffer_width = weston_surface_buffer_width(surface);
-		buffer_height = weston_surface_buffer_height(surface);
+		surface_width = weston_surface_buffer_width(surface);
+		surface_height = weston_surface_buffer_height(surface);
 	}
 
 	if (surface->configure && surface->pending.newly_attached)
 		surface->configure(surface,
 				   surface->pending.sx, surface->pending.sy,
-				   buffer_width, buffer_height);
+				   surface_width, surface_height);
 
 	if (surface->pending.buffer)
 		wl_list_remove(&surface->pending.buffer_destroy_listener.link);
@@ -1588,6 +1627,16 @@ surface_set_buffer_transform(struct wl_client *client,
 	surface->pending.buffer_transform = transform;
 }
 
+static void
+surface_set_buffer_scale(struct wl_client *client,
+			 struct wl_resource *resource,
+			 uint32_t scale)
+{
+	struct weston_surface *surface = resource->data;
+
+	surface->pending.buffer_scale = scale;
+}
+
 static const struct wl_surface_interface surface_interface = {
 	surface_destroy,
 	surface_attach,
@@ -1596,7 +1645,8 @@ static const struct wl_surface_interface surface_interface = {
 	surface_set_opaque_region,
 	surface_set_input_region,
 	surface_commit,
-	surface_set_buffer_transform
+	surface_set_buffer_transform,
+	surface_set_buffer_scale
 };
 
 static void
@@ -1702,25 +1752,28 @@ weston_subsurface_commit_from_cache(struct weston_subsurface *sub)
 {
 	struct weston_surface *surface = sub->surface;
 	pixman_region32_t opaque;
-	int buffer_width = 0;
-	int buffer_height = 0;
+	int surface_width = 0;
+	int surface_height = 0;
 
-	/* wl_surface.set_buffer_rotation */
+	/* wl_surface.set_buffer_transform */
 	surface->buffer_transform = sub->cached.buffer_transform;
 
+	/* wl_surface.set_buffer_scale */
+	surface->buffer_scale = sub->cached.buffer_scale;
+
 	/* wl_surface.attach */
 	if (sub->cached.buffer_ref.buffer || sub->cached.newly_attached)
 		weston_surface_attach(surface, sub->cached.buffer_ref.buffer);
 	weston_buffer_reference(&sub->cached.buffer_ref, NULL);
 
 	if (surface->buffer_ref.buffer) {
-		buffer_width = weston_surface_buffer_width(surface);
-		buffer_height = weston_surface_buffer_height(surface);
+		surface_width = weston_surface_buffer_width(surface);
+		surface_height = weston_surface_buffer_height(surface);
 	}
 
 	if (surface->configure && sub->cached.newly_attached)
 		surface->configure(surface, sub->cached.sx, sub->cached.sy,
-				   buffer_width, buffer_height);
+				   surface_width, surface_height);
 	sub->cached.sx = 0;
 	sub->cached.sy = 0;
 	sub->cached.newly_attached = 0;
@@ -1797,6 +1850,7 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
 	surface->pending.newly_attached = 0;
 
 	sub->cached.buffer_transform = surface->pending.buffer_transform;
+	sub->cached.buffer_scale = surface->pending.buffer_scale;
 
 	pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
 
@@ -2466,6 +2520,9 @@ bind_output(struct wl_client *client,
 				output->subpixel,
 				output->make, output->model,
 				output->transform);
+	if (version >= 2)
+		wl_output_send_scale(resource,
+				     output->scale);
 
 	wl_list_for_each (mode, &output->mode_list, link) {
 		wl_output_send_mode(resource,
@@ -2474,6 +2531,9 @@ bind_output(struct wl_client *client,
 				    mode->height,
 				    mode->refresh);
 	}
+
+	if (version >= 2)
+		wl_output_send_done(resource);
 }
 
 WL_EXPORT void
@@ -2608,6 +2668,12 @@ weston_output_transform_init(struct weston_output *output, uint32_t transform)
 	}
 }
 
+static void
+weston_output_scale_init(struct weston_output *output, uint32_t scale)
+{
+        output->scale = scale;
+}
+
 WL_EXPORT void
 weston_output_move(struct weston_output *output, int x, int y)
 {
@@ -2622,7 +2688,8 @@ weston_output_move(struct weston_output *output, int x, int y)
 
 WL_EXPORT void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
-		   int x, int y, int width, int height, uint32_t transform)
+		   int x, int y, int width, int height, uint32_t transform,
+		   uint32_t scale)
 {
 	output->compositor = c;
 	output->x = x;
@@ -2636,6 +2703,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 	output->dirty = 1;
 
 	weston_output_transform_init(output, transform);
+	weston_output_scale_init(output, scale);
 	weston_output_init_zoom(output);
 
 	weston_output_move(output, x, y);
diff --git a/src/compositor.h b/src/compositor.h
index 318fc0d..9a16ab6 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -183,7 +183,8 @@ struct weston_output {
 	char *make, *model, *serial_number;
 	uint32_t subpixel;
 	uint32_t transform;
-	
+	uint32_t scale;
+
 	struct weston_mode *current;
 	struct weston_mode *origin;
 	struct wl_list mode_list;
@@ -601,6 +602,9 @@ struct weston_subsurface {
 
 		/* wl_surface.set_buffer_transform */
 		uint32_t buffer_transform;
+
+		/* wl_surface.set_buffer_scale */
+		uint32_t buffer_scale;
 	} cached;
 
 	int synchronized;
@@ -704,6 +708,7 @@ struct weston_surface {
 
 	struct weston_buffer_reference buffer_ref;
 	uint32_t buffer_transform;
+	uint32_t buffer_scale;
 	int keep_buffer; /* bool for backends to prevent early release */
 
 	/* All the pending state, that wl_surface.commit will apply. */
@@ -729,6 +734,9 @@ struct weston_surface {
 
 		/* wl_surface.set_buffer_transform */
 		uint32_t buffer_transform;
+
+		/* wl_surface.set_scaling_factor */
+		uint32_t buffer_scale;
 	} pending;
 
 	/*
@@ -787,6 +795,10 @@ weston_surface_buffer_height(struct weston_surface *surface);
 WL_EXPORT void
 weston_surface_to_buffer_float(struct weston_surface *surface,
 			       float x, float y, float *bx, float *by);
+WL_EXPORT void
+weston_surface_to_buffer(struct weston_surface *surface,
+                         int sx, int sy, int *bx, int *by);
+
 pixman_box32_t
 weston_surface_to_buffer_rect(struct weston_surface *surface,
 			      pixman_box32_t rect);
@@ -1005,7 +1017,7 @@ void
 weston_output_move(struct weston_output *output, int x, int y);
 void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
-		   int x, int y, int width, int height, uint32_t transform);
+		   int x, int y, int width, int height, uint32_t transform, uint32_t scale);
 void
 weston_output_destroy(struct weston_output *output);
 
@@ -1136,10 +1148,12 @@ module_init(struct weston_compositor *compositor,
 void
 weston_transformed_coord(int width, int height,
 			 enum wl_output_transform transform,
+			 uint32_t scale,
 			 float sx, float sy, float *bx, float *by);
 pixman_box32_t
 weston_transformed_rect(int width, int height,
 			enum wl_output_transform transform,
+			uint32_t scale,
 			pixman_box32_t rect);
 
 #ifdef  __cplusplus
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index be74eba..52d15e0 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -68,6 +68,7 @@ struct gl_surface_state {
 
 	struct weston_buffer_reference buffer_ref;
 	int pitch; /* in pixels */
+	int height; /* in pixels */
 };
 
 struct gl_renderer {
@@ -552,17 +553,7 @@ texture_region(struct weston_surface *es, pixman_region32_t *region,
 	vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt);
 
 	inv_width = 1.0 / gs->pitch;
-
-	switch (es->buffer_transform) {
-	case WL_OUTPUT_TRANSFORM_90:
-	case WL_OUTPUT_TRANSFORM_270:
-	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
-	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		inv_height = 1.0 / es->geometry.width;
-		break;
-	default:
-		inv_height = 1.0 / es->geometry.height;
-	}
+        inv_height = 1.0 / gs->height;
 
 	for (i = 0; i < nrects; i++) {
 		pixman_box32_t *rect = &rects[i];
@@ -791,7 +782,7 @@ draw_surface(struct weston_surface *es, struct weston_output *output,
 	use_shader(gr, gs->shader);
 	shader_uniforms(gs->shader, es, output);
 
-	if (es->transform.enabled || output->zoom.active)
+	if (es->transform.enabled || output->zoom.active || output->scale != es->buffer_scale)
 		filter = GL_LINEAR;
 	else
 		filter = GL_NEAREST;
@@ -1023,9 +1014,9 @@ gl_renderer_repaint_output(struct weston_output *output,
 	int32_t width, height;
 	pixman_region32_t buffer_damage, total_damage;
 
-	width = output->current->width +
+	width = output->current->width * output->scale +
 		output->border.left + output->border.right;
-	height = output->current->height +
+	height = output->current->height * output->scale +
 		output->border.top + output->border.bottom;
 
 	glViewport(0, 0, width, height);
@@ -1214,6 +1205,7 @@ gl_renderer_attach(struct weston_surface *es, struct wl_buffer *buffer)
 
 	if (wl_buffer_is_shm(buffer)) {
 		gs->pitch = wl_shm_buffer_get_stride(buffer) / 4;
+		gs->height = wl_shm_buffer_get_height(buffer);
 		gs->target = GL_TEXTURE_2D;
 
 		ensure_textures(gs, 1);
@@ -1279,6 +1271,7 @@ gl_renderer_attach(struct weston_surface *es, struct wl_buffer *buffer)
 		}
 
 		gs->pitch = buffer->width;
+		gs->height = buffer->height;
 	} else {
 		weston_log("unhandled buffer type!\n");
 		weston_buffer_reference(&gs->buffer_ref, NULL);
diff --git a/src/pixman-renderer.c b/src/pixman-renderer.c
index 92c6bb3..b1bbcd6 100644
--- a/src/pixman-renderer.c
+++ b/src/pixman-renderer.c
@@ -105,6 +105,36 @@ pixman_renderer_read_pixels(struct weston_output *output,
 }
 
 static void
+box_scale(pixman_box32_t *dst, int scale)
+{
+	dst->x1 *= scale;
+	dst->x2 *= scale;
+	dst->y1 *= scale;
+	dst->y2 *= scale;
+}
+
+static void
+scale_region (pixman_region32_t *region, int scale)
+{
+	pixman_box32_t *rects, *scaled_rects;
+	int nrects, i;
+
+	if (scale != 1)	{
+		rects = pixman_region32_rectangles(region, &nrects);
+		scaled_rects = calloc(nrects, sizeof(pixman_box32_t));
+
+		for (i = 0; i < nrects; i++) {
+			scaled_rects[i] = rects[i];
+			box_scale(&scaled_rects[i], scale);
+		}
+		pixman_region32_clear(region);
+
+		pixman_region32_init_rects (region, scaled_rects, nrects);
+		free (scaled_rects);
+	}
+}
+
+static void
 transform_region (pixman_region32_t *region, int width, int height, enum wl_output_transform transform)
 {
 	pixman_box32_t *rects, *transformed_rects;
@@ -180,6 +210,7 @@ region_global_to_output(struct weston_output *output, pixman_region32_t *region)
 {
 	pixman_region32_translate(region, -output->x, -output->y);
 	transform_region (region, output->width, output->height, output->transform);
+	scale_region (region, output->scale);
 }
 
 #define D2F(v) pixman_double_to_fixed((double)v)
@@ -229,9 +260,12 @@ repaint_region(struct weston_surface *es, struct weston_output *output,
 	pixman_image_set_clip_region32 (po->shadow_image, &final_region);
 
 	/* Set up the source transformation based on the surface
-	   position, the output position/transform and the client
-	   specified buffer transform */
+	   position, the output position/transform/scale and the client
+	   specified buffer transform/scale */
 	pixman_transform_init_identity(&transform);
+	pixman_transform_scale(&transform, NULL,
+			       pixman_double_to_fixed ((double)1.0/output->scale),
+			       pixman_double_to_fixed ((double)1.0/output->scale));
 
 	fw = pixman_int_to_fixed(output->width);
 	fh = pixman_int_to_fixed(output->height);
@@ -338,9 +372,13 @@ repaint_region(struct weston_surface *es, struct weston_output *output,
 		break;
 	}
 
+	pixman_transform_scale(&transform, NULL,
+			       pixman_double_to_fixed ((double)es->buffer_scale),
+			       pixman_double_to_fixed ((double)es->buffer_scale));
+
 	pixman_image_set_transform(ps->image, &transform);
 
-	if (es->transform.enabled)
+	if (es->transform.enabled || output->scale != es->buffer_scale)
 		pixman_image_set_filter(ps->image, PIXMAN_FILTER_BILINEAR, NULL, 0);
 	else
 		pixman_image_set_filter(ps->image, PIXMAN_FILTER_NEAREST, NULL, 0);
@@ -651,8 +689,8 @@ pixman_renderer_output_create(struct weston_output *output)
 		return -1;
 
 	/* set shadow image transformation */
-	w = output->current->width;
-	h = output->current->height;
+	w = output->current->width * output->scale;
+	h = output->current->height * output->scale;
 
 	po->shadow_buffer = malloc(w * h * 4);
 
-- 
1.8.1.4



More information about the wayland-devel mailing list