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 c253ec029160e6f5e05e2fe1323b65298b854f43
Author: Cedric BAIL <[email protected]>
AuthorDate: Sat Aug 8 09:23:19 2026 -0600

    evas: add a buffer crop property to image objects
    
    An image object always draws its whole pixel buffer. _evas_image_render()
    hardcodes the source rectangle on every draw path:
    
        _draw_image(obj, engine, output, context, surface, pixels,
                    0, 0, imagew, imageh,
                    offx + ix, offy + iy, iw, ih,
                    o->cur->smooth_scale, do_async);
    
    and the bordered and 9-patch paths do the same by deriving their cell
    offsets from imagew/imageh with an implicit 0,0 origin. Scaling into the
    object is available through the fill rectangle, but selecting a
    sub-region of the buffer is not, so there is no way to crop and scale.
    
    Nothing in Evas offered this. Evas_Object_Image_State carries fill (a
    destination rect) and image.w/h (the buffer size) but no source rect,
    and load_opts.scale_load is a dead pass-through to the file loader that
    is never written anywhere in the tree, so it cannot help a native
    surface or a data_set() buffer.
    
    The engines already take an arbitrary source rectangle -- image_draw()
    has taken src_x/y/w/h since forever, and the software engine even
    forwards it into im->native.func.bind() -- so the gap was purely at the
    object level.
    
    Add Efl.Gfx.Image.buffer_crop (@beta), implemented on
    Efl.Canvas.Image_Internal, plus evas_object_image_buffer_crop_set/get()
    for legacy callers. It names a sub-region of the buffer, in buffer
    pixels, which is then scaled into the fill area exactly as the whole
    buffer would have been: it crops before it scales and does not change
    where or how large the result is drawn.
    
    Not called source_region because evas_object_image_source_* is already
    the proxy source API, and Evas_Object_Image_State.source is the proxy
    source object.
    
    The region is resolved once per render into (srcx, srcy, srcw, srch) and
    threaded through all three draw strategies, and equally through
    evas_object_image_is_inside() so pointer events land on the pixels that
    are actually on screen. A crop also changes:
    
      - damage. Buffer-space updates are rebased onto the crop and clipped
        to it, since damage outside is never displayed. The 1:1 branch that
        assumes image size == geometry is skipped entirely, as a crop
        invalidates that assumption.
      - opacity. A crop clamped to nothing draws nothing, so the object
        stops covering its area. One that does select pixels still fills the
        whole fill area and stays opaque.
      - filter input. The raw buffer can no longer be handed to a filter
        unchanged.
      - map. The fill rectangle and borders are ignored under a map, so the
        crop folds into the UV coordinates instead. evas_object_map_update()
        gains the source origin and RGBA_Map remembers it, both so the UVs
        can be rebased and so a crop change is picked up as a map change.
    
    Clamping is done at render time rather than in the setter because the
    buffer size is not known when the crop is set: a file image has no size
    until it loads, and a native surface takes its size from a later
    evas_object_image_size_set().
    
    The property is integer. wp_viewporter's set_source takes 24.8 fixed
    point, but Evas_Func.image_draw is integer across every engine, so a
    double-valued property would only round internally and mislead. Being
    @beta leaves room to revisit that if fractional scaling makes it matter.
    
    Without a crop set, _image_state_crop_get() returns the whole buffer and
    returns EINA_TRUE unconditionally, so every path behaves exactly as it
    did before, degenerate buffer sizes included.
    
    This is the Evas half of honouring wl_surface.set_buffer_scale and
    wp_viewporter in the two Wayland compositors built on top of it.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Ke8zkumHXWeJHNDMzEUqsH
---
 src/lib/efl/interfaces/efl_gfx_image.eo            |  29 +++
 src/lib/evas/Evas_Legacy.h                         |  46 ++++
 src/lib/evas/canvas/efl_canvas_image_internal.eo   |   1 +
 src/lib/evas/canvas/evas_filter_mixin.c            |   2 +-
 src/lib/evas/canvas/evas_image_legacy.c            |  23 ++
 src/lib/evas/canvas/evas_image_private.h           |   4 +
 src/lib/evas/canvas/evas_map.c                     |  20 +-
 src/lib/evas/canvas/evas_object_image.c            | 235 +++++++++++++++++----
 src/lib/evas/canvas/evas_render.c                  |   2 +-
 src/lib/evas/include/evas_common_private.h         |   5 +
 src/lib/evas/include/evas_private.h                |   2 +-
 .../evas/engines/software_generic/evas_engine.c    |   2 +
 12 files changed, 320 insertions(+), 51 deletions(-)

diff --git a/src/lib/efl/interfaces/efl_gfx_image.eo b/src/lib/efl/interfaces/efl_gfx_image.eo
index b9d962b9ab..28b1d16ed5 100644
--- a/src/lib/efl/interfaces/efl_gfx_image.eo
+++ b/src/lib/efl/interfaces/efl_gfx_image.eo
@@ -223,6 +223,35 @@ interface Efl.Gfx.Image
                 stretchable in the image vertical space.]]
          }
       }
