From b2d92f2626574efec6d2e159b08d44c7e4936069 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Mon, 5 Sep 2016 13:53:00 +0900 Subject: [PATCH] evas: Implement support for external buffers This brings support for the eo api for external buffers (like the old data_set / data_get). The new API now works with slices and planes. The internal code still relies on the old cs.data array for YUV color conversion. This makes the code a little bit too complex to my taste. Tested with expedite for RGBA and YUV 422 601 planar, both SW and GL engines (x11). --- src/lib/evas/canvas/efl_canvas_image.c | 23 +- src/lib/evas/common/evas_image_main.c | 18 +- src/lib/evas/include/evas_common_private.h | 3 +- src/lib/evas/include/evas_private.h | 3 + .../evas/engines/gl_common/evas_gl_image.c | 9 +- .../evas/engines/gl_generic/evas_engine.c | 199 +++++++++++++++++- .../engines/software_generic/evas_engine.c | 189 +++++++++++++++++ 7 files changed, 413 insertions(+), 31 deletions(-) diff --git a/src/lib/evas/canvas/efl_canvas_image.c b/src/lib/evas/canvas/efl_canvas_image.c index d1932d3fda..6e0f7e07d1 100644 --- a/src/lib/evas/canvas/efl_canvas_image.c +++ b/src/lib/evas/canvas/efl_canvas_image.c @@ -639,34 +639,30 @@ _image_pixels_set(Evas_Object_Protected_Data *obj, } } - if (copy && !slice) + if (!slice || !slice->mem) { + // note: we release all planes at once if (o->engine_data) ENFN->image_free(ENDT, o->engine_data); o->engine_data = ENFN->image_new_from_copied_data(ENDT, w, h, NULL, o->cur->has_alpha, cspace); } - else if (copy) - { -#warning TODO - CRI("NOT IMPLEMENTED YET"); - //o->engine_data = ENFN->image_copy_slice(ENDT, o->engine_data, slice, w, h, stride, cspace, plane, o->cur->has_alpha) - } else { -#warning TODO - CRI("NOT IMPLEMENTED YET"); - //o->engine_data = ENFN->image_set_slice(ENDT, o->engine_data, slice, w, h, stride, cspace, plane, o->cur->has_alpha); + o->buffer_data_set = EINA_TRUE; + o->engine_data = ENFN->image_data_slice_add(ENDT, o->engine_data, + slice, copy, w, h, stride, + cspace, plane, o->cur->has_alpha); } - if ((o->cur->image.w != w) || (o->cur->image.h != h)) - resized = EINA_TRUE; - if (!o->engine_data) { ERR("Failed to create internal image"); goto end; } + if ((o->cur->image.w != w) || (o->cur->image.h != h)) + resized = EINA_TRUE; + if (ENFN->image_scale_hint_set) ENFN->image_scale_hint_set(ENDT, o->engine_data, o->scale_hint); @@ -698,6 +694,7 @@ end: if (resized) evas_object_inform_call_image_resize(obj->object); + efl_gfx_buffer_update_add(obj->object, 0, 0, w, h); return ret; } diff --git a/src/lib/evas/common/evas_image_main.c b/src/lib/evas/common/evas_image_main.c index df2e5e27eb..ade4b9cd42 100644 --- a/src/lib/evas/common/evas_image_main.c +++ b/src/lib/evas/common/evas_image_main.c @@ -246,12 +246,12 @@ _evas_common_rgba_image_plane_get(const RGBA_Image *im, int plane, Eina_Slice *s } return EINA_FALSE; - // YUV, assume contiguous memory within a plane (and no padding) - // single interleaved plane + // YUV, assume contiguous memory within a plane - padding ok + // 1 plane case EVAS_COLORSPACE_YCBCR422601_PL: if (plane != 0) return EINA_FALSE; slice->mem = csdata[0]; - slice->len = (w * h * 3) / 2; + slice->len = (h > 1) ? ((size_t) (csdata[1] - csdata[0]) * h * 2) : (w * 2); return EINA_TRUE; // 2 planes @@ -260,13 +260,13 @@ _evas_common_rgba_image_plane_get(const RGBA_Image *im, int plane, Eina_Slice *s if (plane == 0) { slice->mem = csdata[0]; - slice->len = w * h; + slice->len = (h > 1) ? ((size_t) (csdata[1] - csdata[0]) * h) : w; return EINA_TRUE; } else if (plane == 1) { slice->mem = csdata[h]; - slice->len = w * h / 4; + slice->len = (h > 1) ? ((size_t) (csdata[h+1] - csdata[h]) * h / 2) : w / 2; return EINA_TRUE; } return EINA_FALSE; @@ -277,19 +277,19 @@ _evas_common_rgba_image_plane_get(const RGBA_Image *im, int plane, Eina_Slice *s if (plane == 0) { slice->mem = csdata[0]; - slice->len = w * h; + slice->len = (h > 1) ? ((size_t) (csdata[1] - csdata[0]) * h) : w; return EINA_TRUE; } else if (plane == 1) { slice->mem = csdata[h]; - slice->len = w * h / 4; + slice->len = (h > 1) ? ((size_t) (csdata[h+1] - csdata[h]) * h / 2) : w / 2; return EINA_TRUE; } else if (plane == 2) { - slice->mem = csdata[2 * h]; - slice->len = w * h / 4; + slice->mem = csdata[h + h / 2]; + slice->len = (h > 1) ? ((size_t) (csdata[h+h/2+1] - csdata[h+h/2]) * h / 2) : w / 2; return EINA_TRUE; } return EINA_FALSE; diff --git a/src/lib/evas/include/evas_common_private.h b/src/lib/evas/include/evas_common_private.h index 8d66ad87f7..07ff291761 100644 --- a/src/lib/evas/include/evas_common_private.h +++ b/src/lib/evas/include/evas_common_private.h @@ -376,6 +376,8 @@ extern EAPI int _evas_log_dom_global; #define TILE_CACHE_LINE_SIZE 64 +#define RGBA_PLANE_MAX 3 + /*****************************************************************************/ #define UNROLL2(op...) op op @@ -422,7 +424,6 @@ typedef struct _RGBA_Pipe RGBA_Pipe; typedef struct _RGBA_Pipe_Thread_Info RGBA_Pipe_Thread_Info; #endif typedef struct _RGBA_Image RGBA_Image; -typedef struct _RGBA_Image_Span RGBA_Image_Span; typedef struct _RGBA_Draw_Context RGBA_Draw_Context; typedef struct _RGBA_Polygon_Point RGBA_Polygon_Point; typedef struct _RGBA_Map_Point RGBA_Map_Point; diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 243c1fee58..2b6c8dfc79 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -1373,6 +1373,9 @@ struct _Evas_Func Eina_Bool (*image_data_unmap) (void *data, void *image, const Eina_Rw_Slice *slice); int (*image_data_maps_get) (void *data, const void *image, const Eina_Rw_Slice **slices); + /* new api for direct data set (not put) */ + void *(*image_data_slice_add) (void *data, void *image, const Eina_Slice *slice, Eina_Bool copy, int w, int h, int stride, Evas_Colorspace space, int plane, Eina_Bool alpha); + int (*image_native_init) (void *data, Evas_Native_Surface_Type type); void (*image_native_shutdown) (void *data, Evas_Native_Surface_Type type); void *(*image_native_set) (void *data, void *image, void *native); diff --git a/src/modules/evas/engines/gl_common/evas_gl_image.c b/src/modules/evas/engines/gl_common/evas_gl_image.c index d982fb42ee..b863bce7d3 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_image.c +++ b/src/modules/evas/engines/gl_common/evas_gl_image.c @@ -360,13 +360,11 @@ evas_gl_common_image_new_from_data(Evas_Engine_GL_Context *gc, unsigned int w, u break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: - if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE); - im->tex = NULL; im->cs.data = data; im->cs.no_free = 1; break; default: - abort(); + ERR("color space not supported: %d", cspace); break; } return im; @@ -414,16 +412,13 @@ evas_gl_common_image_new_from_copied_data(Evas_Engine_GL_Context *gc, unsigned i break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: - if (im->tex) evas_gl_common_texture_free(im->tex, EINA_TRUE); - im->tex = NULL; - im->cs.no_free = 0; if (im->im->cache_entry.h > 0) im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2); if ((data) && (im->cs.data)) memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2); break; default: - abort(); + ERR("color space not supported: %d", cspace); break; } return im; diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c index f383326507..50c5f5599e 100644 --- a/src/modules/evas/engines/gl_generic/evas_engine.c +++ b/src/modules/evas/engines/gl_generic/evas_engine.c @@ -245,7 +245,8 @@ eng_image_file_colorspace_get(void *data EINA_UNUSED, void *image) } static Eina_Bool -eng_image_data_direct_get(void *data EINA_UNUSED, void *image, int plane, Eina_Slice *slice, Evas_Colorspace *cspace) +eng_image_data_direct_get(void *data EINA_UNUSED, void *image, int plane, + Eina_Slice *slice, Evas_Colorspace *cspace) { Evas_GL_Image *im = image; @@ -2897,6 +2898,201 @@ eng_image_data_maps_get(void *engdata EINA_UNUSED, const void *image, const Eina return k; } +static inline Eina_Bool +_is_yuv(Efl_Gfx_Colorspace cspace) +{ + switch (cspace) + { + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + case EFL_GFX_COLORSPACE_YCBCR420TM12601_PL: + return EINA_TRUE; + + default: + return EINA_FALSE; + } +} + +static void * +eng_image_data_slice_add(void *engdata, void *image, + const Eina_Slice *slice, Eina_Bool copy, + int w, int h, int stride, Evas_Colorspace cspace, + int plane, Eina_Bool alpha) +{ + const Eina_Bool use_cs = _is_yuv(cspace); + const unsigned char **cs_data; + Evas_GL_Image *im = image; + int bpp = 0; + + // Note: This code is not very robust by choice. It should NOT be used + // in conjunction with data_put/data_get. Ever. + // Assume w,h,cspace,alpha to be correct. + // We still use cs.data for YUV. + // 'image' may be NULL, in that case create a new one. Otherwise, it must + // have been created by a previous call to this function. + + if ((plane < 0) || (plane >= RGBA_PLANE_MAX)) goto fail; + if (!slice || !slice->mem) goto fail; + copy = !!copy; + + // not implemented + if (use_cs && copy) + { + // To implement this, we should switch the internals to slices first, + // as this would give 3 planes rather than N rows of datas + ERR("Evas can not copy YUV data (not implemented yet)."); + goto fail; + } + + if (im && !im->im) + { + evas_gl_common_image_unref(im); + im = NULL; + } + + // alloc + if (!im) + { + switch (cspace) + { + case EFL_GFX_COLORSPACE_ARGB8888: + case EFL_GFX_COLORSPACE_AGRY88: + case EFL_GFX_COLORSPACE_GRY8: + if (plane != 0) goto fail; + if (copy) + im = eng_image_new_from_copied_data(engdata, w, h, NULL, alpha, cspace); + else + im = eng_image_new_from_data(engdata, w, h, NULL, alpha, cspace); + break; + + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + im = eng_image_new_from_data(engdata, w, h, NULL, alpha, cspace); + break; + + default: + // TODO: ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12) + goto fail; + } + if (!im) goto fail; + } + + if (use_cs && (!im->cs.data || im->cs.no_free)) + { + im->cs.data = calloc(1, h * sizeof(void *) * 2); + if (!im->cs.data) goto fail; + im->cs.no_free = EINA_FALSE; + if (!im->im->cs.no_free) free(im->im->cs.data); + im->im->cs.data = im->cs.data; + im->im->cs.no_free = EINA_TRUE; + } + + // is this allocating image.data or cs.data? + evas_gl_common_image_alloc_ensure(im); + if (!im->im) + goto fail; + + // assign + switch (cspace) + { + case EFL_GFX_COLORSPACE_ARGB8888: + bpp = 4; + case EFL_GFX_COLORSPACE_AGRY88: + if (!bpp) bpp = 2; + case EFL_GFX_COLORSPACE_GRY8: + if (!bpp) bpp = 1; + if (plane != 0) goto fail; + if (!im->im->image.data) goto fail; + if (!stride) stride = w * bpp; + if (copy) + { + for (int y = 0; y < h; y++) + { + const unsigned char *src = slice->bytes + h * stride; + unsigned char *dst = im->im->image.data8 + bpp * w; + memcpy(dst, src, w * bpp); + } + } + else + { + if (stride != (bpp * w)) + { + ERR("invalid stride for zero-copy data set"); + goto fail; + } + im->im->image.data = (DATA32 *) slice->mem; + im->im->image.no_free = EINA_TRUE; + } + break; + + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + /* YCbCr 4:2:2 Planar: Y rows, then the Cb, then Cr rows. */ + cs_data = im->cs.data; + if (plane == 0) + { + if (!stride) stride = w; + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + } + else if (plane == 1) + { + if (!stride) stride = w / 2; + for (int y = 0; y < (h / 2); y++) + cs_data[h + y] = slice->bytes + (y * stride); + } + else if (plane == 2) + { + if (!stride) stride = w / 2; + for (int y = 0; y < (h / 2); y++) + cs_data[h + (h / 2) + y] = slice->bytes + (y * stride); + } + else goto fail; + break; + + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + /* YCbCr 4:2:2: lines of Y,Cb,Y,Cr bytes. */ + if (plane != 0) goto fail; + if (!stride) stride = w * 2; + cs_data = im->cs.data; + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + break; + + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + /* YCbCr 4:2:0: Y rows, then the Cb,Cr rows. */ + if (!stride) stride = w; + cs_data = im->cs.data; + if (plane == 0) + { + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + } + else if (plane == 1) + { + for (int y = 0; y < (h / 2); y++) + cs_data[h + y] = slice->bytes + (y * stride); + } + break; + + // ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12) + default: + ERR("unsupported color space %d", cspace); + goto fail; + } + + evas_gl_common_image_dirty(im, 0, 0, 0, 0); + return im; + +fail: + if (im) eng_image_free(engdata, im); + return NULL; +} + static int module_open(Evas_Module *em) { @@ -2980,6 +3176,7 @@ module_open(Evas_Module *em) ORD(image_data_map); ORD(image_data_unmap); ORD(image_data_maps_get); + ORD(image_data_slice_add); ORD(font_cache_flush); ORD(font_cache_set); diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index e34463c656..52505d8882 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -1749,6 +1749,194 @@ eng_image_data_maps_get(void *engdata EINA_UNUSED, const void *image, const Eina return k; } +static inline Eina_Bool +_is_yuv(Efl_Gfx_Colorspace cspace) +{ + switch (cspace) + { + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + case EFL_GFX_COLORSPACE_YCBCR420TM12601_PL: + return EINA_TRUE; + + default: + return EINA_FALSE; + } +} + +static void * +eng_image_data_slice_add(void *engdata, void *image, + const Eina_Slice *slice, Eina_Bool copy, + int w, int h, int stride, Evas_Colorspace cspace, + int plane, Eina_Bool alpha) +{ + const Eina_Bool use_cs = _is_yuv(cspace); + const unsigned char **cs_data; + RGBA_Image *im = image; + int bpp = 0; + + // Note: This code is not very robust by choice. It should NOT be used + // in conjunction with data_put/data_get. Ever. + // Assume w,h,cspace,alpha to be correct. + // We still use cs.data for YUV. + // 'image' may be NULL, in that case create a new one. Otherwise, it must + // have been created by a previous call to this function. + + if ((plane < 0) || (plane >= RGBA_PLANE_MAX)) goto fail; + if (!slice || !slice->mem) goto fail; + copy = !!copy; + + // not implemented + if (use_cs && copy) + { + // To implement this, we should switch the internals to slices first, + // as this would give 3 planes rather than N rows of datas + ERR("Evas can not copy YUV data (not implemented yet)."); + goto fail; + } + + // alloc + if (!im) + { + switch (cspace) + { + case EFL_GFX_COLORSPACE_ARGB8888: + case EFL_GFX_COLORSPACE_AGRY88: + case EFL_GFX_COLORSPACE_GRY8: + if (plane != 0) goto fail; + if (copy) + im = eng_image_new_from_copied_data(engdata, w, h, NULL, alpha, cspace); + else + im = eng_image_new_from_data(engdata, w, h, NULL, alpha, cspace); + break; + + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + // Use 'copied' to allocate the RGBA buffer + im = eng_image_new_from_copied_data(engdata, w, h, NULL, alpha, cspace); + break; + + default: + // TODO: ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12) + goto fail; + } + if (!im) goto fail; + } + else + { + im = (RGBA_Image *) evas_cache_image_alone(&im->cache_entry); + if (!im) goto fail; + } + + if (use_cs && (!im->cs.data || im->cs.no_free)) + { + im->cs.data = calloc(1, h * sizeof(void *) * 2); + if (!im->cs.data) goto fail; + im->cs.no_free = EINA_FALSE; + } + + // assign + switch (cspace) + { + case EFL_GFX_COLORSPACE_ARGB8888: + bpp = 4; + case EFL_GFX_COLORSPACE_AGRY88: + if (!bpp) bpp = 2; + case EFL_GFX_COLORSPACE_GRY8: + if (!bpp) bpp = 1; + if (plane != 0) goto fail; + if (!stride) stride = w * bpp; + if (copy) + { + for (int y = 0; y < h; y++) + { + const unsigned char *src = slice->bytes + h * stride; + unsigned char *dst = im->image.data8 + bpp * w; + memcpy(dst, src, w * bpp); + } + } + else + { + if (stride != (bpp * w)) + { + ERR("invalid stride for zero-copy data set"); + goto fail; + } + im->image.data = (DATA32 *) slice->mem; + im->image.no_free = EINA_TRUE; + } + break; + + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + /* YCbCr 4:2:2 Planar: Y rows, then the Cb, then Cr rows. */ + cs_data = im->cs.data; + if (plane == 0) + { + if (!stride) stride = w; + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + } + else if (plane == 1) + { + if (!stride) stride = w / 2; + for (int y = 0; y < (h / 2); y++) + cs_data[h + y] = slice->bytes + (y * stride); + } + else if (plane == 2) + { + if (!stride) stride = w / 2; + for (int y = 0; y < (h / 2); y++) + cs_data[h + (h / 2) + y] = slice->bytes + (y * stride); + } + else goto fail; + evas_common_image_colorspace_dirty(im); + break; + + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + /* YCbCr 4:2:2: lines of Y,Cb,Y,Cr bytes. */ + if (plane != 0) goto fail; + if (!stride) stride = w * 2; + cs_data = im->cs.data; + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + evas_common_image_colorspace_dirty(im); + break; + + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + /* YCbCr 4:2:0: Y rows, then the Cb,Cr rows. */ + if (!stride) stride = w; + cs_data = im->cs.data; + if (plane == 0) + { + for (int y = 0; y < h; y++) + cs_data[y] = slice->bytes + (y * stride); + } + else if (plane == 1) + { + for (int y = 0; y < (h / 2); y++) + cs_data[h + y] = slice->bytes + (y * stride); + } + evas_common_image_colorspace_dirty(im); + break; + + // ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12) + default: + ERR("unsupported color space %d", cspace); + goto fail; + } + + return im; + +fail: + if (im) eng_image_free(engdata, im); + return NULL; +} + static void _image_flip_horizontal(DATA32 *pixels_out, const DATA32 *pixels_in, int iw, int ih) @@ -4449,6 +4637,7 @@ static Evas_Func func = eng_image_data_map, eng_image_data_unmap, eng_image_data_maps_get, + eng_image_data_slice_add, eng_image_native_init, eng_image_native_shutdown, eng_image_native_set,