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).
This commit is contained in:
Jean-Philippe Andre 2016-09-05 13:53:00 +09:00
parent 2f737e8f3b
commit b2d92f2626
7 changed files with 413 additions and 31 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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,