diff options
| -rw-r--r-- | include/wlr/types/wlr_surface.h | 18 | ||||
| -rw-r--r-- | types/wlr_surface.c | 86 | 
2 files changed, 95 insertions, 9 deletions
| diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index fa9fa6d5..250d28dd 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -14,6 +14,7 @@  #include <stdint.h>  #include <time.h>  #include <wayland-server-core.h> +#include <wlr/types/wlr_box.h>  #include <wlr/types/wlr_output.h>  enum wlr_surface_state_field { @@ -25,6 +26,7 @@ enum wlr_surface_state_field {  	WLR_SURFACE_STATE_TRANSFORM = 1 << 5,  	WLR_SURFACE_STATE_SCALE = 1 << 6,  	WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, +	WLR_SURFACE_STATE_VIEWPORT = 1 << 8,  };  struct wlr_surface_state { @@ -41,6 +43,21 @@ struct wlr_surface_state {  	int width, height; // in surface-local coordinates  	int buffer_width, buffer_height; +	/** +	 * The viewport is applied after the surface transform and scale. +	 * +	 * If has_src is true, the surface content is cropped to the provided +	 * rectangle. If has_dst is true, the surface content is scaled to the +	 * provided rectangle. +	 */ +	struct { +		bool has_src, has_dst; +		// In coordinates after scale/transform are applied, but before the +		// destination rectangle is applied +		struct wlr_fbox src; +		int dst_width, dst_height; // in surface-local coordinates +	} viewport; +  	struct wl_listener buffer_destroy;  }; @@ -220,7 +237,6 @@ void wlr_surface_send_leave(struct wlr_surface *surface,  void wlr_surface_send_frame_done(struct wlr_surface *surface,  		const struct timespec *when); -struct wlr_box;  /**   * Get the bounding box that contains the surface and all subsurfaces in   * surface coordinates. diff --git a/types/wlr_surface.c b/types/wlr_surface.c index d741d119..ffaa5223 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -139,6 +139,30 @@ static void surface_set_input_region(struct wl_client *client,  	}  } +/** + * Computes the surface viewport source size, ie. the size after applying the + * surface's scale, transform and cropping (via the viewport's source + * rectangle) but before applying the viewport scaling (via the viewport's + * destination rectangle). + */ +static void surface_state_viewport_src_size(struct wlr_surface_state *state, +		int *out_width, int *out_height) { +	if (state->viewport.has_src) { +		*out_width = state->viewport.src.width; +		*out_height = state->viewport.src.height; +	} else { +		int width = state->buffer_width / state->scale; +		int height = state->buffer_height / state->scale; +		if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { +			int tmp = width; +			width = height; +			height = tmp; +		} +		*out_width = width; +		*out_height = height; +	} +} +  static void surface_state_finalize(struct wlr_surface *surface,  		struct wlr_surface_state *state) {  	if ((state->committed & WLR_SURFACE_STATE_BUFFER)) { @@ -150,15 +174,17 @@ static void surface_state_finalize(struct wlr_surface *surface,  		}  	} -	int width = state->buffer_width / state->scale; -	int height = state->buffer_height / state->scale; -	if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { -		int tmp = width; -		width = height; -		height = tmp; +	if (state->buffer_resource != NULL) { +		if (state->viewport.has_dst) { +			state->width = state->viewport.dst_width; +			state->height = state->viewport.dst_height; +		} else { +			surface_state_viewport_src_size(state, +				&state->width, &state->height); +		} +	} else { +		state->width = state->height = 0;  	} -	state->width = width; -	state->height = height;  	pixman_region32_intersect_rect(&state->surface_damage,  		&state->surface_damage, 0, 0, state->width, state->height); @@ -183,6 +209,22 @@ static void surface_update_damage(pixman_region32_t *buffer_damage,  		pixman_region32_init(&surface_damage);  		pixman_region32_copy(&surface_damage, &pending->surface_damage); + +		if (pending->viewport.has_dst) { +			int src_width, src_height; +			surface_state_viewport_src_size(pending, &src_width, &src_height); +			float scale_x = (float)pending->viewport.dst_width / src_width; +			float scale_y = (float)pending->viewport.dst_height / src_height; +			wlr_region_scale_xy(&surface_damage, &surface_damage, +				1.0 / scale_x, 1.0 / scale_y); +		} +		if (pending->viewport.has_src) { +			// This is lossy: do a best-effort conversion +			pixman_region32_translate(&surface_damage, +				floor(pending->viewport.src.x), +				floor(pending->viewport.src.y)); +		} +  		wlr_region_transform(&surface_damage, &surface_damage,  			wlr_output_transform_invert(pending->transform),  			pending->width, pending->height); @@ -230,6 +272,9 @@ static void surface_state_copy(struct wlr_surface_state *state,  	if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) {  		pixman_region32_copy(&state->input, &next->input);  	} +	if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { +		memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); +	}  	state->committed |= next->committed;  } @@ -1125,6 +1170,13 @@ void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) {  	box->height = acc.max_y - acc.min_y;  } +static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, +		const struct wlr_box *box) { +	pixman_region32_intersect_rect(dst, src, +		box->x, box->y, box->width, box->height); +	pixman_region32_translate(dst, -box->x, -box->y); +} +  void wlr_surface_get_effective_damage(struct wlr_surface *surface,  		pixman_region32_t *damage) {  	pixman_region32_clear(damage); @@ -1135,6 +1187,24 @@ void wlr_surface_get_effective_damage(struct wlr_surface *surface,  		surface->current.buffer_height);  	wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); +	if (surface->current.viewport.has_src) { +		struct wlr_box src_box = { +			.x = floor(surface->current.viewport.src.x), +			.y = floor(surface->current.viewport.src.y), +			.width = ceil(surface->current.viewport.src.width), +			.height = ceil(surface->current.viewport.src.height), +		}; +		crop_region(damage, damage, &src_box); +	} +	if (surface->current.viewport.has_dst) { +		int src_width, src_height; +		surface_state_viewport_src_size(&surface->current, +			&src_width, &src_height); +		float scale_x = (float)surface->current.viewport.dst_width / src_width; +		float scale_y = (float)surface->current.viewport.dst_height / src_height; +		wlr_region_scale_xy(damage, damage, scale_x, scale_y); +	} +  	// On resize, damage the previous bounds of the surface. The current bounds  	// have already been damaged in surface_update_damage.  	if (surface->previous.width > surface->current.width || | 
