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 {