
#include "evas_image_private.h"
#include "evas_image_eo.h"

#define EVAS_IMAGE_API(_o, ...) do { \
   if (EINA_UNLIKELY(!efl_isa(_o, EFL_CANVAS_IMAGE_INTERNAL_CLASS))) { \
      EINA_SAFETY_ERROR("object is not an image!"); \
      return __VA_ARGS__; \
   } } while (0)

#define EVAS_IMAGE_LEGACY_API(_o, ...) do { \
   EVAS_OBJECT_LEGACY_API(_o, __VA_ARGS__); \
   EVAS_IMAGE_API(_o, __VA_ARGS__); \
   } while (0)

typedef struct _Evas_Image_Legacy_Pixels_Entry Evas_Image_Legacy_Pixels_Entry;

struct _Evas_Image_Legacy_Pixels_Entry
{
   Eo    *object;
   void  *image;
};

EVAS_API Evas_Object *
evas_object_image_add(Evas *eo_e)
{
   eo_e = evas_find(eo_e);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(eo_e, EVAS_CANVAS_CLASS), NULL);
   return efl_add(EVAS_IMAGE_CLASS, eo_e,
                 efl_gfx_fill_auto_set(efl_added, EINA_FALSE),
                 efl_canvas_object_legacy_ctor(efl_added));
}

EVAS_API Evas_Object *
evas_object_image_filled_add(Evas *eo_e)
{
   eo_e = evas_find(eo_e);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(eo_e, EVAS_CANVAS_CLASS), NULL);
   return efl_add(EVAS_IMAGE_CLASS, eo_e,
                 efl_canvas_object_legacy_ctor(efl_added));
}

EVAS_API void
evas_object_image_memfile_set(Evas_Object *eo_obj, void *data, int size, char *format EINA_UNUSED, char *key)
{
   Eina_File *f;

   EVAS_IMAGE_API(eo_obj);

   f = eina_file_virtualize(NULL, data, size, EINA_TRUE);
   if (!f) return ;
   efl_file_simple_mmap_load(eo_obj, f, key);
   eina_file_close(f); // close matching open OK
}

EVAS_API void
evas_object_image_fill_set(Evas_Object *obj,
                           Evas_Coord x, Evas_Coord y,
                           Evas_Coord w, Evas_Coord h)
{
   EVAS_IMAGE_API(obj);
   _evas_image_fill_set(obj, efl_data_scope_get(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS), x, y, w, h);
}

EVAS_API void
evas_object_image_preload(Evas_Object *eo_obj, Eina_Bool cancel)
{
   EVAS_IMAGE_API(eo_obj);
   if (cancel) _evas_image_load_async_cancel(eo_obj);
   else _evas_image_load_async_start(eo_obj);
}

EVAS_API Eina_Bool
evas_object_image_filled_get(const Evas_Object *eo_obj)
{
   EVAS_IMAGE_API(eo_obj, EINA_FALSE);
   return efl_gfx_fill_auto_get(eo_obj);
}

EVAS_API void
evas_object_image_filled_set(Evas_Object *eo_obj, Eina_Bool value)
{
   EVAS_IMAGE_API(eo_obj);
   efl_gfx_fill_auto_set(eo_obj, value);
}

EVAS_API void
evas_object_image_fill_get(const Evas_Object *obj,
                           Evas_Coord *x, Evas_Coord *y,
                           Evas_Coord *w, Evas_Coord *h)
{
   Eina_Rect r;

   EVAS_IMAGE_API(obj);
   r = efl_gfx_fill_get(obj);
   if (x) *x = r.x;
   if (y) *y = r.y;
   if (w) *w = r.w;
   if (h) *h = r.h;
}

EVAS_API void
evas_object_image_alpha_set(Evas_Object *obj, Eina_Bool alpha)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_buffer_alpha_set(obj, alpha);
}

EVAS_API Eina_Bool
evas_object_image_alpha_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return efl_gfx_buffer_alpha_get(obj);
}

EVAS_API void
evas_object_image_border_set(Evas_Object *obj, int l, int r, int t, int b)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_border_insets_set(obj, l, r, t, b);
}

EVAS_API void
evas_object_image_border_get(const Evas_Object *obj, int *l, int *r, int *t, int *b)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_border_insets_get(obj, l, r, t, b);
}

EVAS_API void
evas_object_image_border_scale_set(Evas_Object *obj, double scale)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_border_insets_scale_set(obj, scale);
}

EVAS_API double
evas_object_image_border_scale_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0.0);
   return efl_gfx_image_border_insets_scale_get(obj);
}

EVAS_API void
evas_object_image_border_center_fill_set(Evas_Object *obj, Evas_Border_Fill_Mode fill)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_center_fill_mode_set(obj, (Efl_Gfx_Center_Fill_Mode) fill);
}

EVAS_API Evas_Border_Fill_Mode
evas_object_image_border_center_fill_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_BORDER_FILL_NONE);
   return (Evas_Border_Fill_Mode) efl_gfx_image_center_fill_mode_get(obj);
}

EVAS_API void
evas_object_image_size_get(const Evas_Object *obj, int *w, int *h)
{
   Eina_Size2D sz;
   EVAS_IMAGE_API(obj);
   sz = efl_gfx_view_size_get(obj);
   if (w) *w = sz.w;
   if (h) *h = sz.h;
}

