Evas: Add SW engine map/unmap functions
authorJean-Philippe Andre <jp.andre@samsung.com>
Wed, 23 Mar 2016 04:33:08 +0000 (13:33 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Mon, 28 Mar 2016 07:40:01 +0000 (16:40 +0900)
Also, fix some of the code using them.

src/lib/efl/interfaces/efl_gfx_types.eot
src/lib/evas/canvas/efl_canvas_image.c
src/lib/evas/canvas/efl_canvas_image.eo
src/lib/evas/common/evas_image.h
src/lib/evas/common/evas_image_main.c
src/lib/evas/include/evas_common_private.h
src/modules/evas/engines/software_generic/evas_engine.c
src/static_libs/draw/draw_convert.c

index 1dcc816..b1f3ab1 100644 (file)
@@ -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.]]
index 3da4290..2dac517 100644 (file)
@@ -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");
 }
index 05c8373..8cececd 100644 (file)
@@ -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;
index 917bfcd..910af43 100644 (file)
@@ -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);
 
index 01db75f..1b0a7d5 100644 (file)
@@ -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)
index 98f9066..600a645 100644 (file)
@@ -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
index b30b778..5d97782 100644 (file)
@@ -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 <dlfcn.h>      /* 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,
index 6e1a35b..4e29d9e 100644 (file)
@@ -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, &reg1);
-
-   if (srccs != EFL_GFX_COLORSPACE_ARGB8888)
-     from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, &reg2);
+   if ((dstcs != EFL_GFX_COLORSPACE_ARGB8888) && (srccs != EFL_GFX_COLORSPACE_ARGB8888))
+     {
+        to_argb = efl_draw_convert_func_get(srccs, EFL_GFX_COLORSPACE_ARGB8888, &reg1);
+        from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, &reg2);
+     }
 
    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;
 }