+      @property buffer_crop @beta {
+         [[The sub-region of the pixel buffer that is displayed.
+
+           By default the whole pixel buffer is displayed, scaled into the area
+           described by @Efl.Gfx.Fill.fill. This property restricts the displayed
+           content to $region, expressed in buffer pixels, which is then scaled
+           into that same area. In other words it crops before it scales; it does
+           not change where or how large the result is drawn.
+
+           Set a region with a zero or negative width or height to unset the crop
+           and go back to displaying the whole buffer.
+
+           The region is clamped to the buffer, so a crop reaching outside of it
+           displays the intersection rather than raising an error. Note that the
+           buffer size may only become known once the image is loaded, so a crop
+           set beforehand is applied against the final size.
+
+           $region is in whole buffer pixels. Sub-pixel source rectangles, such
+           as those a wp_viewporter client may request, must be rounded by the
+           caller.
+         ]]
+         set {}
+         get {}
+         values {
+            region: Eina.Rect; [[The region of the buffer to display, in buffer
+                                 pixels. The default is 0x0 at 0,0, meaning the
+                                 whole buffer.]]
+         }
+      }
       @property image_size {
          [[This represents the size of the original image in pixels.
 
diff --git a/src/lib/evas/Evas_Legacy.h b/src/lib/evas/Evas_Legacy.h
index 4affbb99ae..9215cae735 100644
--- a/src/lib/evas/Evas_Legacy.h
+++ b/src/lib/evas/Evas_Legacy.h
@@ -5686,6 +5686,52 @@ EVAS_API void evas_object_image_border_set(Evas_Object *obj, int l, int r, int t
  */
 EVAS_API void evas_object_image_border_get(const Evas_Object *obj, int *l, int *r, int *t, int *b);
 
+/**
+ * @brief Set the sub-region of the pixel buffer that is displayed.
+ *
+ * By default the whole pixel buffer is displayed, scaled into the area
+ * described by @ref evas_object_image_fill_set. This restricts the displayed
+ * content to the given region, expressed in buffer pixels, which is then
+ * scaled into that same area. In other words it crops before it scales; it
+ * does not change where or how large the result is drawn.
+ *
+ * Pass a zero or negative @p w or @p h to unset the crop and go back to
+ * displaying the whole buffer.
+ *
+ * The region is clamped to the buffer, so a crop reaching outside of it
+ * displays the intersection rather than failing. Note that the buffer size may
+ * only become known once the image is loaded, so a crop set beforehand is
+ * applied against the final size.
+ *
+ * @param[in] obj The object
+ * @param[in] x The X coordinate of the region, in buffer pixels.
+ * @param[in] y The Y coordinate of the region, in buffer pixels.
+ * @param[in] w The width of the region, in buffer pixels.
+ * @param[in] h The height of the region, in buffer pixels.
+ *
+ * @see evas_object_image_buffer_crop_get
+ * @since 1.29
+ */
+EVAS_API void evas_object_image_buffer_crop_set(Evas_Object *obj, int x, int y, int w, int h);
+
+/**
+ * @brief Retrieve the sub-region of the pixel buffer that is displayed.
+ *
+ * Returns 0x0 at 0,0 when no crop is set, meaning the whole buffer is
+ * displayed. The values are returned as they were set, without the clamping
+ * to the buffer that rendering applies.
+ *
+ * @param[in] obj The object
+ * @param[out] x The X coordinate of the region, in buffer pixels.
+ * @param[out] y The Y coordinate of the region, in buffer pixels.
+ * @param[out] w The width of the region, in buffer pixels.
+ * @param[out] h The height of the region, in buffer pixels.
+ *
+ * @see evas_object_image_buffer_crop_set
+ * @since 1.29
+ */
+EVAS_API void evas_object_image_buffer_crop_get(const Evas_Object *obj, int *x, int *y, int *w, int *h);
+
 /**
  * @brief Scaling factor applied to the image borders.
  *
diff --git a/src/lib/evas/canvas/efl_canvas_image_internal.eo b/src/lib/evas/canvas/efl_canvas_image_internal.eo
index 3a96eb972c..8f8a45fa67 100644
--- a/src/lib/evas/canvas/efl_canvas_image_internal.eo
+++ b/src/lib/evas/canvas/efl_canvas_image_internal.eo
@@ -19,6 +19,7 @@ abstract @beta Efl.Canvas.Image_Internal extends Efl.Canvas.Object implements Ef
       Efl.Gfx.Image.border_insets_scale { get; set; }
       Efl.Gfx.Image.center_fill_mode { get; set; }
       Efl.Gfx.Image.stretch_region { get; set; }
+      Efl.Gfx.Image.buffer_crop { get; set; }
       Efl.Gfx.Image.scale_hint { get; set; }
       Efl.Gfx.Image.content_hint { get; set; }
       Efl.Gfx.Image.image_size { get; }
diff --git a/src/lib/evas/canvas/evas_filter_mixin.c b/src/lib/evas/canvas/evas_filter_mixin.c
index 11a3fe45b5..c1799fcbe1 100644
--- a/src/lib/evas/canvas/evas_filter_mixin.c
+++ b/src/lib/evas/canvas/evas_filter_mixin.c
@@ -281,7 +281,7 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
 
         use_map = EINA_TRUE;
         ENFN->image_size_get(engine, previous, &iw, &ih);
-        evas_object_map_update(eo_obj, x, y, iw, ih, iw, ih);
+        evas_object_map_update(eo_obj, x, y, 0, 0, iw, ih, iw, ih);
      }
 
    if (!pd->data->chain)
diff --git a/src/lib/evas/canvas/evas_image_legacy.c b/src/lib/evas/canvas/evas_image_legacy.c
index d02cd3220b..b95fe1ead9 100644
--- a/src/lib/evas/canvas/evas_image_legacy.c
+++ b/src/lib/evas/canvas/evas_image_legacy.c
@@ -127,6 +127,29 @@ evas_object_image_border_get(const Evas_Object *obj, int *l, int *r, int *t, int
    efl_gfx_image_border_insets_get(obj, l, r, t, b);
 }
 
+EVAS_API void
+evas_object_image_buffer_crop_set(Evas_Object *obj, int x, int y, int w, int h)
+{
+   EVAS_IMAGE_API(obj);
+   efl_gfx_image_buffer_crop_set(obj, EINA_RECT(x, y, w, h));
+}
+
+EVAS_API void
+evas_object_image_buffer_crop_get(const Evas_Object *obj, int *x, int *y, int *w, int *h)
+{
+   Eina_Rect region = EINA_RECT_EMPTY();
+
+   if (efl_isa(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS))
+     region = efl_gfx_image_buffer_crop_get(obj);
+   else
+     EINA_SAFETY_ERROR("object is not an image!");
+
+   if (x) *x = region.x;
+   if (y) *y = region.y;
+   if (w) *w = region.w;
+   if (h) *h = region.h;
+}
+
 EVAS_API void
 evas_object_image_border_scale_set(Evas_Object *obj, double scale)
 {
diff --git a/src/lib/evas/canvas/evas_image_private.h b/src/lib/evas/canvas/evas_image_private.h
index e094478def..6086e6b680 100644
--- a/src/lib/evas/canvas/evas_image_private.h
+++ b/src/lib/evas/canvas/evas_image_private.h
@@ -67,6 +67,10 @@ struct _Evas_Object_Image_State
    struct {
       short         w, h, stride;
    } image;
+   /* Sub-region of the pixel buffer that is displayed, in buffer
+    * coordinates. A width or height <= 0 means "unset", i.e. use the whole
+    * buffer. See _evas_image_crop_get(). */
+   Eina_Rectangle   crop;
    struct {
       double        scale;
       short         l, r, t, b;
diff --git a/src/lib/evas/canvas/evas_map.c b/src/lib/evas/canvas/evas_map.c
index eaafa8ee72..0a62208976 100644
--- a/src/lib/evas/canvas/evas_map.c
+++ b/src/lib/evas/canvas/evas_map.c
@@ -1307,6 +1307,7 @@ evas_map_util_clockwise_get(Evas_Map *m)
 Eina_Bool
 evas_object_map_update(Evas_Object *eo_obj,
                        int x, int y,
+                       int srcx, int srcy,
                        int imagew, int imageh,
                        int uvw, int uvh)
 {
@@ -1319,6 +1320,7 @@ evas_object_map_update(Evas_Object *eo_obj,
      {
         if (obj->map->spans->x != x || obj->map->spans->y != y ||
             obj->map->spans->image.w != imagew || obj->map->spans->image.h != imageh ||
+            obj->map->spans->src.x != srcx || obj->map->spans->src.y != srcy ||
             obj->map->spans->uv.w != uvw || obj->map->spans->uv.h != uvh)
           obj->changed_map = EINA_TRUE;
      }
@@ -1364,6 +1366,8 @@ evas_object_map_update(Evas_Object *eo_obj,
         map_write->spans->uv.h = uvh;
         map_write->spans->image.w = imagew;
         map_write->spans->image.h = imageh;
+        map_write->spans->src.x = srcx;
+        map_write->spans->src.y = srcy;
 
         pts = obj->map->spans->pts;
 
@@ -1392,14 +1396,14 @@ evas_object_map_update(Evas_Object *eo_obj,
         pt->fx = p->x + (float) x;
         pt->fy = p->y + (float) y;
         pt->fz = p->z;
-        if ((uvw == 0) || (imagew == 0)) pt->u = 0;
-        else pt->u = ((lround(p->u * uscale) * imagew) / uvw) * FP1;
-        if ((uvh == 0) || (imageh == 0)) pt->v = 0;
-        else pt->v = ((lround(p->v * vscale) * imageh) / uvh) * FP1;
-        if      (pt->u < 0) pt->u = 0;
-        else if (pt->u > (imagew * FP1)) pt->u = (imagew * FP1);
-        if      (pt->v < 0) pt->v = 0;
-        else if (pt->v > (imageh * FP1)) pt->v = (imageh * FP1);
+        if ((uvw == 0) || (imagew == 0)) pt->u = srcx * FP1;
+        else pt->u = (srcx + ((lround(p->u * uscale) * imagew) / uvw)) * FP1;
+        if ((uvh == 0) || (imageh == 0)) pt->v = srcy * FP1;
+        else pt->v = (srcy + ((lround(p->v * vscale) * imageh) / uvh)) * FP1;
+        if      (pt->u < (srcx * FP1)) pt->u = srcx * FP1;
+        else if (pt->u > ((srcx + imagew) * FP1)) pt->u = (srcx + imagew) * FP1;
+        if      (pt->v < (srcy * FP1)) pt->v = srcy * FP1;
+        else if (pt->v > ((srcy + imageh) * FP1)) pt->v = (srcy + imageh) * FP1;
         pt->col = ARGB_JOIN(p->a, p->r, p->g, p->b);
      }
    if (obj->map->cur.map->count & 0x1)
diff --git a/src/lib/evas/canvas/evas_object_image.c b/src/lib/evas/canvas/evas_object_image.c
index cfe2adb44d..60401346f0 100644
--- a/src/lib/evas/canvas/evas_object_image.c
+++ b/src/lib/evas/canvas/evas_object_image.c
@@ -94,6 +94,7 @@ static const Evas_Object_Image_Pixels default_pixels = {
 static const Evas_Object_Image_State default_state = {
    { 0, 0, 0, 0 }, // fill
    { 0, 0, 0 }, // image
+   { 0, 0, 0, 0 }, // crop (w or h <= 0 means unset)
    { 1.0, 0, 0, 0, 0, 1 }, // border
    { { NULL, 0, 0 }, { NULL, 0, 0 } },
    NULL, NULL, //source, defmap
@@ -625,6 +626,47 @@ _efl_canvas_image_internal_efl_gfx_image_border_insets_get(const Eo *eo_obj EINA
    if (b) *b = o->cur->border.b;
 }
 
+EOLIAN static void
+_efl_canvas_image_internal_efl_gfx_image_buffer_crop_set(Eo *eo_obj, Evas_Image_Data *o, Eina_Rect region)
+{
+   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
+
+   /* A zero or negative extent unsets the crop. Normalise all of those to the
+    * same stored value so that the comparison below and the change detection
+    * in evas_object_image_render_pre() stay simple. */
+   if ((region.w <= 0) || (region.h <= 0))
+     region = EINA_RECT_EMPTY();
+
+   if ((o->cur->crop.x == region.x) && (o->cur->crop.y == region.y) &&
+       (o->cur->crop.w == region.w) && (o->cur->crop.h == region.h)) return;
+
+   evas_object_async_block(obj);
+   EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
+   {
+      state_write->crop.x = region.x;
+      state_write->crop.y = region.y;
+      state_write->crop.w = region.w;
+      state_write->crop.h = region.h;
+      /* A crop that does not cover the whole buffer changes what is opaque. */
+      state_write->opaque_valid = 0;
+   }
+   EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
+   o->changed = EINA_TRUE;
+   evas_object_change(eo_obj, obj);
+}
+
+EOLIAN static Eina_Rect
+_efl_canvas_image_internal_efl_gfx_image_buffer_crop_get(const Eo *eo_obj EINA_UNUSED, Evas_Image_Data *o)
+{
+   Eina_Rect region;
+
+   region.x = o->cur->crop.x;
+   region.y = o->cur->crop.y;
+   region.w = o->cur->crop.w;
+   region.h = o->cur->crop.h;
+   return region;
+}
+
 EOLIAN static void
 _efl_canvas_image_internal_efl_gfx_image_center_fill_mode_set(Eo *eo_obj, Evas_Image_Data *o, Efl_Gfx_Center_Fill_Mode _fill)
 {
@@ -1990,6 +2032,57 @@ _efl_canvas_image_internal_efl_canvas_filter_internal_filter_state_prepare(
    state->scale = obj->cur->scale;
 }
 
+/* Takes the state rather than the object data so that both the current and the
+ * previous state can be asked (see evas_object_image_was_opaque()). */
+static inline Eina_Bool
+_image_state_has_crop(const Evas_Object_Image_State *st)
+{
+   return (st->crop.w > 0) && (st->crop.h > 0);
+}
+
+#define _image_has_crop(o) _image_state_has_crop((o)->cur)
+
+/* Resolve the source rectangle, in buffer coordinates, that the render paths
+ * must read from: the crop when one is set, the whole buffer otherwise.
+ *
+ * The clamping has to happen here rather than in the setter because the buffer
+ * size is not necessarily known when the crop is set -- a file image has no
+ * size until it is loaded, and a native surface takes its size from a later
+ * evas_object_image_size_set().
+ *
+ * Returns EINA_FALSE when the result selects no pixels at all, in which case
+ * the caller must not draw. */
+static inline Eina_Bool
+_image_state_crop_get(const Evas_Object_Image_State *st, int imagew, int imageh,
+                      int *sx, int *sy, int *sw, int *sh)
+{
+   int x, y, w, h;
+
+   if (!_image_state_has_crop(st))
+     {
+        /* Deliberately unconditional: without a crop the callers behave
+         * exactly as they did before this property existed, degenerate buffer
+         * sizes included. Only a crop can select nothing. */
+        *sx = 0; *sy = 0; *sw = imagew; *sh = imageh;
+        return EINA_TRUE;
+     }
+
+   x = st->crop.x;
+   y = st->crop.y;
+   w = st->crop.w;
+   h = st->crop.h;
+   if (x < 0) { w += x; x = 0; }
+   if (y < 0) { h += y; y = 0; }
+   if (w > imagew - x) w = imagew - x;
+   if (h > imageh - y) h = imageh - y;
+
+   *sx = x; *sy = y; *sw = w; *sh = h;
+   return (w > 0) && (h > 0);
+}
+
+#define _image_crop_get(o, iw, ih, sx, sy, sw, sh) \
+   _image_state_crop_get((o)->cur, iw, ih, sx, sy, sw, sh)
+
 static inline Eina_Bool
 _image_has_border(Evas_Object_Protected_Data *obj EINA_UNUSED, Evas_Image_Data *o)
 {
@@ -2031,7 +2124,8 @@ _efl_canvas_image_internal_efl_canvas_filter_internal_filter_input_render(
 
    // FIXME: In GL we could use the image even if scaled
    if (!(ENFN->image_native_get && ENFN->image_native_get(engine, o->engine_data)) &&
-       !_image_has_border(obj, o) && _image_is_filled(obj, o) && !_image_is_scaled(obj, o))
+       !_image_has_border(obj, o) && _image_is_filled(obj, o) && !_image_is_scaled(obj, o) &&
+       !_image_has_crop(o))
      {
         int imagew, imageh, uvw, uvh;
 
@@ -2395,6 +2489,7 @@ static void
 _evas_image_render_hband(Evas_Object_Protected_Data *obj, Evas_Image_Data *o,
                          void *engine, void *output, void *context,
                          void *surface, void *pixels,
+                         int srcx, int srcy,
                          const int *imw, const int *imh EINA_UNUSED,
                          int stretchw, int stretchh EINA_UNUSED,
                          int *inx, int *iny, int *inw, int *inh,
@@ -2417,7 +2512,7 @@ _evas_image_render_hband(Evas_Object_Protected_Data *obj, Evas_Image_Data *o,
 
         if (*inw && *outw)
           _draw_image(obj, engine, output, context, surface, pixels,
-                      *inx, *iny, *inw, *inh,
+                      srcx + *inx, srcy + *iny, *inw, *inh,
                       *outx, *outy, *outw, *outh,
                       o->cur->smooth_scale, do_async);
 
@@ -2433,7 +2528,7 @@ _evas_image_render_hband(Evas_Object_Protected_Data *obj, Evas_Image_Data *o,
 
         if (*inw)
           _draw_image(obj, engine, output, context, surface, pixels,
-                      *inx, *iny, *inw, *inh,
+                      srcx + *inx, srcy + *iny, *inw, *inh,
                       *outx, *outy, *outw, *outh,
                       o->cur->smooth_scale, do_async);
 
@@ -2447,7 +2542,7 @@ _evas_image_render_hband(Evas_Object_Protected_Data *obj, Evas_Image_Data *o,
 
    if (*inw)
      _draw_image(obj, engine, output, context, surface, pixels,
-                 *inx, *iny, *inw, *inh,
+                 srcx + *inx, srcy + *iny, *inw, *inh,
                  *outx, *outy, *outw, *outh,
                  o->cur->smooth_scale, do_async);
 
@@ -2465,6 +2560,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
    int imagew, imageh, uvw, uvh, cw, ch;
    int ix, iy, iw, ih, offx, offy;
    int idw, idh, idx, idy;
+   int srcx, srcy, srcw, srch;
    void *pixels;
 
    pixels = _evas_image_pixels_get(eo_obj, obj, engine, output, context, surface, x, y,
@@ -2474,10 +2570,14 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
    if (ENFN->context_clip_get(engine, context, NULL, NULL, &cw, &ch) && (!cw || !ch))
      return;
 
+   /* Everything below reads from (srcx, srcy, srcw, srch) rather than the
+    * whole buffer. Without a crop that is exactly (0, 0, imagew, imageh). */
+   if (!_image_crop_get(o, imagew, imageh, &srcx, &srcy, &srcw, &srch)) return;
+
    if (!skip_map && (obj->map->cur.map) && (obj->map->cur.map->count > 3)
        && (obj->map->cur.usemap))
      {
-        evas_object_map_update(eo_obj, x, y, imagew, imageh, uvw, uvh);
+        evas_object_map_update(eo_obj, x, y, srcx, srcy, srcw, srch, uvw, uvh);
 
         evas_draw_image_map_async_check(
           obj, engine, output, context, surface, pixels, obj->map->spans,
@@ -2552,8 +2652,8 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   uint32_t vacc;
                   uint32_t vi;
 
-                  imw = imagew;
-                  imh = imageh;
+                  imw = srcw;
+                  imh = srch;
                   ox = offx + ix;
                   oy = offy + iy;
                   iny = 0;
@@ -2584,7 +2684,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                        outh = vacc;
 
                        _evas_image_render_hband(obj, o, engine, output, context,
-                                                surface, pixels,
+                                                surface, pixels, srcx, srcy,
                                                 &imw, &imh, stretchw, stretchh,
                                                 &inx, &iny, &inw, &inh,
                                                 &outx, &outy, &outw, &outh,
@@ -2598,7 +2698,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                        outh = vacc * stretchh / o->cur->stretch.vertical.stretchable;
 
                        _evas_image_render_hband(obj, o, engine, output, context,
-                                                surface, pixels,
+                                                surface, pixels, srcx, srcy,
                                                 &imw, &imh, stretchw, stretchh,
                                                 &inx, &iny, &inw, &inh,
                                                 &outx, &outy, &outw, &outh,
@@ -2611,7 +2711,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   outh = inh; // Again, if my math are correct, this should be the same
 
                   _evas_image_render_hband(obj, o, engine, output, context,
-                                           surface, pixels,
+                                           surface, pixels, srcx, srcy,
                                            &imw, &imh, stretchw, stretchh,
                                            &inx, &iny, &inw, &inh,
                                            &outx, &outy, &outw, &outh,
@@ -2622,7 +2722,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                  (o->cur->border.fill != 0))
                {
                   _draw_image(obj, engine, output, context, surface, pixels,
-                              0, 0, imagew, imageh,
+                              srcx, srcy, srcw, srch,
                               offx + ix, offy + iy, iw, ih,
                               o->cur->smooth_scale, do_async);
                }
@@ -2634,8 +2734,8 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
 
                   ox = offx + ix;
                   oy = offy + iy;
-                  imw = imagew;
-                  imh = imageh;
+                  imw = srcw;
+                  imh = srch;
                   bl = o->cur->border.l;
                   br = o->cur->border.r;
                   bt = o->cur->border.t;
@@ -2714,7 +2814,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = bl; inh = bt;
                   outx = ox; outy = oy;
                   outw = bsl; outh = bst;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .##.
                   // |  |
                   // '--'
@@ -2722,7 +2822,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = imw - bl - br; inh = bt;
                   outx = ox + bsl; outy = oy;
                   outw = iw - bsl - bsr; outh = bst;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--#
                   // |  |
                   // '--'
@@ -2730,7 +2830,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = br; inh = bt;
                   outx = ox + iw - bsr; outy = oy;
                   outw = bsr; outh = bst;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--.
                   // #  |
                   // '--'
@@ -2738,7 +2838,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = bl; inh = imh - bt - bb;
                   outx = ox; outy = oy + bst;
                   outw = bsl; outh = ih - bst - bsb;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--.
                   // |##|
                   // '--'
@@ -2754,11 +2854,11 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                            (obj->cur->render_op == EVAS_RENDER_BLEND))
                          {
                             ENFN->context_render_op_set(engine, context, EVAS_RENDER_COPY);
-                            _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                            _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                             ENFN->context_render_op_set(engine, context, obj->cur->render_op);
                          }
                        else
-                         _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                         _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                     }
                   // .--.
                   // |  #
@@ -2767,7 +2867,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = br; inh = imh - bt - bb;
                   outx = ox + iw - bsr; outy = oy + bst;
                   outw = bsr; outh = ih - bst - bsb;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--.
                   // |  |
                   // #--'
@@ -2775,7 +2875,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = bl; inh = bb;
                   outx = ox; outy = oy + ih - bsb;
                   outw = bsl; outh = bsb;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--.
                   // |  |
                   // '##'
@@ -2783,7 +2883,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = imw - bl - br; inh = bb;
                   outx = ox + bsl; outy = oy + ih - bsb;
                   outw = iw - bsl - bsr; outh = bsb;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                   // .--.
                   // |  |
                   // '--#
@@ -2791,7 +2891,7 @@ _evas_image_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                   inw = br; inh = bb;
                   outx = ox + iw - bsr; outy = oy + ih - bsb;
                   outw = bsr; outh = bsb;
-                  _draw_image(obj, engine, output, context, surface, pixels, inx, iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
+                  _draw_image(obj, engine, output, context, surface, pixels, srcx + inx, srcy + iny, inw, inh, outx, outy, outw, outh, o->cur->smooth_scale, do_async);
                }
              idy += idh;
              if (dobreak_h) break;
@@ -2908,6 +3008,10 @@ evas_object_image_render_pre(Evas_Object *eo_obj,
           }
         if ((o->cur->image.w != o->prev->image.w) ||
             (o->cur->image.h != o->prev->image.h) ||
+            (o->cur->crop.x != o->prev->crop.x) ||
+            (o->cur->crop.y != o->prev->crop.y) ||
+            (o->cur->crop.w != o->prev->crop.w) ||
+            (o->cur->crop.h != o->prev->crop.h) ||
             (o->cur->has_alpha != o->prev->has_alpha) ||
             (o->cur->cspace != o->prev->cspace) ||
             (o->cur->smooth_scale != o->prev->smooth_scale))
@@ -3010,14 +3114,34 @@ evas_object_image_render_pre(Evas_Object *eo_obj,
                                                 y + e->framespace.y,
                                                 w, h);
                     }
+                  int srcx, srcy, srcw, srch;
+
+                  _image_crop_get(o, o->cur->image.w, o->cur->image.h,
+                                  &srcx, &srcy, &srcw, &srch);
+
                   EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
                   {
                      EINA_LIST_FREE(pixi_write->pixel_updates, rr)
                        {
                           Evas_Coord idw, idh, idx, idy;
                           int x, y, w, h;
+                          int dx, dy, dw, dh;
                           e->engine.func->image_dirty_region(ENC, o->engine_data, rr->x, rr->y, rr->w, rr->h);
 
+                          /* Rebase the buffer-space damage onto the crop and
+                           * drop whatever falls outside of it: those pixels are
+                           * never drawn, so they owe the canvas no damage. */
+                          dx = rr->x - srcx;
+                          dy = rr->y - srcy;
+                          dw = rr->w;
+                          dh = rr->h;
+                          RECTS_CLIP_TO_RECT(dx, dy, dw, dh, 0, 0, srcw, srch);
+                          if ((dw <= 0) || (dh <= 0))
+                            {
+                               eina_rectangle_free(rr);
+                               continue;
+                            }
+
                           idx = evas_object_image_figure_x_fill(eo_obj, obj, o->cur->fill.x, o->cur->fill.w, &idw);
                           idy = evas_object_image_figure_y_fill(eo_obj, obj, o->cur->fill.y, o->cur->fill.h, &idh);
 
@@ -3039,10 +3163,10 @@ evas_object_image_render_pre(Evas_Object *eo_obj,
                                     y = idy;
                                     h = ((int)(idy + idh)) - y;
 
-                                    r.x = (rr->x * w) / o->cur->image.w;
-                                    r.y = (rr->y * h) / o->cur->image.h;
-                                    r.w = ((rr->w * w) + (o->cur->image.w * 2) - 1) / o->cur->image.w;
-                                    r.h = ((rr->h * h) + (o->cur->image.h * 2) - 1) / o->cur->image.h;
+                                    r.x = (dx * w) / srcw;
+                                    r.y = (dy * h) / srch;
+                                    r.w = ((dw * w) + (srcw * 2) - 1) / srcw;
+                                    r.h = ((dh * h) + (srch * 2) - 1) / srch;
                                     r.x += obj->cur->geometry.x + x;
                                     r.y += obj->cur->geometry.y + y;
                                     RECTS_CLIP_TO_RECT(r.x, r.y, r.w, r.h,
@@ -3061,8 +3185,13 @@ evas_object_image_render_pre(Evas_Object *eo_obj,
                }
              else
                {
+                  /* This branch maps buffer damage onto the canvas 1:1, which a
+                   * crop breaks: the content is offset and rescaled. Let a
+                   * cropped image fall through to the whole-object damage
+                   * below instead. */
                   if ((o->cur->image.w > 0) &&
                       (o->cur->image.h > 0) &&
+                      (!_image_has_crop(o)) &&
                       (o->cur->image.w == obj->cur->geometry.w) &&
                       (o->cur->image.h == obj->cur->geometry.h) &&
                       (o->cur->fill.x == 0) &&
@@ -3307,6 +3436,17 @@ evas_object_image_is_opaque(Evas_Object *eo_obj EINA_UNUSED,
           return o->cur->opaque;
         if (o->has_filter)
           return o->cur->opaque;
+        /* A crop that selects no pixels at all draws nothing, so the object
+         * covers nothing. A crop that does select pixels still fills the whole
+         * fill area, so it stays opaque. */
+        if (_image_state_has_crop(o->cur))
+          {
+             int sx, sy, sw, sh;
+
+             if (!_image_state_crop_get(o->cur, o->cur->image.w, o->cur->image.h,
+                                        &sx, &sy, &sw, &sh))
+               return o->cur->opaque;
+          }
 
         // FIXME: use proxy
         if (o->cur->source)
@@ -3410,6 +3550,17 @@ evas_object_image_was_opaque(Evas_Object *eo_obj EINA_UNUSED,
           return o->prev->opaque;
         if (!o->engine_data)
           return o->prev->opaque;
+        /* A crop that selects no pixels at all draws nothing, so the object
+         * covers nothing. A crop that does select pixels still fills the whole
+         * fill area, so it stays opaque. */
+        if (_image_state_has_crop(o->prev))
+          {
+             int sx, sy, sw, sh;
+
+             if (!_image_state_crop_get(o->prev, o->prev->image.w, o->prev->image.h,
+                                        &sx, &sy, &sw, &sh))
+               return o->prev->opaque;
+          }
 
         // FIXME: use proxy
         if (o->prev->source)
@@ -3492,6 +3643,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
 {
    Evas_Image_Data *o = type_private_data;
    int imagew, imageh, uvw, uvh, ix, iy, iw, ih, idw, idh, idx, idy;
+   int srcx, srcy, srcw, srch;
    int is_inside = 0;
    void *pixels;
    void *output;
@@ -3522,10 +3674,13 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
           }
      }
 
+   if (!_image_crop_get(o, imagew, imageh, &srcx, &srcy, &srcw, &srch))
+     return is_inside;
+
    /* TODO: not handling map, need to apply map to point */
    if ((obj->map->cur.map) && (obj->map->cur.map->count > 3) && (obj->map->cur.usemap))
      {
-        evas_object_map_update(eo_obj, 0, 0, imagew, imageh, uvw, uvh);
+        evas_object_map_update(eo_obj, 0, 0, srcx, srcy, srcw, srch, uvw, uvh);
 
         ERR("map not supported");
         return is_inside;
@@ -3573,7 +3728,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                  (o->cur->border.fill != 0))
                {
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            0, 0, imagew, imageh,
+                                            srcx, srcy, srcw, srch,
                                             obj->cur->geometry.x + ix,
                                             obj->cur->geometry.y + iy,
                                             iw, ih))
@@ -3592,8 +3747,8 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
 
                   ox = obj->cur->geometry.x + ix;
                   oy = obj->cur->geometry.y + iy;
-                  imw = imagew;
-                  imh = imageh;
+                  imw = srcw;
+                  imh = srch;
                   bl = o->cur->border.l;
                   br = o->cur->border.r;
                   bt = o->cur->border.t;
@@ -3636,7 +3791,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox; outy = oy;
                   outw = bsl; outh = bst;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3652,7 +3807,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox + bsl; outy = oy;
                   outw = iw - bsl - bsr; outh = bst;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3667,7 +3822,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox + iw - bsr; outy = oy;
                   outw = bsr; outh = bst;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3682,7 +3837,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox; outy = oy + bst;
                   outw = bsl; outh = ih - bst - bsb;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3699,7 +3854,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                        outx = ox + bsl; outy = oy + bst;
                        outw = iw - bsl - bsr; outh = ih - bst - bsb;
                        if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                                 inx, iny, inw, inh,
+                                                 srcx + inx, srcy + iny, inw, inh,
                                                  outx, outy, outw, outh))
                          {
                             is_inside = alpha > 0;
@@ -3715,7 +3870,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox + iw - bsr; outy = oy + bst;
                   outw = bsr; outh = ih - bst - bsb;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3730,7 +3885,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox; outy = oy + ih - bsb;
                   outw = bsl; outh = bsb;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3745,7 +3900,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox + bsl; outy = oy + ih - bsb;
                   outw = iw - bsl - bsr; outh = bsb;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
@@ -3760,7 +3915,7 @@ evas_object_image_is_inside(Evas_Object *eo_obj,
                   outx = ox + iw - bsr; outy = oy + ih - bsb;
                   outw = bsr; outh = bsb;
                   if (ENFN->pixel_alpha_get(pixels, px, py, &alpha,
-                                            inx, iny, inw, inh,
+                                            srcx + inx, srcy + iny, inw, inh,
                                             outx, outy, outw, outh))
                     {
                        is_inside = alpha > 0;
diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c
index 7467aa9ce5..8c83e9ff19 100644
--- a/src/lib/evas/canvas/evas_render.c
+++ b/src/lib/evas/canvas/evas_render.c
@@ -1951,7 +1951,7 @@ evas_render_mapped(Evas_Public_Data *evas, Evas_Object *eo_obj,
         RD(level, "  surf size: %ix%i\n", sw, sh);
         if ((sw <= 0) || (sh <= 0)) goto end;
 
-        pchanged = evas_object_map_update(eo_obj, off_x, off_y, sw, sh, sw, sh);
+        pchanged = evas_object_map_update(eo_obj, off_x, off_y, 0, 0, sw, sh, sw, sh);
 
         if (obj->map->surface)
           {
diff --git a/src/lib/evas/include/evas_common_private.h b/src/lib/evas/include/evas_common_private.h
index 3758ec61ee..f295a1909b 100644
--- a/src/lib/evas/include/evas_common_private.h
+++ b/src/lib/evas/include/evas_common_private.h
@@ -869,9 +869,14 @@ struct _RGBA_Map
 {
    void *engine_data;
 
+   /* image is the extent of the source rectangle inside the buffer, src its
+    * origin. Without a crop that is the whole buffer at 0,0. */
    struct {
       int w, h;
    } image, uv;
+   struct {
+      int x, y;
+   } src;
 
    int x, y;
    int count;
diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h
index 5a4b447ee9..5606196100 100644
--- a/src/lib/evas/include/evas_private.h
+++ b/src/lib/evas/include/evas_private.h
@@ -1386,7 +1386,7 @@ void evas_render_proxy_subrender(Evas *eo_e, void *output, Evas_Object *eo_sourc
 
 Eina_Bool evas_map_inside_get(const Evas_Map *m, Evas_Coord x, Evas_Coord y);
 EVAS_API Eina_Bool evas_map_coords_get(const Evas_Map *m, double x, double y, double *mx, double *my, int grab);
-Eina_Bool evas_object_map_update(Evas_Object *obj, int x, int y, int imagew, int imageh, int uvw, int uvh);
+Eina_Bool evas_object_map_update(Evas_Object *obj, int x, int y, int srcx, int srcy, int imagew, int imageh, int uvw, int uvh);
 void evas_map_object_move_diff_set(Evas_Map *m, Evas_Coord diff_x, Evas_Coord diff_y);
 
 Eina_List *evas_module_engine_list(void);
diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c
index 83630e5818..cf546dcc34 100644
--- a/src/modules/evas/engines/software_generic/evas_engine.c
+++ b/src/modules/evas/engines/software_generic/evas_engine.c
@@ -2675,6 +2675,8 @@ _map_draw_thread_cmd(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, RG
    cm->map->engine_data = map->engine_data;
    cm->map->image.w = map->image.w;
    cm->map->image.h = map->image.h;
+   cm->map->src.x = map->src.x;
+   cm->map->src.y = map->src.y;
    cm->map->uv.w = map->uv.w;
    cm->map->uv.h = map->uv.h;
    cm->map->x = map->x;

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to