EVAS_API Evas_Colorspace
evas_object_image_colorspace_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_COLORSPACE_ARGB8888);
   return (Evas_Colorspace) efl_gfx_buffer_colorspace_get(obj);
}

EVAS_API int
evas_object_image_stride_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0);
   Evas_Image_Data *o = efl_data_scope_get(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   return o->cur->image.stride;
}

EVAS_API void
evas_object_image_data_update_add(Evas_Object *obj, int x, int y, int w, int h)
{
   Eina_Rect r;

   EVAS_IMAGE_API(obj);
   r = EINA_RECT(x, y, w, h);
   efl_gfx_buffer_update_add(obj, &r);
}

EVAS_API void
evas_object_image_file_set(Evas_Object *obj, const char *file, const char *key)
{
   EVAS_IMAGE_API(obj);
   efl_file_simple_load(obj, file, key);
}

EVAS_API void
evas_object_image_file_get(const Evas_Object *obj, const char **file, const char **key)
{
   EVAS_IMAGE_API(obj);
   efl_file_simple_get(obj, file, key);
}

EVAS_API void
evas_object_image_mmap_set(Evas_Object *obj, const Eina_File *f, const char *key)
{
   EVAS_IMAGE_API(obj);
   efl_file_simple_mmap_load(obj, f, key);
}

EVAS_API void
evas_object_image_mmap_get(const Evas_Object *obj, const Eina_File **f, const char **key)
{
   EVAS_IMAGE_API(obj);
   efl_file_simple_mmap_get(obj, f, key);
}

EVAS_API Eina_Bool
evas_object_image_save(const Evas_Object *obj, const char *file, const char *key, const char *flags)
{
   char *encoding = NULL;
   Efl_File_Save_Info info;
   Eina_Error ret;

   EVAS_IMAGE_API(obj, EINA_FALSE);

   if (flags)
     {
        char *p, *pp;
        char *tflags;
        int quality = 80, compress = 9;

        tflags = alloca(strlen(flags) + 1);
        strcpy(tflags, flags);
        p = tflags;
        while (p)
          {
             pp = strchr(p, ' ');
             if (pp) *pp = 0;
             sscanf(p, "quality=%4i", &quality);
             sscanf(p, "compress=%4i", &compress);
             sscanf(p, "encoding=%ms", &encoding);
             if (pp) p = pp + 1;
             else break;
          }
        info.quality = quality;
        info.compression = compress;
        info.encoding = encoding;

     }
   ret = efl_file_save(obj, file, key, flags ? &info : NULL);
   free(encoding);
   return ret;
}

EVAS_API Eina_Bool
evas_object_image_animated_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return _evas_image_animated_get(obj);
}

EVAS_API void
evas_object_image_animated_frame_set(Evas_Object *obj, int frame_index)
{
   EVAS_IMAGE_API(obj);
   _evas_image_animated_frame_set(obj, frame_index);
}

EVAS_API int
evas_object_image_animated_frame_get(Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0);
   return _evas_image_animated_frame_get(obj);
}

EVAS_API int
evas_object_image_animated_frame_count_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0);
   return _evas_image_animated_frame_count_get(obj);
}

EVAS_API Evas_Image_Animated_Loop_Hint
evas_object_image_animated_loop_type_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_IMAGE_ANIMATED_HINT_NONE);
   return (Evas_Image_Animated_Loop_Hint) _evas_image_animated_loop_type_get(obj);
}

EVAS_API int
evas_object_image_animated_loop_count_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0);
   return _evas_image_animated_loop_count_get(obj);
}

EVAS_API double
evas_object_image_animated_frame_duration_get(const Evas_Object *obj, int start_frame, int frame_num)
{
   EVAS_IMAGE_API(obj, 0.0);
   return _evas_image_animated_frame_duration_get(obj, start_frame, frame_num);
}

EVAS_API void
evas_object_image_load_size_set(Evas_Object *obj, int w, int h)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_size_set(obj, w, h);
}

EVAS_API void
evas_object_image_load_size_get(const Evas_Object *obj, int *w, int *h)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_size_get(obj, w, h);
}

EVAS_API void
evas_object_image_load_dpi_set(Evas_Object *obj, double dpi)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_dpi_set(obj, dpi);
}

EVAS_API double
evas_object_image_load_dpi_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 0.0);
   return _evas_image_load_dpi_get(obj);
}

EVAS_API void
evas_object_image_load_region_set(Evas_Object *obj, int x, int y, int w, int h)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_region_set(obj, x, y, w, h);
}

EVAS_API void
evas_object_image_load_region_get(const Evas_Object *obj, int *x, int *y, int *w, int *h)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_region_get(obj, x, y, w, h);
}

EVAS_API Eina_Bool
evas_object_image_region_support_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return _evas_image_load_region_support_get(obj);
}

EVAS_API void
evas_object_image_load_orientation_set(Evas_Object *obj, Eina_Bool enable)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_orientation_set(obj, enable);
}

EVAS_API Eina_Bool
evas_object_image_load_orientation_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return _evas_image_load_orientation_get(obj);
}

