commit ee6d5a8

shrub  ·  2026-01-20 22:31:55 +0000 UTC
parent 12d8c40
zoom!
swc_set_zoom and get_zoom api added
zoom does a fullscreen damage and renders a zoomed scene into a shm
buffer, with pixman
2 files changed,  +254, -8
+241, -8
  1@@ -108,6 +108,10 @@ static struct {
  2 
  3 	bool updating;
  4 	struct wl_global *global;
  5+
  6+	/* zoom level (1.0 = normal, >1 = zoomed in, <1 = zoomed out) */
  7+	float zoom;
  8+	struct wld_buffer *zoom_buffer;
  9 } compositor;
 10 
 11 static struct {
 12@@ -456,6 +460,17 @@ schedule_updates(uint32_t screens)
 13 			screens |= screen_mask(screen);
 14 	}
 15 
 16+	/* when zoomed, force full screen damage since actual area differs from world coords */
 17+	if (compositor.zoom != 1.0f) {
 18+		struct screen *screen;
 19+		wl_list_for_each (screen, &swc.screens, link) {
 20+			pixman_region32_union_rect(&compositor.damage, &compositor.damage,
 21+				screen->base.geometry.x, screen->base.geometry.y,
 22+				screen->base.geometry.width, screen->base.geometry.height);
 23+			screens |= screen_mask(screen);
 24+		}
 25+	}
 26+
 27 	compositor.scheduled_updates |= screens;
 28 }
 29 
 30@@ -503,6 +518,202 @@ swc_overlay_clear(void)
 31 	schedule_updates(-1);
 32 }
 33 
 34+EXPORT void
 35+swc_set_zoom(float level)
 36+{
 37+	if (level < 0.1f)
 38+		level = 0.1f;
 39+	if (level > 10.0f)
 40+		level = 10.0f;
 41+
 42+	if (compositor.zoom != level) {
 43+		compositor.zoom = level;
 44+		/* damage entire screen to force full repaint */
 45+		schedule_updates(-1);
 46+	}
 47+}
 48+
 49+EXPORT float
 50+swc_get_zoom(void)
 51+{
 52+	return compositor.zoom;
 53+}
 54+
 55+static pixman_format_code_t
 56+wld_to_pixman_format(enum wld_format format)
 57+{
 58+	switch (format) {
 59+	case WLD_FORMAT_XRGB8888:
 60+		return PIXMAN_x8r8g8b8;
 61+	case WLD_FORMAT_ARGB8888:
 62+		return PIXMAN_a8r8g8b8;
 63+	default:
 64+		return PIXMAN_x8r8g8b8;
 65+	}
 66+}
 67+
 68+/* render zoomed view to shm      wallpaper unscaled, windows scaled */
 69+static struct wld_buffer *
 70+render_zoomed_to_shm(struct screen *screen, float zoom)
 71+{
 72+	uint32_t width = screen->base.geometry.width;
 73+	uint32_t height = screen->base.geometry.height;
 74+	int32_t screen_x = screen->base.geometry.x;
 75+	int32_t screen_y = screen->base.geometry.y;
 76+	int32_t cx = screen_x + width / 2;
 77+	int32_t cy = screen_y + height / 2;
 78+	struct compositor_view *view;
 79+
 80+	struct wld_buffer *buffer = wld_create_buffer(swc.shm->context,
 81+		width, height, WLD_FORMAT_XRGB8888, WLD_FLAG_MAP);
 82+	if (!buffer)
 83+		return NULL;
 84+
 85+	if (!wld_set_target_buffer(swc.shm->renderer, buffer)) {
 86+		wld_buffer_unreference(buffer);
 87+		return NULL;
 88+	}
 89+
 90+	pixman_region32_t full;
 91+	pixman_region32_init_rect(&full, 0, 0, width, height);
 92+	if (wallbuf)
 93+		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &full);
 94+	else
 95+		wld_fill_region(swc.shm->renderer, bgcolor, &full);
 96+	pixman_region32_fini(&full);
 97+	wld_flush(swc.shm->renderer);
 98+
 99+	if (!wld_map(buffer)) {
100+		wld_buffer_unreference(buffer);
101+		return NULL;
102+	}
103+
104+	pixman_image_t *dst_img = pixman_image_create_bits(
105+		wld_to_pixman_format(buffer->format),
106+		buffer->width, buffer->height,
107+		buffer->map, buffer->pitch);
108+
109+	if (!dst_img) {
110+		wld_unmap(buffer);
111+		wld_buffer_unreference(buffer);
112+		return NULL;
113+	}
114+
115+	/* render each view with scaling */
116+	wl_list_for_each_reverse(view, &compositor.views, link) {
117+		struct wld_buffer *src = view->buffer;
118+		const struct swc_rectangle *geom = &view->base.geometry;
119+
120+		if (!src)
121+			continue;
122+
123+		if (!(wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ))
124+			src = view->base.buffer;
125+		if (!src)
126+			continue;
127+
128+		/* maths     zoom position and size */
129+		float zoomed_x = (geom->x - cx) * zoom + width / 2.0f;
130+		float zoomed_y = (geom->y - cy) * zoom + height / 2.0f;
131+		float zoomed_w = geom->width * zoom;
132+		float zoomed_h = geom->height * zoom;
133+
134+		float border_out = view->border.outwidth * zoom;
135+		float border_in = view->border.inwidth * zoom;
136+		float total_border = border_out + border_in;
137+
138+		if (zoomed_x + zoomed_w + total_border < 0 || zoomed_x - total_border >= (int32_t)width ||
139+		    zoomed_y + zoomed_h + total_border < 0 || zoomed_y - total_border >= (int32_t)height)
140+			continue;
141+
142+		if (view->border.outwidth > 0 && border_out >= 1) {
143+			int32_t bx = (int32_t)(zoomed_x - total_border);
144+			int32_t by = (int32_t)(zoomed_y - total_border);
145+			int32_t bw = (int32_t)(zoomed_w + 2 * total_border);
146+			int32_t bh = (int32_t)(zoomed_h + 2 * total_border);
147+			int32_t bo = (int32_t)border_out;
148+
149+			pixman_color_t color = {
150+				.red = ((view->border.outcolor >> 16) & 0xff) * 257,
151+				.green = ((view->border.outcolor >> 8) & 0xff) * 257,
152+				.blue = (view->border.outcolor & 0xff) * 257,
153+				.alpha = 0xffff
154+			};
155+			pixman_image_t *fill = pixman_image_create_solid_fill(&color);
156+			if (fill) {
157+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
158+					0, 0, 0, 0, bx, by, bw, bo);
159+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
160+					0, 0, 0, 0, bx, by + bh - bo, bw, bo);
161+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
162+					0, 0, 0, 0, bx, by + bo, bo, bh - 2 * bo);
163+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
164+					0, 0, 0, 0, bx + bw - bo, by + bo, bo, bh - 2 * bo);
165+				pixman_image_unref(fill);
166+			}
167+		}
168+
169+		if (view->border.inwidth > 0 && border_in >= 1) {
170+			int32_t bx = (int32_t)(zoomed_x - border_in);
171+			int32_t by = (int32_t)(zoomed_y - border_in);
172+			int32_t bw = (int32_t)(zoomed_w + 2 * border_in);
173+			int32_t bh = (int32_t)(zoomed_h + 2 * border_in);
174+			int32_t bi = (int32_t)border_in;
175+
176+			pixman_color_t color = {
177+				.red = ((view->border.incolor >> 16) & 0xff) * 257,
178+				.green = ((view->border.incolor >> 8) & 0xff) * 257,
179+				.blue = (view->border.incolor & 0xff) * 257,
180+				.alpha = 0xffff
181+			};
182+			pixman_image_t *fill = pixman_image_create_solid_fill(&color);
183+			if (fill) {
184+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
185+					0, 0, 0, 0, bx, by, bw, bi);
186+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
187+					0, 0, 0, 0, bx, by + bh - bi, bw, bi);
188+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
189+					0, 0, 0, 0, bx, by + bi, bi, bh - 2 * bi);
190+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
191+					0, 0, 0, 0, bx + bw - bi, by + bi, bi, bh - 2 * bi);
192+				pixman_image_unref(fill);
193+			}
194+		}
195+
196+		if (!wld_map(src))
197+			continue;
198+
199+		pixman_image_t *src_img = pixman_image_create_bits(
200+			wld_to_pixman_format(src->format),
201+			src->width, src->height,
202+			src->map, src->pitch);
203+
204+		if (src_img) {
205+			pixman_transform_t transform;
206+			pixman_transform_init_identity(&transform);
207+			pixman_fixed_t scale = pixman_double_to_fixed(1.0 / zoom);
208+			pixman_transform_scale(&transform, NULL, scale, scale);
209+			pixman_image_set_transform(src_img, &transform);
210+			pixman_image_set_filter(src_img, PIXMAN_FILTER_BILINEAR, NULL, 0);
211+
212+			pixman_image_composite32(PIXMAN_OP_OVER,
213+				src_img, NULL, dst_img,
214+				0, 0, 0, 0,
215+				(int32_t)zoomed_x, (int32_t)zoomed_y,
216+				(int32_t)(zoomed_w + 1), (int32_t)(zoomed_h + 1));
217+
218+			pixman_image_unref(src_img);
219+		}
220+
221+		wld_unmap(src);
222+	}
223+
224+	pixman_image_unref(dst_img);
225+	wld_unmap(buffer);
226+
227+	return buffer;
228+}
229+
230 static bool
231 update(struct view *base)
232 {
233@@ -996,14 +1207,32 @@ update_screen(struct screen *screen)
234 		return;
235 	}
236 
237-	pixman_region32_t base_damage;
238-	pixman_region32_copy(&damage, total_damage);
239-	pixman_region32_translate(&damage, geom->x, geom->y);
240-	pixman_region32_init(&base_damage);
241-	pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
242-	renderer_repaint(target, &damage, &base_damage, &compositor.views);
243-	pixman_region32_fini(&damage);
244-	pixman_region32_fini(&base_damage);
245+	/* check if zoom */
246+	if (compositor.zoom != 1.0f) {
247+		pixman_region32_fini(&damage);
248+
249+		struct wld_buffer *zoomed = render_zoomed_to_shm(screen, compositor.zoom);
250+		if (!zoomed)
251+			return;
252+
253+		pixman_region32_t full;
254+		pixman_region32_init_rect(&full, 0, 0, geom->width, geom->height);
255+		wld_set_target_surface(swc.drm->renderer, target->surface);
256+		wld_copy_region(swc.drm->renderer, zoomed, 0, 0, &full);
257+		wld_flush(swc.drm->renderer);
258+		pixman_region32_fini(&full);
259+
260+		wld_buffer_unreference(zoomed);
261+	} else {
262+		pixman_region32_t base_damage;
263+		pixman_region32_copy(&damage, total_damage);
264+		pixman_region32_translate(&damage, geom->x, geom->y);
265+		pixman_region32_init(&base_damage);
266+		pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
267+		renderer_repaint(target, &damage, &base_damage, &compositor.views);
268+		pixman_region32_fini(&damage);
269+		pixman_region32_fini(&base_damage);
270+	}
271 
272 	switch (target_swap_buffers(target)) {
273 	case -EACCES:
274@@ -1162,6 +1391,8 @@ compositor_initialize(void)
275 	compositor.scheduled_updates = 0;
276 	compositor.pending_flips = 0;
277 	compositor.updating = false;
278+	compositor.zoom = 1.0f;
279+	compositor.zoom_buffer = NULL;
280 	pixman_region32_init(&compositor.damage);
281 	pixman_region32_init(&compositor.opaque);
282 	wl_list_init(&compositor.views);
283@@ -1185,6 +1416,8 @@ compositor_initialize(void)
284 void
285 compositor_finalize(void)
286 {
287+	if (compositor.zoom_buffer)
288+		wld_buffer_unreference(compositor.zoom_buffer);
289 	pixman_region32_fini(&compositor.damage);
290 	pixman_region32_fini(&compositor.opaque);
291 	wl_global_destroy(compositor.global);
+13, -0
 1@@ -121,6 +121,19 @@ void swc_overlay_set_box(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_
 2  */
 3 void swc_overlay_clear(void);
 4 
 5+/**
 6+ * Set the compositor zoom level.
 7+ *
 8+ * 1.0 = normal, >1.0 = zoomed in, <1.0 = zoomed out
 9+ * Uses software (pixman) scaling.
10+ */
11+void swc_set_zoom(float level);
12+
13+/**
14+ * Get the current zoom level.
15+ */
16+float swc_get_zoom(void);
17+
18 /* Rectangles {{{ */
19 
20 struct swc_rectangle {