This is an automated email from the git hooks/post-receive script.

git pushed a commit to reference refs/pull/142/head
in repository efl.

View the commit online.

commit 9e5c5a43aa3b305162946b78d3b0430df71df210
Author: Cedric BAIL <[email protected]>
AuthorDate: Sat Aug 8 09:23:31 2026 -0600

    evas/tests: cover the image buffer crop property
    
    The source image is a 4x4 buffer of four solid 2x2 quadrants, so a crop
    pinned to one quadrant and scaled over the object must paint it in
    exactly one colour. That makes every assertion an exact pixel comparison
    against ecore_evas_buffer_pixels_get() rather than a tolerance, and it
    holds regardless of how the scaler interpolates.
    
    Covers the property itself, including that any zero or negative extent
    normalises to the same unset value; the four quadrants rendering; the
    clamping cases (oversized crop, negative origin, and a crop entirely
    outside the buffer, which must show the background and so also proves
    the opacity path); damage inside the crop being painted while damage
    outside it does not leak onto the canvas; and the bordered nine-cell
    path reading from inside the crop.
    
    Hit testing is driven with real pointer events rather than
    evas_object_top_at_xy_get(), which is purely geometric and never
    consults precise_is_inside. With the crop on the transparent quadrant
    the centre of the object must stop receiving events, which it would not
    if evas_object_image_is_inside() still sampled the whole buffer.
    
    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_crop.c | 372 +++++++++++++++++++++++++++++++++
 src/tests/evas/meson.build             |   1 +
 4 files changed, 375 insertions(+)