EVAS_API void
evas_object_image_load_scale_down_set(Evas_Object *obj, int scale_down)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_scale_down_set(obj, scale_down);
}

EVAS_API int
evas_object_image_load_scale_down_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, 1);
   return _evas_image_load_scale_down_get(obj);
}

EVAS_API void
evas_object_image_load_head_skip_set(Evas_Object *obj, Eina_Bool skip)
{
   EVAS_IMAGE_API(obj);
   _evas_image_load_head_skip_set(obj, skip);
}

EVAS_API Eina_Bool
evas_object_image_load_head_skip_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return _evas_image_load_head_skip_get(obj);
}

EVAS_API Evas_Load_Error
evas_object_image_load_error_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_LOAD_ERROR_GENERIC);
   return _efl_gfx_image_load_error_to_evas_load_error(efl_gfx_image_load_error_get(obj));
}

EVAS_API void
evas_object_image_smooth_scale_set(Evas_Object *obj, Eina_Bool smooth_scale)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_smooth_scale_set(obj, smooth_scale);
}

EVAS_API Eina_Bool
evas_object_image_smooth_scale_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   return efl_gfx_image_smooth_scale_get(obj);
}

EVAS_API void
evas_object_image_orient_set(Evas_Object *obj, Evas_Image_Orient orient)
{
   EVAS_IMAGE_API(obj);

   Evas_Image_Data *o = efl_data_scope_get(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   _evas_image_orientation_set(obj, o, orient);
}

EVAS_API Evas_Image_Orient
evas_object_image_orient_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_IMAGE_ORIENT_NONE);

   Evas_Image_Data *o = efl_data_scope_get(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   return o->cur->orient;
}

EVAS_API void
evas_object_image_snapshot_set(Evas_Object *eo, Eina_Bool s)
{
   EVAS_IMAGE_API(eo);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo, EFL_CANVAS_OBJECT_CLASS);

   if (obj->cur->snapshot == s) return;

   EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
     state_write->snapshot = !!s;
   EINA_COW_STATE_WRITE_END(obj, state_write, cur);
}

EVAS_API Eina_Bool
evas_object_image_snapshot_get(const Evas_Object *eo)
{
   EVAS_IMAGE_API(eo, EINA_FALSE);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo, EFL_CANVAS_OBJECT_CLASS);
   return obj->cur->snapshot;
}

EVAS_API Eina_Bool
evas_object_image_source_set(Evas_Object *eo, Evas_Object *src)
{
   EVAS_IMAGE_API(eo, EINA_FALSE);
   return _evas_image_proxy_source_set(eo, src);
}

EVAS_API Evas_Object *
evas_object_image_source_get(const Evas_Object *eo)
{
   EVAS_IMAGE_API(eo, NULL);
   return _evas_image_proxy_source_get(eo);
}

EVAS_API Eina_Bool
evas_object_image_source_unset(Evas_Object *eo_obj)
{
   EVAS_IMAGE_API(eo_obj, EINA_FALSE);
   return _evas_image_proxy_source_set(eo_obj, NULL);
}

EVAS_API void
evas_object_image_source_clip_set(Evas_Object *eo, Eina_Bool source_clip)
{
   EVAS_IMAGE_API(eo);
   _evas_image_proxy_source_clip_set(eo, source_clip);
}

EVAS_API Eina_Bool
evas_object_image_source_clip_get(const Evas_Object *eo)
{
   EVAS_IMAGE_API(eo, EINA_FALSE);
   return _evas_image_proxy_source_clip_get(eo);
}

EVAS_API void
evas_object_image_source_events_set(Evas_Object *eo, Eina_Bool repeat)
{
   EVAS_IMAGE_API(eo);
   _evas_image_proxy_source_events_set(eo, repeat);
}

EVAS_API Eina_Bool
evas_object_image_source_events_get(const Evas_Object *eo)
{
   EVAS_IMAGE_API(eo, EINA_FALSE);
   return _evas_image_proxy_source_events_get(eo);
}

EVAS_API void
evas_object_image_content_hint_set(Evas_Object *obj, Evas_Image_Content_Hint hint)
{
   EVAS_IMAGE_API(obj);
   efl_gfx_image_content_hint_set(obj, (Efl_Gfx_Image_Content_Hint)hint);
}

EVAS_API Evas_Image_Content_Hint
evas_object_image_content_hint_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_IMAGE_CONTENT_HINT_NONE);
   return (Evas_Image_Content_Hint)efl_gfx_image_content_hint_get(obj);
}

EVAS_API void
evas_object_image_scale_hint_set(Evas_Object *obj, Evas_Image_Scale_Hint hint)
{
   EVAS_IMAGE_API(obj);
   return efl_gfx_image_scale_hint_set(obj, (Efl_Gfx_Image_Scale_Hint) hint);
}

EVAS_API Evas_Image_Scale_Hint
evas_object_image_scale_hint_get(const Evas_Object *obj)
{
   EVAS_IMAGE_API(obj, EVAS_IMAGE_SCALE_HINT_NONE);
   return (Evas_Image_Scale_Hint) efl_gfx_image_scale_hint_get(obj);
}

