diff --git a/src/lib/efl/interfaces/efl_gfx_types.eot b/src/lib/efl/interfaces/efl_gfx_types.eot index 1dcc816a51..b1f3ab17e8 100644 --- a/src/lib/efl/interfaces/efl_gfx_types.eot +++ b/src/lib/efl/interfaces/efl_gfx_types.eot @@ -4,9 +4,9 @@ enum Efl.Gfx.Colorspace { ycbcr422p709_pl, [[YCbCr 4:2:2 Planar, ITU.BT-709 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb, then Cr rows.]] rgb565_a5p, [[16bit rgb565 + Alpha plane at end - 5 bits of the 8 being used per alpha byte.]] gry8 = 4, [[8-bit gray image, or alpha only.]] - ycbcr422601_pl, [[ YCbCr 4:2:2, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to line of Y,Cb,Y,Cr bytes.]] - ycbcr420nv12601_pl, [[YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb,Cr rows..]] - ycbcr420tm12601_pl, [[YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of tiled row pointer, pointing to the Y rows, then the Cb,Cr rows..]] + ycbcr422601_pl, [[YCbCr 4:2:2, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to line of Y,Cb,Y,Cr bytes.]] + ycbcr420nv12601_pl, [[YCbCr 4:2:0, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb,Cr rows..]] + ycbcr420tm12601_pl, [[YCbCr 4:2:0, ITU.BT-601 specifications. The data pointed to is just an array of tiled row pointer, pointing to the Y rows, then the Cb,Cr rows..]] agry88 = 8, [[AY 8bits Alpha and 8bits Grey, accessed 1 16bits at a time.]] etc1 = 9, [[OpenGL ETC1 encoding of RGB texture (4 bit per pixel) @since 1.10.]] rgb8_etc2 = 10, [[OpenGL GL_COMPRESSED_RGB8_ETC2 texture compression format (4 bit per pixel) @since 1.10.]] diff --git a/src/lib/evas/canvas/efl_canvas_image.c b/src/lib/evas/canvas/efl_canvas_image.c index 3da4290d47..2dac517320 100644 --- a/src/lib/evas/canvas/efl_canvas_image.c +++ b/src/lib/evas/canvas/efl_canvas_image.c @@ -38,6 +38,21 @@ _efl_canvas_image_class_destructor(Eo_Class *eo_class EINA_UNUSED) _map_data_cow = NULL; } +EOLIAN static Eo_Base * +_efl_canvas_image_eo_base_constructor(Eo *obj, Efl_Canvas_Image_Data *pd) +{ + obj = eo_constructor(eo_super(obj, MY_CLASS)); + pd->map_data = eina_cow_alloc(_map_data_cow); + return obj; +} + +EOLIAN static void +_efl_canvas_image_eo_base_destructor(Eo *obj, Efl_Canvas_Image_Data *pd) +{ + eina_cow_free(_map_data_cow, (const Eina_Cow_Data **) &pd->map_data); + eo_destructor(eo_super(obj, MY_CLASS)); +} + Eina_Bool _evas_image_mmap_set(Eo *eo_obj, const Eina_File *f, const char *key) { @@ -805,7 +820,11 @@ _efl_canvas_image_efl_gfx_buffer_buffer_map(Eo *eo_obj, Efl_Canvas_Image_Data *p goto end; // not implemented if (!o->engine_data) - goto end; + { + if (o->cur->u.file) + ERR("image is not loaded yet"); + goto end; + } if ((x < 0) || (y < 0) || ((x + (int) w) > (int) o->cur->image.w) || ((y + (int) h) > (int) o->cur->image.h)) { @@ -839,7 +858,7 @@ _efl_canvas_image_efl_gfx_buffer_buffer_unmap(Eo *eo_obj, Efl_Canvas_Image_Data Evas_Image_Data *o = eo_data_scope_get(eo_obj, EVAS_IMAGE_CLASS); Map_Data *map; - if (!ENFN->image_data_map) + if (!ENFN->image_data_unmap) goto fail; // not implemented if (!o->engine_data) @@ -855,6 +874,8 @@ _efl_canvas_image_efl_gfx_buffer_buffer_unmap(Eo *eo_obj, Efl_Canvas_Image_Data free(map); } + return; + fail: ERR("unmap failed"); } diff --git a/src/lib/evas/canvas/efl_canvas_image.eo b/src/lib/evas/canvas/efl_canvas_image.eo index 05c8373a92..8cececdf94 100644 --- a/src/lib/evas/canvas/efl_canvas_image.eo +++ b/src/lib/evas/canvas/efl_canvas_image.eo @@ -1,4 +1,4 @@ -class Efl.Canvas.Image (Evas.Image, Efl.Image_Load, Efl.Image_Animated) +class Efl.Canvas.Image (Evas.Image, Efl.Gfx.Buffer, Efl.Image_Load, Efl.Image_Animated) { [[Low-level Image object. @@ -9,6 +9,8 @@ class Efl.Canvas.Image (Evas.Image, Efl.Image_Load, Efl.Image_Animated) implements { class.constructor; class.destructor; + Eo.Base.constructor; + Eo.Base.destructor; Efl.Gfx.Buffer.buffer_data_get; Efl.Gfx.Buffer.buffer_data_set; Efl.Gfx.Buffer.buffer_copy_set; diff --git a/src/lib/evas/common/evas_image.h b/src/lib/evas/common/evas_image.h index 917bfcd119..910af43ab5 100644 --- a/src/lib/evas/common/evas_image.h +++ b/src/lib/evas/common/evas_image.h @@ -76,7 +76,8 @@ EAPI int evas_common_load_rgba_image_data_from_file (Image_Entry *im); EAPI double evas_common_load_rgba_image_frame_duration_from_file(Image_Entry *im, int start_frame, int frame_num); void _evas_common_rgba_image_post_surface(Image_Entry *ie); -int _evas_common_rgba_image_surface_size(unsigned int w, unsigned int h, Evas_Colorspace cspace, /* inout */ int *l, int *r, int *t, int *b); +EAPI int _evas_common_rgba_image_surface_size(unsigned int w, unsigned int h, Evas_Colorspace cspace, /* inout */ int *l, int *r, int *t, int *b); +EAPI int _evas_common_rgba_image_data_offset(int rx, int ry, int rw, int rh, int plane, const RGBA_Image *im); EAPI Eina_Bool evas_common_extension_can_load_get(const char *file); diff --git a/src/lib/evas/common/evas_image_main.c b/src/lib/evas/common/evas_image_main.c index 01db75ff25..1b0a7d5cc0 100644 --- a/src/lib/evas/common/evas_image_main.c +++ b/src/lib/evas/common/evas_image_main.c @@ -110,7 +110,7 @@ static const Evas_Cache2_Image_Func _evas_common_image_func2 = }; #endif -int +EAPI int _evas_common_rgba_image_surface_size(unsigned int w, unsigned int h, Evas_Colorspace cspace, /* inout */ int *l, int *r, int *t, int *b) @@ -179,6 +179,93 @@ _evas_common_rgba_image_surface_size(unsigned int w, unsigned int h, #undef ALIGN_TO_PAGE } +EAPI int +_evas_common_rgba_image_data_offset(int rx, int ry, int rw, int rh, int plane, const RGBA_Image *im) +{ + // note: no stride support + + EINA_SAFETY_ON_NULL_RETURN_VAL(im, -1); + + const Image_Entry *ie = &im->cache_entry; + + if ((rx < 0) || (ry < 0) || (rw < 0) || (rh < 0)) + return -1; + + if (((rx + rw) > (int) ie->w) || ((ry + rh) > (int) ie->h)) + return -1; + + switch (ie->space) + { + case EVAS_COLORSPACE_ARGB8888: + return (ry * ie->w + rx) * 4; + case EVAS_COLORSPACE_AGRY88: + return (ry * ie->w + rx) * 2; + case EVAS_COLORSPACE_GRY8: + return ry * ie->w + rx; + case EVAS_COLORSPACE_RGB565_A5P: + if (plane == 0) + return (ry * ie->w + rx) * 2; + else if (plane == 1) + return ry * ie->w + rx + (ie->w * ie->h) * 2; + else return -1; + + // YUV + case EVAS_COLORSPACE_YCBCR422P601_PL: + case EVAS_COLORSPACE_YCBCR422P709_PL: + case EVAS_COLORSPACE_YCBCR422601_PL: + if ((rx & 1) || (rw & 1)) + return -1; + if (plane == 0) + return ry * ie->w + rx; + else if (plane == 1) + return (ry * ie->w) / 2 + rx + ie->w * ie->h; + else return -1; + + case EVAS_COLORSPACE_YCBCR420NV12601_PL: + case EVAS_COLORSPACE_YCBCR420TM12601_PL: + if ((rx & 1) || (ry & 1) || (rw & 1) || (rh & 1)) + return -1; + if (plane == 0) + return ry * ie->w + rx; + else if (plane == 1) + return (ry * ie->w + rx) / 2 + ie->w * ie->h; + else return -1; + + // ETC1/2 RGB, S3TC RGB + case EVAS_COLORSPACE_ETC1: + case EVAS_COLORSPACE_RGB8_ETC2: + case EVAS_COLORSPACE_RGB_S3TC_DXT1: + if ((rx & 3) || (ry & 3) || (rw & 3) || (rh & 3)) + return -1; + return (ry * ie->w + rx) * 8 / 16; + + // ETC2 ARGB, S3TC ARGB + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: + case EVAS_COLORSPACE_RGBA_S3TC_DXT1: + case EVAS_COLORSPACE_RGBA_S3TC_DXT2: + case EVAS_COLORSPACE_RGBA_S3TC_DXT3: + case EVAS_COLORSPACE_RGBA_S3TC_DXT4: + case EVAS_COLORSPACE_RGBA_S3TC_DXT5: + if ((rx & 3) || (ry & 3) || (rw & 3) || (rh & 3)) + return -1; + return (ry * ie->w + rx) * 16 / 16; + + // ETC1+Alpha + case EVAS_COLORSPACE_ETC1_ALPHA: + if ((rx & 3) || (ry & 3) || (rw & 3) || (rh & 3)) + return -1; + if (plane == 0) + return (ry * ie->w + rx) * 8 / 16; + else if (plane == 1) + return (ry * ie->w + rx) * 8 / 16 + (ie->w * ie->h) * 8 / 16; + else return -1; + + default: + CRI("unknown colorspace %d", ie->space); + return EINA_FALSE; + } +} + static void * _evas_common_rgba_image_surface_mmap(Image_Entry *ie, unsigned int w, unsigned int h, /* inout */ int *pl, int *pr, int *pt, int *pb) diff --git a/src/lib/evas/include/evas_common_private.h b/src/lib/evas/include/evas_common_private.h index 98f9066444..600a645820 100644 --- a/src/lib/evas/include/evas_common_private.h +++ b/src/lib/evas/include/evas_common_private.h @@ -428,6 +428,7 @@ typedef struct _RGBA_Font_Source RGBA_Font_Source; typedef struct _RGBA_Font_Glyph RGBA_Font_Glyph; typedef struct _RGBA_Font_Glyph_Out RGBA_Font_Glyph_Out; typedef struct _RGBA_Gfx_Compositor RGBA_Gfx_Compositor; +typedef struct _RGBA_Image_Data_Map RGBA_Image_Data_Map; typedef struct _Cutout_Rect Cutout_Rect; typedef struct _Cutout_Rects Cutout_Rects; @@ -832,6 +833,18 @@ struct _RGBA_Pipe_Thread_Info }; #endif +struct _RGBA_Image_Data_Map { + EINA_INLIST; + unsigned char *ptr; + unsigned int size, stride; // in bytes + int rx, ry, rw, rh; // actual map region + unsigned char *baseptr; + unsigned char plane; + Evas_Colorspace cspace; + Eina_Bool allocated; // ptr is malloc() for cow or cspace conv + Efl_Gfx_Buffer_Access_Mode mode; +}; + struct _RGBA_Image { Image_Entry cache_entry; @@ -879,6 +892,9 @@ struct _RGBA_Image void *data; } func; } native; + + /* data map/unmap */ + RGBA_Image_Data_Map *maps; }; struct _RGBA_Polygon_Point diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index b30b77853d..5d97782067 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -11,6 +11,7 @@ #include "cairo/Ector_Cairo.h" #include "evas_ector_buffer.eo.h" #include "evas_ector_software_buffer.eo.h" +#include "draw.h" #if defined HAVE_DLSYM && ! defined _WIN32 # include /* dlopen,dlclose,etc */ @@ -1436,6 +1437,267 @@ eng_image_data_put(void *data, void *image, DATA32 *image_data) return im; } +static void * +eng_image_data_map(void *engdata EINA_UNUSED, void **image, + unsigned int *length, unsigned int *stride, + int x, int y, int w, int h, + Evas_Colorspace cspace, Efl_Gfx_Buffer_Access_Mode mode) +{ + Eina_Bool cow = EINA_FALSE, to_write = EINA_FALSE; + RGBA_Image_Data_Map *map; + RGBA_Image *im = *image; + Image_Entry *ie = &im->cache_entry; + int src_stride, src_offset; + void *data; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(image && *image, NULL); + + // FIXME: implement planes support (YUV, RGB565, ETC1+Alpha) + // FIXME: implement YUV support (im->cs.data) + if (!im->image.data) + { + int error = evas_cache_image_load_data(ie); + if (error != EVAS_LOAD_ERROR_NONE) + return NULL; + } + + if (mode & EFL_GFX_BUFFER_ACCESS_MODE_COW) + cow = EINA_TRUE; + + if (mode & EFL_GFX_BUFFER_ACCESS_MODE_WRITE) + to_write = EINA_TRUE; + + // verify region is valid regarding special colorspaces (ETC, S3TC, YUV) + src_offset = _evas_common_rgba_image_data_offset(x, y, w, h, 0, im); + if ((src_offset < 0) || !w || !h) + { + ERR("invalid region for colorspace %d: %dx%d + %d,%d, image: %dx%d", + cspace, w, h, x, y, ie->w, ie->h); + return NULL; + } + + src_stride = _evas_common_rgba_image_data_offset(ie->w, 0, 0, 0, 0, im); + + // safety check for COW flag + EINA_INLIST_FOREACH(im->maps, map) + { + if ((!(map->mode & EFL_GFX_BUFFER_ACCESS_MODE_COW)) != (!cow)) + { + ERR("can't map shared image data multiple times with " + "different COW flag"); + return NULL; + } + } + + // ensure that we are the sole owner of this image entry. + if (cow) + { + ie = evas_cache_image_alone(ie); + if (!ie) return NULL; + im = (RGBA_Image *) ie; + *image = im; + } + else + { + if (to_write && (ie->references > 1)) + { + ERR("write map requires COW flag for shared images"); + return NULL; + } + } + + if (cspace != ie->space) + { + // using 4x4 "blocks" to support etc1/2, s3tc... + // note: no actual stride support + Cspace_Convert_Func cs_func; + Eina_Bool can_region; + RGBA_Image fake; + int rx, ry, rw, rh; + void *src_data; + int dst_stride, dst_len, dst_offset = 0; + + cs_func = efl_draw_convert_func_get(ie->space, cspace, &can_region); + if (!cs_func) return NULL; + + // make sure we can convert back, if map for writing + if (to_write && !efl_draw_convert_func_get(cspace, ie->space, NULL)) + return NULL; + + if (can_region) + { + rx = x; + ry = y; + rw = w; + rh = h; + src_data = im->image.data8 + src_offset; + } + else + { + rx = 0; + ry = 0; + rw = ie->w; + rh = ie->h; + src_data = im->image.data8; + } + + // a bit hacky, but avoids passing too many parameters to the function + fake.cache_entry.w = rw; + fake.cache_entry.h = rh; + fake.cache_entry.space = cspace; + dst_stride = _evas_common_rgba_image_data_offset(rw, 0, 0, 0, 0, &fake); + if (!can_region) + dst_offset = _evas_common_rgba_image_data_offset(rx, ry, 0, 0, 0, &fake); + dst_len = rh * dst_stride; + + data = malloc(dst_len); + if (!data) return NULL; + + if (!cs_func(data, src_data, rw, rh, src_stride, dst_stride, ie->flags.alpha, ie->space, cspace)) + { + ERR("color conversion failed"); + free(data); + return NULL; + } + + map = calloc(1, sizeof(*map)); + map->allocated = EINA_TRUE; + map->cspace = cspace; + map->rx = rx; + map->ry = ry; + map->rh = rh; + map->rw = rw; + map->mode = mode; + map->baseptr = data; + map->ptr = map->baseptr + dst_offset; + map->size = dst_len; + map->stride = dst_stride; + } + else + { + // same colorspace + if (!to_write || !cow) + { + // no copy + int end_offset = _evas_common_rgba_image_data_offset(x + w, y + h, 0, 0, 0, im) - src_stride; + map = calloc(1, sizeof(*map)); + map->baseptr = im->image.data8; + map->ptr = im->image.data8 + src_offset; + map->size = end_offset - src_offset; + } + else + { + // copy + int size = _evas_common_rgba_image_data_offset(w, h, 0, 0, 0, im); + data = malloc(size); + if (!data) return NULL; + + memcpy(data, im->image.data8 + src_offset, size); + + map = calloc(1, sizeof(*map)); + map->allocated = EINA_TRUE; + map->baseptr = data; + map->ptr = data; + map->size = size; + } + map->cspace = cspace; + map->rx = x; + map->ry = y; + map->rw = w; + map->rh = h; + map->stride = src_stride; + } + + if (map) + { + im->maps = (RGBA_Image_Data_Map *) + eina_inlist_prepend(EINA_INLIST_GET(im->maps), EINA_INLIST_GET(map)); + if (length) *length = map->size; + if (stride) *stride = map->stride; + return map->ptr; + } + + return NULL; +} + +static void +_image_data_commit(RGBA_Image *im, RGBA_Image_Data_Map *map) +{ + Image_Entry *ie = &im->cache_entry; + + int dst_offset = _evas_common_rgba_image_data_offset(map->rx, map->ry, 0, 0, map->plane, im); + int dst_stride = _evas_common_rgba_image_data_offset(ie->w, 0, 0, 0, map->plane, im); + unsigned char *dst = im->image.data8 + dst_offset; + + if (map->cspace == ie->space) + { + if (dst_stride == (int) map->stride) + { + DBG("unmap commit: single memcpy"); + memcpy(dst, map->ptr, dst_stride * map->rh); + } + else + { + DBG("unmap commit: multiple memcpy"); + for (int k = 0; k < dst_stride; k++) + memcpy(dst + k * dst_stride, map->ptr + k * dst_stride, dst_stride); + } + } + else + { + Cspace_Convert_Func cs_func; + Eina_Bool can_region; + + cs_func = efl_draw_convert_func_get(map->cspace, ie->space, &can_region); + EINA_SAFETY_ON_NULL_RETURN(cs_func); + + DBG("unmap commit: convert func (%p)", cs_func); + if (can_region) + { + cs_func(dst, map->ptr, map->rw, map->rh, map->stride, dst_stride, + ie->flags.alpha, map->cspace, ie->space); + } + else + { + cs_func(dst, map->baseptr, ie->w, ie->h, map->stride, dst_stride, + ie->flags.alpha, map->cspace, ie->space); + } + } +} + +static void * +eng_image_data_unmap(void *engdata EINA_UNUSED, void *image, void *memory, unsigned int length) +{ + RGBA_Image_Data_Map *map; + RGBA_Image *im = image; + Eina_Bool found = EINA_FALSE; + + if (!im || !memory) return im; + + EINA_INLIST_FOREACH(EINA_INLIST_GET(im->maps), map) + { + if ((map->ptr == memory) && (map->size == length)) + { + found = EINA_TRUE; + if (map->allocated) + { + if (map->mode & EFL_GFX_BUFFER_ACCESS_MODE_WRITE) + _image_data_commit(im, map); + free(map->baseptr); + } + im->maps = (RGBA_Image_Data_Map *) + eina_inlist_remove(EINA_INLIST_GET(im->maps), EINA_INLIST_GET(map)); + free(map); + break; + } + } + + if (!found) + ERR("failed to unmap region %p (%u bytes)", memory, length); + + return im; +} + static void _image_flip_horizontal(DATA32 *pixels_out, const DATA32 *pixels_in, int iw, int ih) @@ -4131,8 +4393,8 @@ static Evas_Func func = eng_image_colorspace_get, eng_image_file_colorspace_get, eng_image_can_region_get, - NULL, // image_data_map - NULL, // image_data_unmap + eng_image_data_map, + eng_image_data_unmap, eng_image_native_init, eng_image_native_shutdown, eng_image_native_set, diff --git a/src/static_libs/draw/draw_convert.c b/src/static_libs/draw/draw_convert.c index 6e1a35b29d..4e29d9ef5b 100644 --- a/src/static_libs/draw/draw_convert.c +++ b/src/static_libs/draw/draw_convert.c @@ -431,11 +431,11 @@ efl_draw_convert_func_get(Efl_Gfx_Colorspace srccs, Efl_Gfx_Colorspace dstcs, EINA_SAFETY_ON_FALSE_RETURN_VAL(srccs != dstcs, NULL); - if (dstcs != EFL_GFX_COLORSPACE_ARGB8888) - to_argb = efl_draw_convert_func_get(srccs, EFL_GFX_COLORSPACE_ARGB8888, ®1); - - if (srccs != EFL_GFX_COLORSPACE_ARGB8888) - from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, ®2); + if ((dstcs != EFL_GFX_COLORSPACE_ARGB8888) && (srccs != EFL_GFX_COLORSPACE_ARGB8888)) + { + to_argb = efl_draw_convert_func_get(srccs, EFL_GFX_COLORSPACE_ARGB8888, ®1); + from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, ®2); + } if (region_can) *region_can = EINA_TRUE; @@ -523,5 +523,6 @@ efl_draw_convert_func_get(Efl_Gfx_Colorspace srccs, Efl_Gfx_Colorspace dstcs, } ERR("unsupported colorspace conversion from %d to %d", srccs, dstcs); + if (region_can) *region_can = EINA_FALSE; return NULL; }