diff --git a/src/tests/evas/evas_suite.c b/src/tests/evas/evas_suite.c
index 05faf08ff8..a5abb8613b 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 Crop", evas_test_image_buffer_crop },
   { "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..a25d9f70a0 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_crop(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_crop.c b/src/tests/evas/evas_test_buffer_crop.c
new file mode 100644
index 0000000000..c46041ed7c
--- /dev/null
+++ b/src/tests/evas/evas_test_buffer_crop.c
@@ -0,0 +1,372 @@
+#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"
+#include "evas_tests_helpers.h"
+
+/* The source image is a 4x4 buffer made of four solid 2x2 quadrants. Cropping
+ * to one quadrant and scaling it over the whole object must therefore paint
+ * the object in exactly one colour, which makes every assertion below an exact
+ * pixel comparison rather than a tolerance. */
+#define IMG_W 4
+#define IMG_H 4
+#define OBJ_W 20
+#define OBJ_H 20
+
+#define C_TL 0xffff0000 /* red    */
+#define C_TR 0xff00ff00 /* green  */
+#define C_BL 0xff0000ff /* blue   */
+#define C_BR 0xffffff00 /* yellow */
+#define C_BG 0xff123456 /* canvas background */
+
+#define START_CROP_TEST()                               \
+   Ecore_Evas *ee;                                      \
+   Evas *e;                                             \
+   Evas_Object *bg, *o;                                 \
+   ee = ecore_evas_buffer_new(OBJ_W, OBJ_H);            \
+   fail_if(!ee);                                        \
+   ecore_evas_show(ee);                                 \
+   ecore_evas_manual_render_set(ee, EINA_TRUE);         \
+   e = ecore_evas_get(ee);                              \
+   bg = _bg_add(e);                                     \
+   o = _img_add(e);                                     \
+   do {} while (0)
+
+#define END_CROP_TEST()                                 \
+   do {                                                 \
+      evas_object_del(o);                               \
+      evas_object_del(bg);                              \
+      ecore_evas_free(ee);                              \
+   } while (0)
+
+static Evas_Object *
+_bg_add(Evas *e)
+{
+   Evas_Object *bg = evas_object_rectangle_add(e);
+
+   evas_object_color_set(bg, 0x12, 0x34, 0x56, 0xff);
+   evas_object_move(bg, 0, 0);
+   evas_object_resize(bg, OBJ_W, OBJ_H);
+   evas_object_show(bg);
+   return bg;
+}
+
+static Evas_Object *
+_img_add(Evas *e)
+{
+   Evas_Object *o = evas_object_image_add(e);
+   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_size_set(o, IMG_W, IMG_H);
+   evas_object_image_smooth_scale_set(o, EINA_FALSE);
+
+   data = "" EINA_TRUE);
+   fail_if(!data);
+   for (y = 0; y < IMG_H; y++)
+     for (x = 0; x < IMG_W; x++)
+       {
+          unsigned int c;
+
+          if (y < IMG_H / 2) c = (x < IMG_W / 2) ? C_TL : C_TR;
+          else               c = (x < IMG_W / 2) ? C_BL : C_BR;
+          data[(y * IMG_W) + x] = c;
+       }
+   evas_object_image_data_set(o, data);
+   evas_object_image_data_update_add(o, 0, 0, IMG_W, IMG_H);
+
+   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;
+}
+
+/* Returns the number of pixels that are not @c expected. */
+static int
+_count_not(const unsigned int *pix, unsigned int expected)
+{
+   int i, bad = 0;
+
+   for (i = 0; i < OBJ_W * OBJ_H; i++)
+     if (pix[i] != expected) bad++;
+   return bad;
+}
+
+static Eina_Bool
+_has_colour(const unsigned int *pix, unsigned int c)
+{
+   int i;
+
+   for (i = 0; i < OBJ_W * OBJ_H; i++)
+     if (pix[i] == c) return EINA_TRUE;
+   return EINA_FALSE;
+}
+
+EFL_START_TEST(evas_object_image_buffer_crop_property)
+{
+   Evas *e = EVAS_TEST_INIT_EVAS();
+   Evas_Object *o = evas_object_image_add(e);
+   int x, y, w, h;
+
+   /* unset by default */
+   evas_object_image_buffer_crop_get(o, &x, &y, &w, &h);
+   fail_if((x != 0) || (y != 0) || (w != 0) || (h != 0));
+
+   evas_object_image_buffer_crop_set(o, 3, 5, 7, 11);
+   evas_object_image_buffer_crop_get(o, &x, &y, &w, &h);
+   fail_if((x != 3) || (y != 5) || (w != 7) || (h != 11));
+
+   /* partial gets must work */
+   x = y = w = h = -1;
+   evas_object_image_buffer_crop_get(o, NULL, NULL, &w, NULL);
+   fail_if(w != 7);
+
+   /* a zero or negative extent unsets, and normalises the origin away so that
+    * "unset" has exactly one representation */
+   evas_object_image_buffer_crop_set(o, 3, 5, 0, 11);
+   evas_object_image_buffer_crop_get(o, &x, &y, &w, &h);
+   fail_if((x != 0) || (y != 0) || (w != 0) || (h != 0));
+
+   evas_object_image_buffer_crop_set(o, 3, 5, 7, -1);
+   evas_object_image_buffer_crop_get(o, &x, &y, &w, &h);
+   fail_if((x != 0) || (y != 0) || (w != 0) || (h != 0));
+
+   evas_object_del(o);
+   evas_free(e);
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_image_buffer_crop_render)
+{
+   const unsigned int *pix;
+   struct {
+      int x, y;
+      unsigned int colour;
+      const char *name;
+   } quad[] = {
+      { 0, 0, C_TL, "top-left" },
+      { 2, 0, C_TR, "top-right" },
+      { 0, 2, C_BL, "bottom-left" },
+      { 2, 2, C_BR, "bottom-right" },
+   };
+   unsigned int i;
+
+   START_CROP_TEST();
+
+   for (i = 0; i < EINA_C_ARRAY_LENGTH(quad); i++)
+     {
+        evas_object_image_buffer_crop_set(o, quad[i].x, quad[i].y, 2, 2);
+        ecore_evas_manual_render(ee);
+        pix = ecore_evas_buffer_pixels_get(ee);
+        fail_if(!pix);
+        ck_assert_msg(_count_not(pix, quad[i].colour) == 0,
+                      "crop of the %s quadrant did not fill the object with %#x",
+                      quad[i].name, quad[i].colour);
+     }
+
+   /* unsetting brings the whole buffer back */
+   evas_object_image_buffer_crop_set(o, 0, 0, 0, 0);
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_has_colour(pix, C_TL) && _has_colour(pix, C_TR) &&
+                 _has_colour(pix, C_BL) && _has_colour(pix, C_BR),
+                 "unsetting the crop did not restore the whole buffer");
+
+   END_CROP_TEST();
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_image_buffer_crop_clamp)
+{
+   const unsigned int *pix;
+
+   START_CROP_TEST();
+
+   /* Reaches past the right and bottom edges: the intersection is the
+    * bottom-right quadrant only. */
+   evas_object_image_buffer_crop_set(o, 2, 2, 100, 100);
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_BR) == 0,
+                 "an oversized crop did not clamp to the buffer");
+
+   /* A negative origin is clamped to 0, shrinking the extent with it, so this
+    * still selects exactly the top-left quadrant. */
+   evas_object_image_buffer_crop_set(o, -2, -2, 4, 4);
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_TL) == 0,
+                 "a negative crop origin was not clamped to the buffer");
+
+   /* Entirely outside: nothing is drawn at all and the background shows
+    * through. This also exercises the opacity path -- if the object were still
+    * reported opaque the background would have been optimised away. */
+   evas_object_image_buffer_crop_set(o, IMG_W + 4, 0, 2, 2);
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_BG) == 0,
+                 "a crop fully outside the buffer still drew something");
+
+   END_CROP_TEST();
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_image_buffer_crop_damage)
+{
+   const unsigned int *pix;
+
+   START_CROP_TEST();
+
+   evas_object_image_buffer_crop_set(o, 0, 0, 2, 2);
+   ecore_evas_manual_render(ee);
+
+   /* Repaint the cropped-in quadrant only, and report damage for it. The
+    * object must pick the change up. */
+   {
+      unsigned int *data = "" EINA_TRUE);
+      int x, y;
+
+      fail_if(!data);
+      for (y = 0; y < IMG_H / 2; y++)
+        for (x = 0; x < IMG_W / 2; x++)
+          data[(y * IMG_W) + x] = C_BR;
+      evas_object_image_data_set(o, data);
+      evas_object_image_data_update_add(o, 0, 0, IMG_W / 2, IMG_H / 2);
+   }
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_BR) == 0,
+                 "damage inside the crop was not painted");
+
+   /* Damage that falls entirely outside the crop must not change what is on
+    * screen: those pixels are never displayed. */
+   {
+      unsigned int *data = "" EINA_TRUE);
+      int x, y;
+
+      fail_if(!data);
+      for (y = IMG_H / 2; y < IMG_H; y++)
+        for (x = IMG_W / 2; x < IMG_W; x++)
+          data[(y * IMG_W) + x] = C_TR;
+      evas_object_image_data_set(o, data);
+      evas_object_image_data_update_add(o, IMG_W / 2, IMG_H / 2,
+                                        IMG_W / 2, IMG_H / 2);
+   }
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_BR) == 0,
+                 "damage outside the crop leaked onto the canvas");
+
+   END_CROP_TEST();
+}
+EFL_END_TEST
+
+static void
+_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
+               void *event_info EINA_UNUSED)
+{
+   int *hit = data;
+
+   *hit = 1;
+}
+
+EFL_START_TEST(evas_object_image_buffer_crop_hittest)
+{
+   unsigned int *data;
+   int hit;
+   int x, y;
+
+   START_CROP_TEST();
+
+   /* Precise hit testing is only consulted when events are actually routed --
+    * evas_object_top_at_xy_get() is purely geometric -- so feed real pointer
+    * events and watch whether the object receives them. */
+   evas_object_image_alpha_set(o, EINA_TRUE);
+   data = "" EINA_TRUE);
+   fail_if(!data);
+   for (y = 0; y < IMG_H / 2; y++)
+     for (x = 0; x < IMG_W / 2; x++)
+       data[(y * IMG_W) + x] = 0x00000000; /* transparent top-left quadrant */
+   evas_object_image_data_set(o, data);
+   evas_object_image_data_update_add(o, 0, 0, IMG_W, IMG_H);
+   evas_object_precise_is_inside_set(o, EINA_TRUE);
+   evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move_cb, &hit);
+   evas_event_feed_mouse_in(e, 0, NULL);
+
+   /* Cropped to the opaque bottom-right quadrant: the centre is a hit. */
+   evas_object_image_buffer_crop_set(o, 2, 2, 2, 2);
+   ecore_evas_manual_render(ee);
+   hit = 0;
+   evas_event_feed_mouse_move(e, (OBJ_W / 2) + 1, (OBJ_H / 2) + 1, 0, NULL);
+   ck_assert_msg(hit == 1, "hit testing missed an opaque cropped region");
+
+   /* Cropped to the transparent top-left quadrant: the same point is not. */
+   evas_object_image_buffer_crop_set(o, 0, 0, 2, 2);
+   ecore_evas_manual_render(ee);
+   hit = 0;
+   evas_event_feed_mouse_move(e, OBJ_W / 2, OBJ_H / 2, 0, NULL);
+   ck_assert_msg(hit == 0,
+                 "hit testing did not follow the crop into a transparent region");
+
+   END_CROP_TEST();
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_image_buffer_crop_border)
+{
+   const unsigned int *pix;
+
+   START_CROP_TEST();
+
+   /* A border splits the source into nine cells. Those cells must be taken
+    * from inside the crop, not from the whole buffer: with the crop pinned to
+    * a solid quadrant every cell is the same colour, so the result stays
+    * solid however the border divides it. */
+   evas_object_image_border_set(o, 1, 1, 1, 1);
+   evas_object_image_buffer_crop_set(o, 2, 2, 2, 2);
+   ecore_evas_manual_render(ee);
+   pix = ecore_evas_buffer_pixels_get(ee);
+   fail_if(!pix);
+   ck_assert_msg(_count_not(pix, C_BR) == 0,
+                 "the bordered draw path read outside the crop");
+
+   END_CROP_TEST();
+}
+EFL_END_TEST
+
+void evas_test_image_buffer_crop(TCase *tc)
+{
+   tcase_add_test(tc, evas_object_image_buffer_crop_property);
+   tcase_add_test(tc, evas_object_image_buffer_crop_render);
+   tcase_add_test(tc, evas_object_image_buffer_crop_clamp);
+   tcase_add_test(tc, evas_object_image_buffer_crop_damage);
+   tcase_add_test(tc, evas_object_image_buffer_crop_hittest);
+   tcase_add_test(tc, evas_object_image_buffer_crop_border);
+}
+
+#else
+
+void evas_test_image_buffer_crop(TCase *tc EINA_UNUSED)
+{
+}
+
+#endif
diff --git a/src/tests/evas/meson.build b/src/tests/evas/meson.build
index 63670c7986..fcbf3a31b2 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_crop.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.

Reply via email to