EVAS_API void
evas_object_image_native_surface_set(Evas_Object *eo_obj, Evas_Native_Surface *surf)
{
   EVAS_IMAGE_API(eo_obj);

   Eina_Bool ret;

   ret = _evas_image_native_surface_set(eo_obj, surf);

   if (surf && !ret)
     {
        Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

        o->load_error = EFL_GFX_IMAGE_LOAD_ERROR_GENERIC;
     }
}

EVAS_API Evas_Native_Surface *
evas_object_image_native_surface_get(const Evas_Object *eo_obj)
{
   EVAS_IMAGE_API(eo_obj, NULL);
   return _evas_image_native_surface_get(eo_obj);
}

EVAS_API void
evas_object_image_pixels_get_callback_set(Eo *eo_obj, Evas_Object_Image_Pixels_Get_Cb func, void *data)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   evas_object_async_block(obj);
   EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
     {
        pixi_write->func.get_pixels = func;
        pixi_write->func.get_pixels_data = data;
     }
   EINA_COW_PIXEL_WRITE_END(o, pixi_write);
}

EVAS_API void
evas_object_image_pixels_dirty_set(Eo *eo_obj, Eina_Bool dirty)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   evas_object_async_block(obj);
   if (dirty)
     {
        o->dirty_pixels = EINA_TRUE;
        o->changed = EINA_TRUE;
     }
   else o->dirty_pixels = EINA_FALSE;

   evas_object_change(eo_obj, obj);
}

EVAS_API Eina_Bool
evas_object_image_pixels_dirty_get(const Eo *eo_obj)
{
   EVAS_IMAGE_API(eo_obj, EINA_FALSE);

   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   return (o->dirty_pixels ? 1 : 0);
}

EVAS_API void
evas_object_image_data_set(Eo *eo_obj, void *data)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   void *p_data, *pixels;
   Eina_Bool resize_call = EINA_FALSE;


   evas_object_async_block(obj);
   evas_render_rendering_wait(obj->layer->evas);

   _evas_image_cleanup(eo_obj, obj, o);
   p_data = o->engine_data;
   if (data)
     {
        // r/o FBO data_get: only free the image, don't update pixels
        if ((pixels = eina_hash_find(o->pixels->images_to_free, data)) != NULL)
          {
             eina_hash_del(o->pixels->images_to_free, data, pixels);
             return;
          }

        if (o->engine_data)
          {
             o->engine_data = ENFN->image_data_put(ENC, o->engine_data, data);
          }
        else
          {
             o->engine_data = ENFN->image_new_from_data(ENC,
                                                        o->cur->image.w,
                                                        o->cur->image.h,
                                                        data,
                                                        o->cur->has_alpha,
                                                        o->cur->cspace);
          }
        if (o->engine_data)
          {
             int stride = 0;

             if (ENFN->image_scale_hint_set)
               ENFN->image_scale_hint_set(ENC, o->engine_data, o->scale_hint);

             if (ENFN->image_content_hint_set)
               ENFN->image_content_hint_set(ENC, o->engine_data, o->content_hint);

             if (ENFN->image_stride_get)
               ENFN->image_stride_get(ENC, o->engine_data, &stride);
             else
               stride = o->cur->image.w * 4;

             if (o->cur->image.stride != stride)
               {
                  EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
                    state_write->image.stride = stride;
                  EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
               }
         }
       o->written = EINA_TRUE;
     }
   else
     {
        if (o->engine_data)
          {
             ENFN->image_free(ENC, o->engine_data);
             o->engine_data = NULL;
             o->changed = EINA_TRUE;
             evas_object_change(eo_obj, obj);
          }
        o->load_error = EFL_GFX_IMAGE_LOAD_ERROR_NONE;
        if ((o->cur->image.w != 0) || (o->cur->image.h != 0))
          resize_call = EINA_TRUE;

        EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
          {
             state_write->image.w = 0;
             state_write->image.h = 0;
             state_write->image.stride = 0;
          }
        EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
     }
/* FIXME - in engine call above
   if (o->engine_data)
     o->engine_data = ENFN->image_alpha_set(ENC, o->engine_data, o->cur->has_alpha);
*/
   if (o->pixels_checked_out > 0) o->pixels_checked_out--;
   if (p_data != o->engine_data)
     {
        o->pixels_checked_out = 0;
     }
   if (resize_call) evas_object_inform_call_image_resize(eo_obj);
}

static void
_image_to_free_del_cb(void *data)
{
   Evas_Image_Legacy_Pixels_Entry *px_entry = data;
   Evas_Object_Protected_Data *obj;

   obj = efl_data_scope_safe_get(px_entry->object, EFL_CANVAS_OBJECT_CLASS);
   EINA_SAFETY_ON_NULL_RETURN(obj);
   ENFN->image_free(ENC, px_entry->image);
   free(px_entry);
}

