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.