This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch devs/cedric/wl/all
in repository efl.
View the commit online.
commit 9996d3a06a9b708b65a787cfad7a95b39a3a2baa
Author: Cedric BAIL <[email protected]>
AuthorDate: Sat Aug 8 09:50:53 2026 -0600
evas/tests: pin the HiDPI buffer scale contract
A Wayland surface with wl_surface.set_buffer_scale = N attaches a buffer
N times larger than the surface it represents, and a compositor renders
that by sizing the canvas image object to the surface size while the
image keeps the buffer size.
That already works, and deliberately gets no new property. Everything a
buffer_scale property would do is present:
- the fill rectangle already scales the whole buffer into the object,
- evas_object_image_render_pre() already maps buffer damage to canvas
damage as (rr->x * w) / o->cur->image.w, dividing by the source
extent and multiplying by the destination extent, so it is
proportional with no scale factor to add,
- evas_object_image_orient_set() already covers all eight
wl_output_transform values, flips included,
- evas_object_image_size_get() keeps reporting the buffer size, which
is what a compositor wants: it needs buffer coordinates to turn
wl_surface.damage into evas_object_image_data_update_add().
A property whose only effect is to save the caller a division would not
earn new API, so pin the behaviour with a test instead. The buffer is
four solid quadrants, so the assertions are exact pixel comparisons at
exact positions with no tolerance and no dependence on how the scaler
filters.
Covers scales 1 through 4 -- scale 1 as the control, since anything the
scaled cases assert has to hold unscaled too or the test is measuring
something else -- that image_size_get() keeps reporting the buffer size,
and that all eight orientations still land correctly while the buffer is
being scaled down. The orientation cases assert six different quadrant
mappings, so they fail if the orientation is ever silently dropped.
Note for anyone extending this: damage granularity is not observable
through the buffer engine. A 64x64 buffer update on a 512x512 canvas
comes back as a single whole-canvas update rect at scale 1 exactly as at
scale 2, because the tiler coalesces. Asserting rendered pixels is the
reliable route here.
Gated on BUILD_ENGINE_BUFFER like evas_test_mask.c.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ke8zkumHXWeJHNDMzEUqsH
---
src/tests/evas/evas_suite.c | 1 +
src/tests/evas/evas_suite.h | 1 +
src/tests/evas/evas_test_buffer_scale.c | 189 ++++++++++++++++++++++++++++++++
src/tests/evas/meson.build | 1 +
4 files changed, 192 insertions(+)
diff --git a/src/tests/evas/evas_suite.c b/src/tests/evas/evas_suite.c
index 05faf08ff8..48044af03d 100644
--- a/src/tests/evas/evas_suite.c
+++ b/src/tests/evas/evas_suite.c
@@ -20,6 +20,7 @@ static const Efl_Test_Case etc[] = {
{ "Filters", evas_test_filters },
{ "Images", evas_test_image_object },
{ "Images", evas_test_image_object2 },
+ { "Buffer Scale", evas_test_image_buffer_scale },
{ "Masking", evas_test_mask },
{ "Evas GL", evas_test_evasgl },
{ "Object Smart", evas_test_object_smart },
diff --git a/src/tests/evas/evas_suite.h b/src/tests/evas/evas_suite.h
index 76a10a19b5..31d2b6aa1a 100644
--- a/src/tests/evas/evas_suite.h
+++ b/src/tests/evas/evas_suite.h
@@ -13,6 +13,7 @@ void evas_test_callbacks(TCase *tc);
void evas_test_render_engines(TCase *tc);
void evas_test_filters(TCase *tc);
void evas_test_image_object(TCase *tc);
+void evas_test_image_buffer_scale(TCase *tc);
void evas_test_image_object2(TCase *tc);
void evas_test_mask(TCase *tc);
void evas_test_evasgl(TCase *tc);
diff --git a/src/tests/evas/evas_test_buffer_scale.c b/src/tests/evas/evas_test_buffer_scale.c
new file mode 100644
index 0000000000..c246e10dce
--- /dev/null
+++ b/src/tests/evas/evas_test_buffer_scale.c
@@ -0,0 +1,189 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef BUILD_ENGINE_BUFFER
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <Eina.h>
+#include <Evas.h>
+#include <Ecore_Evas.h>
+
+#include "evas_suite.h"
+
+/* A Wayland surface with wl_surface.set_buffer_scale = N attaches a buffer N
+ * times larger than the surface it represents. A compositor renders that by
+ * sizing the canvas image object to the surface size while the image keeps the
+ * buffer size, so the whole buffer scales down by N into the object.
+ *
+ * That needs no dedicated property -- the fill rectangle already does it --
+ * but it is the contract the compositors depend on, so pin it down here:
+ * exact colours at exact positions, for several integer scales, and composed
+ * with the orientation used to implement wl_surface.set_buffer_transform.
+ *
+ * The buffer is four solid quadrants, so every assertion is an exact pixel
+ * comparison with no tolerance and no dependence on the scaler's filtering. */
+#define OBJ_W 8
+#define OBJ_H 8
+
+#define C_TL 0xffff0000 /* red */
+#define C_TR 0xff00ff00 /* green */
+#define C_BL 0xff0000ff /* blue */
+#define C_BR 0xffffff00 /* yellow */
+
+static Evas_Object *
+_scaled_img_add(Evas *e, int scale)
+{
+ Evas_Object *o = evas_object_image_add(e);
+ int bw = OBJ_W * scale, bh = OBJ_H * scale;
+ unsigned int *data;
+ int x, y;
+
+ evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
+ evas_object_image_alpha_set(o, EINA_FALSE);
+ evas_object_image_smooth_scale_set(o, EINA_FALSE);
+ /* the image carries the buffer size ... */
+ evas_object_image_size_set(o, bw, bh);
+
+ data = "" EINA_TRUE);
+ fail_if(!data);
+ for (y = 0; y < bh; y++)
+ for (x = 0; x < bw; x++)
+ data[(y * bw) + x] = (y < bh / 2) ? ((x < bw / 2) ? C_TL : C_TR)
+ : ((x < bw / 2) ? C_BL : C_BR);
+ evas_object_image_data_set(o, data);
+ evas_object_image_data_update_add(o, 0, 0, bw, bh);
+
+ /* ... while the object carries the surface size */
+ evas_object_image_filled_set(o, EINA_TRUE);
+ evas_object_move(o, 0, 0);
+ evas_object_resize(o, OBJ_W, OBJ_H);
+ evas_object_show(o);
+ return o;
+}
+
+/* Sample the centre of each quadrant of the rendered object. */
+static void
+_quadrants_get(const unsigned int *pix, unsigned int q[4])
+{
+ const int qx = OBJ_W / 4, qy = OBJ_H / 4;
+
+ q[0] = pix[(qy * OBJ_W) + qx]; /* top-left */
+ q[1] = pix[(qy * OBJ_W) + (OBJ_W - 1 - qx)]; /* top-right */
+ q[2] = pix[((OBJ_H - 1 - qy) * OBJ_W) + qx]; /* bottom-left */
+ q[3] = pix[((OBJ_H - 1 - qy) * OBJ_W) + (OBJ_W - 1 - qx)]; /* bottom-right */
+}
+
+EFL_START_TEST(evas_object_image_buffer_scale_render)
+{
+ int scale;
+
+ /* Scale 1 is the control: whatever the scaled cases assert must already
+ * hold unscaled, otherwise the test is measuring something else. */
+ for (scale = 1; scale <= 4; scale++)
+ {
+ Ecore_Evas *ee = ecore_evas_buffer_new(OBJ_W, OBJ_H);
+ Evas *e;
+ Evas_Object *o;
+ const unsigned int *pix;
+ unsigned int q[4];
+
+ fail_if(!ee);
+ ecore_evas_show(ee);
+ ecore_evas_manual_render_set(ee, EINA_TRUE);
+ e = ecore_evas_get(ee);
+
+ o = _scaled_img_add(e, scale);
+ ecore_evas_manual_render(ee);
+ pix = ecore_evas_buffer_pixels_get(ee);
+ fail_if(!pix);
+ _quadrants_get(pix, q);
+
+ ck_assert_msg(q[0] == C_TL, "scale %d: top-left was %#x", scale, q[0]);
+ ck_assert_msg(q[1] == C_TR, "scale %d: top-right was %#x", scale, q[1]);
+ ck_assert_msg(q[2] == C_BL, "scale %d: bottom-left was %#x", scale, q[2]);
+ ck_assert_msg(q[3] == C_BR, "scale %d: bottom-right was %#x", scale, q[3]);
+
+ /* The image keeps reporting the buffer size, not the surface size --
+ * a compositor needs the buffer size to convert damage. */
+ {
+ int w, h;
+
+ evas_object_image_size_get(o, &w, &h);
+ ck_assert_msg((w == OBJ_W * scale) && (h == OBJ_H * scale),
+ "scale %d: image size was %dx%d", scale, w, h);
+ }
+
+ evas_object_del(o);
+ ecore_evas_free(ee);
+ }
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_image_buffer_scale_orient)
+{
+ /* wl_surface.set_buffer_transform maps onto evas_object_image_orient_set(),
+ * and has to keep working when the buffer is also being scaled down. A 90
+ * degree clockwise rotation sends the bottom-left quadrant to the top-left,
+ * the top-left to the top-right, and so on round. */
+ struct {
+ Evas_Image_Orient orient;
+ unsigned int q[4];
+ const char *name;
+ } tc[] = {
+ { EVAS_IMAGE_ORIENT_NONE, { C_TL, C_TR, C_BL, C_BR }, "none" },
+ { EVAS_IMAGE_ORIENT_90, { C_BL, C_TL, C_BR, C_TR }, "90" },
+ { EVAS_IMAGE_ORIENT_180, { C_BR, C_BL, C_TR, C_TL }, "180" },
+ { EVAS_IMAGE_ORIENT_270, { C_TR, C_BR, C_TL, C_BL }, "270" },
+ { EVAS_IMAGE_FLIP_HORIZONTAL, { C_TR, C_TL, C_BR, C_BL }, "flip-h" },
+ { EVAS_IMAGE_FLIP_VERTICAL, { C_BL, C_BR, C_TL, C_TR }, "flip-v" },
+ };
+ unsigned int i;
+
+ for (i = 0; i < EINA_C_ARRAY_LENGTH(tc); i++)
+ {
+ Ecore_Evas *ee = ecore_evas_buffer_new(OBJ_W, OBJ_H);
+ Evas *e;
+ Evas_Object *o;
+ const unsigned int *pix;
+ unsigned int q[4];
+ int j;
+
+ fail_if(!ee);
+ ecore_evas_show(ee);
+ ecore_evas_manual_render_set(ee, EINA_TRUE);
+ e = ecore_evas_get(ee);
+
+ o = _scaled_img_add(e, 2);
+ evas_object_image_orient_set(o, tc[i].orient);
+ ecore_evas_manual_render(ee);
+ pix = ecore_evas_buffer_pixels_get(ee);
+ fail_if(!pix);
+ _quadrants_get(pix, q);
+
+ for (j = 0; j < 4; j++)
+ ck_assert_msg(q[j] == tc[i].q[j],
+ "orient %s at scale 2: quadrant %d was %#x, wanted %#x",
+ tc[i].name, j, q[j], tc[i].q[j]);
+
+ evas_object_del(o);
+ ecore_evas_free(ee);
+ }
+}
+EFL_END_TEST
+
+void evas_test_image_buffer_scale(TCase *tc)
+{
+ tcase_add_test(tc, evas_object_image_buffer_scale_render);
+ tcase_add_test(tc, evas_object_image_buffer_scale_orient);
+}
+
+#else
+
+void evas_test_image_buffer_scale(TCase *tc EINA_UNUSED)
+{
+}
+
+#endif
diff --git a/src/tests/evas/meson.build b/src/tests/evas/meson.build
index 63670c7986..fd4adccebb 100644
--- a/src/tests/evas/meson.build
+++ b/src/tests/evas/meson.build
@@ -11,6 +11,7 @@ evas_suite_src = [
'evas_test_render_engines.c',
'evas_test_filters.c',
'evas_test_image.c',
+ 'evas_test_buffer_scale.c',
'evas_test_mask.c',
'evas_test_evasgl.c',
'evas_test_focus.c',
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.