EVAS_API void*
evas_object_image_data_get(const Eo *eo_obj, Eina_Bool for_writing)
{
   EVAS_IMAGE_API(eo_obj, NULL);

   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   Evas_Image_Legacy_Pixels_Entry *px_entry = NULL;
   Eina_Bool tofree = 0;
   void *pixels = NULL;
   int stride = 0;
   DATA32 *data;
   int load_error;

   if (!o->engine_data) return NULL;

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);

   if (for_writing) evas_object_async_block(obj);
   if (for_writing) evas_render_rendering_wait(obj->layer->evas);

   data = NULL;
   if (ENFN->image_scale_hint_set)
     ENFN->image_scale_hint_set(ENC, o->engine_data, o->scale_hint);
   if (ENFN->image_content_hint_set)
     ENFN->image_content_hint_set(ENC, o->engine_data, o->content_hint);
   pixels = ENFN->image_data_get(ENC, o->engine_data, for_writing, &data, &load_error, &tofree);
   o->load_error = _evas_load_error_to_efl_gfx_image_load_error(load_error);

   /* if we fail to get engine_data, we have to return NULL */
   if (!pixels || !data) goto error;

   if (!tofree)
     {
        o->engine_data = pixels;
        if (ENFN->image_stride_get)
          ENFN->image_stride_get(ENC, o->engine_data, &stride);
        else
           stride = o->cur->image.w * 4;

        if (o->cur->image.stride != stride)
          {
             EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
                   state_write->image.stride = stride;
             EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
          }

        o->pixels_checked_out++;
     }
   else
     {
        Eina_Hash *hash = o->pixels->images_to_free;

        if (!hash)
          {
             hash = eina_hash_pointer_new(_image_to_free_del_cb);
             if (!hash) goto error;
             EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
               pixi_write->images_to_free = hash;
             EINA_COW_PIXEL_WRITE_END(o, pixi_write);
          }

        px_entry = calloc(1, sizeof(*px_entry));
        px_entry->object = (Eo *) eo_obj;
        px_entry->image = pixels;
        if (!eina_hash_add(hash, data, px_entry))
          goto error;
     }

   if (for_writing)
     {
        o->written = EINA_TRUE;
     }

   return data;

error:
   free(px_entry);
   if (tofree && pixels)
     ENFN->image_free(ENC, pixels);
   return NULL;
}

EVAS_API void
evas_object_image_data_copy_set(Eo *eo_obj, void *data)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   if (!data) return;
   evas_object_async_block(obj);
   _evas_image_cleanup(eo_obj, obj, o);
   if ((o->cur->image.w <= 0) ||
       (o->cur->image.h <= 0)) return;
   if (o->engine_data)
     ENFN->image_free(ENC, o->engine_data);
   o->engine_data = ENFN->image_new_from_copied_data(ENC,
                                                     o->cur->image.w,
                                                     o->cur->image.h,
                                                     data,
                                                     o->cur->has_alpha,
                                                     o->cur->cspace);
   if (o->engine_data)
     {
        int stride = 0;

        o->engine_data =
          ENFN->image_alpha_set(ENC, o->engine_data, o->cur->has_alpha);
        if (ENFN->image_scale_hint_set)
          ENFN->image_scale_hint_set(ENC, o->engine_data, o->scale_hint);
        if (ENFN->image_content_hint_set)
          ENFN->image_content_hint_set(ENC, o->engine_data, o->content_hint);
        if (ENFN->image_stride_get)
          ENFN->image_stride_get(ENC, o->engine_data, &stride);
        else
          stride = o->cur->image.w * 4;

        if (o->cur->image.stride != stride)
          {
             EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
               state_write->image.stride = stride;
             EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);
          }
        o->written = EINA_TRUE;
     }
   o->pixels_checked_out = 0;
}

/* Evas_Object equivalent: pixels_set(null, w, h, cspace) to (re)allocate an image */
EVAS_API void
evas_object_image_size_set(Evas_Object *eo_obj, int w, int h)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   int stride = 0;

   evas_object_async_block(obj);
   _evas_image_cleanup(eo_obj, obj, o);
   if (w < 1) w = 1;
   if (h < 1) h = 1;
   if (w >= 32768) return;
   if (h >= 32768) return;
   if ((w == o->cur->image.w) &&
       (h == o->cur->image.h)) return;

   EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
     {
        state_write->image.w = w;
        state_write->image.h = h;
     }
   EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);

   if (o->engine_data)
      o->engine_data = ENFN->image_size_set(ENC, o->engine_data, w, h);
   else
      o->engine_data = ENFN->image_new_from_copied_data
        (ENC, w, h, NULL, o->cur->has_alpha, o->cur->cspace);

   if (o->engine_data)
     {
        if (ENFN->image_scale_hint_set)
           ENFN->image_scale_hint_set(ENC, o->engine_data, o->scale_hint);
        if (ENFN->image_content_hint_set)
           ENFN->image_content_hint_set(ENC, o->engine_data, o->content_hint);
        if (ENFN->image_stride_get)
           ENFN->image_stride_get(ENC, o->engine_data, &stride);
        else
           stride = w * 4;
     }
   else
      stride = w * 4;
   EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, cur_write)
     {
        cur_write->image.stride = stride;

/* FIXME - in engine call above
   if (o->engine_data)
     o->engine_data = ENFN->image_alpha_set(ENC, o->engine_data, o->cur->has_alpha);
*/
        EINA_COW_WRITE_BEGIN(evas_object_image_state_cow, o->prev, Evas_Object_Image_State, prev_write)
          EVAS_OBJECT_IMAGE_FREE_FILE_AND_KEY(cur_write, prev_write);
        EINA_COW_WRITE_END(evas_object_image_state_cow, o->prev, prev_write);
     }
   EINA_COW_IMAGE_STATE_WRITE_END(o, cur_write);

   o->written = EINA_TRUE;
   o->changed = EINA_TRUE;
   evas_object_inform_call_image_resize(eo_obj);
   evas_object_change(eo_obj, obj);
}

/* Evas_Object equivalent: pixels_set(null, w, h, cspace) to (re)allocate an image */
EVAS_API void
evas_object_image_colorspace_set(Evas_Object *eo_obj, Evas_Colorspace cspace)
{
   EVAS_IMAGE_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   evas_object_async_block(obj);
   _evas_image_cleanup(eo_obj, obj, o);

   EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write)
     state_write->cspace = cspace;
   EINA_COW_IMAGE_STATE_WRITE_END(o, state_write);

   if (o->engine_data)
     ENFN->image_colorspace_set(ENC, o->engine_data, cspace);
}

/* old video surfaces */

EVAS_API void
evas_object_image_video_surface_set(Evas_Object *eo_obj, Evas_Video_Surface *surf)
{
   EVAS_IMAGE_LEGACY_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   evas_object_async_block(obj);

   _evas_image_cleanup(eo_obj, obj, o);
   if (o->video_surface)
     {
        o->video_surface = EINA_FALSE;
        obj->layer->evas->video_objects = eina_list_remove(obj->layer->evas->video_objects, eo_obj);
     }

   if (surf)
     {
        if (surf->version != EVAS_VIDEO_SURFACE_VERSION) return;

        if (!surf->update_pixels ||
            !surf->move ||
            !surf->resize ||
            !surf->hide ||
            !surf->show)
          return;

        o->created = EINA_TRUE;
        o->video_surface = EINA_TRUE;

        EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
          pixi_write->video = *surf;
        EINA_COW_PIXEL_WRITE_END(o, pixi_write)

        obj->layer->evas->video_objects = eina_list_append(obj->layer->evas->video_objects, eo_obj);
     }
   else
     {
        if (!o->video_surface &&
            !o->pixels->video.update_pixels &&
            !o->pixels->video.move &&
            !o->pixels->video.resize &&
            !o->pixels->video.hide &&
            !o->pixels->video.show &&
            !o->pixels->video.data)
          return;

        o->video_surface = EINA_FALSE;
        EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
          {
             pixi_write->video.update_pixels = NULL;
             pixi_write->video.move = NULL;
             pixi_write->video.resize = NULL;
             pixi_write->video.hide = NULL;
             pixi_write->video.show = NULL;
             pixi_write->video.data = NULL;
          }
        EINA_COW_PIXEL_WRITE_END(o, pixi_write)
     }
}

EVAS_API const Evas_Video_Surface*
evas_object_image_video_surface_get(const Evas_Object *eo_obj)
{
   EVAS_IMAGE_LEGACY_API(eo_obj, NULL);

   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   return (!o->video_surface ? NULL : &o->pixels->video);
}

EVAS_API void
evas_object_image_video_surface_caps_set(Evas_Object *eo_obj, unsigned int caps)
{
   EVAS_IMAGE_LEGACY_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   evas_object_async_block(obj);

   _evas_image_cleanup(eo_obj, obj, o);

   if (caps == o->pixels->video_caps)
     return;

   EINA_COW_PIXEL_WRITE_BEGIN(o, pixi_write)
     pixi_write->video_caps = caps;
   EINA_COW_PIXEL_WRITE_END(o, pixi_write)
}

EVAS_API unsigned int
evas_object_image_video_surface_caps_get(const Evas_Object *eo_obj)
{
   EVAS_IMAGE_LEGACY_API(eo_obj, 0);

   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);

   /* The generic hardware plane code calls this function on
    * non-video surfaces, return stacking check for those to
    * allow them to use common video surface code */
   return (!o->video_surface ? EVAS_VIDEO_SURFACE_STACKING_CHECK : o->pixels->video_caps);
}

/* deprecated */
EVAS_API void
evas_object_image_fill_spread_set(Evas_Object *obj EINA_UNUSED, Evas_Fill_Spread spread)
{
   /* not implemented! */
   if (spread != EFL_GFX_FILL_REPEAT)
     WRN("Fill spread support is not implemented!");
}

/* deprecated */
EVAS_API Evas_Fill_Spread
evas_object_image_fill_spread_get(const Evas_Object *obj EINA_UNUSED)
{
   return EFL_GFX_FILL_REPEAT;
}

/* deprecated */
EVAS_API void
evas_object_image_source_visible_set(Evas_Object *eo, Eina_Bool visible)
{
   /* FIXME: I'd love to remove this feature and replace by no_render.
    * But they are not 100% equivalent: if all proxies are removed, then the
    * source becomes visible again. This has some advantages for some apps but
    * it's complete hell to handle in evas render side.
    * -- jpeg, 2016/03/07
    */

   EVAS_IMAGE_LEGACY_API(eo);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object_Protected_Data *src_obj;
   Evas_Image_Data *o;

   o = efl_data_scope_get(eo, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if (!o->cur->source) return;

   visible = !!visible;
   src_obj = efl_data_scope_get(o->cur->source, EFL_CANVAS_OBJECT_CLASS);
   if (src_obj->proxy->src_invisible == !visible) return;

   evas_object_async_block(obj);
   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src_obj->proxy, Evas_Object_Proxy_Data, proxy_write)
     proxy_write->src_invisible = !visible;
   EINA_COW_WRITE_END(evas_object_proxy_cow, src_obj->proxy, proxy_write);

   src_obj->changed_src_visible = EINA_TRUE;
   evas_object_smart_member_cache_invalidate(o->cur->source, EINA_FALSE,
                                             EINA_FALSE, EINA_TRUE);
   evas_object_change(o->cur->source, src_obj);
   if ((!visible) || (!src_obj->proxy->src_events)) return;
   //FIXME: Feed mouse events here.
}

/* deprecated */
EVAS_API Eina_Bool
evas_object_image_source_visible_get(const Evas_Object *eo)
{
   /* FIXME: see evas_object_image_source_visible_set */

   EVAS_IMAGE_LEGACY_API(eo, EINA_FALSE);

   Evas_Object_Protected_Data *src_obj;
   Evas_Image_Data *o;
   Eina_Bool visible;

   o = efl_data_scope_get(eo, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if (!o->cur->source) visible = EINA_FALSE;
   src_obj = efl_data_scope_get(o->cur->source, EFL_CANVAS_OBJECT_CLASS);
   if (src_obj) visible = !src_obj->proxy->src_invisible;
   else visible = EINA_FALSE;

   return visible;
}

/* deprecated */
EVAS_API void*
evas_object_image_data_convert(Evas_Object *eo_obj, Evas_Colorspace to_cspace)
{
   EVAS_IMAGE_LEGACY_API(eo_obj, NULL);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o;
   void *engine_data;
   DATA32 *data;
   void* result = NULL;
   int load_error;

   static int warned = 0;
   if (!warned)
     {
        ERR("%s is deprecated and shouldn't be called", __func__);
        warned = 1;
     }

   evas_object_async_block(obj);
   o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if (!o->engine_data) return NULL;
   if (o->video_surface)
     o->pixels->video.update_pixels(o->pixels->video.data, eo_obj, &o->pixels->video);
   if (o->cur->cspace == to_cspace) return NULL;
   if ((o->preload & EVAS_IMAGE_PRELOADING) && (o->engine_data))
     {
        o->preload = EVAS_IMAGE_PRELOAD_NONE;
        ENFN->image_data_preload_cancel(ENC, o->engine_data, eo_obj, EINA_TRUE);
     }
   data = NULL;
   engine_data = ENFN->image_data_get(ENC, o->engine_data, 0, &data, &load_error, NULL);
   o->load_error = _evas_load_error_to_efl_gfx_image_load_error(load_error);
   result = _evas_image_data_convert_internal(o, data, to_cspace);
   if (engine_data)
     o->engine_data = ENFN->image_data_put(ENC, engine_data, data);

   return result;
}

/* deprecated */
EVAS_API void
evas_object_image_reload(Evas_Object *eo_obj)
{
   EVAS_IMAGE_LEGACY_API(eo_obj);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o;

   evas_object_async_block(obj);
   o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if ((!o->cur->f) || (o->pixels_checked_out > 0)) return;
   if ((o->preload & EVAS_IMAGE_PRELOADING) && (o->engine_data))
     {
        o->preload = EVAS_IMAGE_PRELOAD_NONE;
        ENFN->image_data_preload_cancel(ENC, o->engine_data, eo_obj, EINA_TRUE);
     }
   if (o->engine_data)
     o->engine_data = ENFN->image_dirty_region(ENC, o->engine_data, 0, 0, o->cur->image.w, o->cur->image.h);

   eina_file_refresh(o->cur->f);
   o->written = EINA_FALSE;

   _evas_image_unload(eo_obj, obj, 1);
   evas_object_inform_call_image_unloaded(eo_obj);
   _evas_image_load(eo_obj, obj, o);

   EINA_COW_WRITE_BEGIN(evas_object_image_state_cow, o->prev, Evas_Object_Image_State, prev_write)
     {
        prev_write->f = NULL;
        prev_write->key = NULL;
     }
   EINA_COW_WRITE_END(evas_object_image_state_cow, o->prev, prev_write);

   o->changed = EINA_TRUE;
   evas_object_change(eo_obj, obj);
}

/* deprecated */
EVAS_API Eina_Bool
evas_object_image_pixels_import(Evas_Object *eo_obj, Evas_Pixel_Import_Source *pixels)
{
   EVAS_IMAGE_LEGACY_API(eo_obj, EINA_FALSE);

   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Image_Data *o;
   int load_error;

   static int warned = 0;
   if (!warned)
     {
        ERR("%s is deprecated and shouldn't be called", __func__);
        warned = 1;
     }

   evas_object_async_block(obj);
   o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   _evas_image_cleanup(eo_obj, obj, o);
   if ((pixels->w != o->cur->image.w) || (pixels->h != o->cur->image.h)) return EINA_FALSE;

   switch (pixels->format)
     {
#if 0
      case EVAS_PIXEL_FORMAT_ARGB32:
          {
             if (o->engine_data)
               {
                  DATA32 *image_pixels = NULL;

                  o->engine_data =
                    ENFN->image_data_get(ENC,
                                         o->engine_data,
                                         1,
                                         &image_pixels,
                                         &load_error);
                  o->load_error = _evas_load_error_to_efl_gfx_image_load_error(load_error);
/* FIXME: need to actualyl support this */
/*		  memcpy(image_pixels, pixels->rows, o->cur->image.w * o->cur->image.h * 4);*/
                  if (o->engine_data)
                    o->engine_data =
                    ENFN->image_data_put(ENC, o->engine_data, image_pixels);
                  if (o->engine_data)
                    o->engine_data =
                    ENFN->image_alpha_set(ENC, o->engine_data, o->cur->has_alpha);
                  o->changed = EINA_TRUE;
                  evas_object_change(eo_obj, obj);
               }
          }
        break;
#endif
      case EVAS_PIXEL_FORMAT_YUV420P_601:
          {
             if (o->engine_data)
               {
                  DATA32 *image_pixels = NULL;

                  o->engine_data = ENFN->image_data_get(ENC, o->engine_data, 1, &image_pixels, &load_error, NULL);
                  o->load_error = _evas_load_error_to_efl_gfx_image_load_error(load_error);
                  if (image_pixels)
                    evas_common_convert_yuv_422p_601_rgba((DATA8 **) pixels->rows, (DATA8 *) image_pixels, o->cur->image.w, o->cur->image.h);
                  if (o->engine_data)
                    o->engine_data = ENFN->image_data_put(ENC, o->engine_data, image_pixels);
                  if (o->engine_data)
                    o->engine_data = ENFN->image_alpha_set(ENC, o->engine_data, o->cur->has_alpha);
                  o->changed = EINA_TRUE;
                  evas_object_change(eo_obj, obj);
               }
          }
        break;
      default:
        return EINA_FALSE;
        break;
     }
   return EINA_TRUE;
}

EVAS_API Eina_Bool
evas_image_max_size_get(Eo *eo_e, int *w, int *h)
{
   Eina_Size2D size;
   Eina_Bool ret;

   ret = efl_canvas_scene_image_max_size_get(eo_e, &size);
   if (ret)
     {
        if (w) *w = size.w;
        if (h) *h = size.h;
     }
   return ret;
}

/* deprecated */
EVAS_API void
evas_object_image_alpha_mask_set(Evas_Object *eo_obj EINA_UNUSED, Eina_Bool ismask EINA_UNUSED)
{
   WRN("This function is not implemented, has never been and never will be.");
   EVAS_IMAGE_LEGACY_API(eo_obj);
}

EOLIAN static Eina_Bool
_evas_image_efl_file_loaded_get(const Eo *eo_obj, void *_pd EINA_UNUSED)
{
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if (!o->skip_head)
     return efl_file_loaded_get(efl_super(eo_obj, EVAS_IMAGE_CLASS));
   return !!o->cur->f;
}

EOLIAN static const Eina_File *
_evas_image_efl_file_mmap_get(const Eo *eo_obj, void *_pd EINA_UNUSED)
{
   Evas_Image_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   if (!o->skip_head)
     return efl_file_mmap_get(efl_super(eo_obj, EVAS_IMAGE_CLASS));
   return o->cur->f;
}

EOLIAN static Eina_Error
_evas_image_efl_file_load(Eo *obj, void *pd EINA_UNUSED)
{
   EVAS_IMAGE_API(obj, EINA_FALSE);
   if (efl_file_loaded_get(obj)) return 0;
   Evas_Image_Data *o = efl_data_scope_get(obj, EFL_CANVAS_IMAGE_INTERNAL_CLASS);
   Eina_Error err = 0;

   if (!o->skip_head)
     err = efl_file_load(efl_super(obj, EVAS_IMAGE_CLASS));
   if (err)
     {
        if (err == ENOENT)
          _efl_canvas_image_load_error_set(obj, EFL_GFX_IMAGE_LOAD_ERROR_DOES_NOT_EXIST);
        else if (err == ENOMEM)
          _efl_canvas_image_load_error_set(obj, EFL_GFX_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
        else if ((err == EPERM) || (err == EACCES))
          _efl_canvas_image_load_error_set(obj, EFL_GFX_IMAGE_LOAD_ERROR_PERMISSION_DENIED);
        else
          _efl_canvas_image_load_error_set(obj, EFL_GFX_IMAGE_LOAD_ERROR_GENERIC);
        return err;
     }
   if (_evas_image_file_load(obj, o))
     return 0;
   return EFL_GFX_IMAGE_LOAD_ERROR_DOES_NOT_EXIST;
}

EOLIAN static void
_evas_image_efl_file_unload(Eo *obj, void *pd EINA_UNUSED)
{
   EVAS_IMAGE_API(obj);
   efl_file_unload(efl_super(obj, EVAS_IMAGE_CLASS));
   _evas_image_file_unload(obj);
}

#include "canvas/evas_image_eo.c"
