From fd4e133cc1af3351b79946cfb1bc6d79feec9d30 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Thu, 2 Jul 2015 11:52:16 +0900 Subject: [PATCH] Evas: Implement image_data_get for FBO images --- src/lib/evas/canvas/evas_object_image.c | 62 +++---- src/lib/evas/include/evas_private.h | 2 +- .../evas/engines/gl_generic/evas_engine.c | 151 +++++++++++------- .../engines/software_generic/evas_engine.c | 17 +- 4 files changed, 136 insertions(+), 96 deletions(-) diff --git a/src/lib/evas/canvas/evas_object_image.c b/src/lib/evas/canvas/evas_object_image.c index 7134783111..e18c58f593 100644 --- a/src/lib/evas/canvas/evas_object_image.c +++ b/src/lib/evas/canvas/evas_object_image.c @@ -1216,6 +1216,8 @@ _evas_image_data_convert(Eo *eo_obj, Evas_Image_Data *o, Evas_Colorspace to_cspa DATA32 *data; void* result = NULL; + // FIXME: This function is not really useful, and can't work with GL. + evas_object_async_block(obj); if ((o->preloading) && (o->engine_data)) { @@ -1227,7 +1229,7 @@ _evas_image_data_convert(Eo *eo_obj, Evas_Image_Data *o, Evas_Colorspace to_cspa o->pixels->video.update_pixels(o->pixels->video.data, eo_obj, &o->pixels->video); if (o->cur->cspace == to_cspace) return NULL; data = NULL; - o->engine_data = ENFN->image_data_get(ENDT, o->engine_data, 0, &data, &o->load_error); + o->engine_data = ENFN->image_data_get(ENDT, o->engine_data, 0, &data, &o->load_error, NULL); result = evas_object_image_data_convert_internal(o, data, to_cspace); if (o->engine_data) { @@ -1324,6 +1326,8 @@ EOLIAN static void* _evas_image_data_get(const Eo *eo_obj, Evas_Image_Data *_pd EINA_UNUSED, Eina_Bool for_writing) { Evas_Image_Data *o = (Evas_Image_Data *) _pd; + int stride = 0; + void *pixels; DATA32 *data; if (!o->engine_data) return NULL; @@ -1338,27 +1342,24 @@ _evas_image_data_get(const Eo *eo_obj, Evas_Image_Data *_pd EINA_UNUSED, Eina_Bo ENFN->image_scale_hint_set(ENDT, o->engine_data, o->scale_hint); if (ENFN->image_content_hint_set) ENFN->image_content_hint_set(ENDT, o->engine_data, o->content_hint); - o->engine_data = ENFN->image_data_get(ENDT, o->engine_data, for_writing, &data, &o->load_error); + pixels = ENFN->image_data_get(ENDT, o->engine_data, for_writing, &data, &o->load_error, NULL); /* if we fail to get engine_data, we have to return NULL */ - if (!o->engine_data) return NULL; + if (!pixels) return NULL; - if (o->engine_data) + o->engine_data = pixels; + if (ENFN->image_stride_get) + ENFN->image_stride_get(ENDT, o->engine_data, &stride); + else + stride = o->cur->image.w * 4; + + if (o->cur->image.stride != stride) { - int stride = 0; - - if (ENFN->image_stride_get) - ENFN->image_stride_get(ENDT, 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); - } + 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++; if (for_writing) { @@ -1628,8 +1629,8 @@ _evas_image_efl_file_save(const Eo *eo_obj, Evas_Image_Data *o, const char *file DATA32 *data = NULL; int quality = 80, compress = 9, ok = 0; char *encoding = NULL; - RGBA_Image *im; - Eina_Bool putback = EINA_FALSE; + Image_Entry *ie; + Eina_Bool putback = EINA_FALSE, tofree = EINA_FALSE; int imagew, imageh; void *pixels; @@ -1679,7 +1680,7 @@ _evas_image_efl_file_save(const Eo *eo_obj, Evas_Image_Data *o, const char *file o->proxyrendering = EINA_FALSE; } - pixels = ENFN->image_data_get(ENDT, pixels, 0, &data, &o->load_error); + pixels = ENFN->image_data_get(ENDT, pixels, 0, &data, &o->load_error, &tofree); if (!pixels) { @@ -1706,11 +1707,12 @@ _evas_image_efl_file_save(const Eo *eo_obj, Evas_Image_Data *o, const char *file else break; } } - im = (RGBA_Image*) evas_cache_image_data(evas_common_image_cache_get(), - imagew, imageh, data, o->cur->has_alpha, - EVAS_COLORSPACE_ARGB8888); - if (im) + ie = evas_cache_image_data(evas_common_image_cache_get(), + imagew, imageh, data, o->cur->has_alpha, + EVAS_COLORSPACE_ARGB8888); + if (ie) { + RGBA_Image *im = (RGBA_Image *) ie; if (o->cur->cspace == EVAS_COLORSPACE_ARGB8888) im->image.data = data; else @@ -1722,10 +1724,12 @@ _evas_image_efl_file_save(const Eo *eo_obj, Evas_Image_Data *o, const char *file if (o->cur->cspace != EVAS_COLORSPACE_ARGB8888) free(im->image.data); } - - evas_cache_image_drop(&im->cache_entry); + evas_cache_image_drop(ie); } - if (putback) + + if (tofree) + ENFN->image_free(ENDT, pixels); + else if (putback) o->engine_data = ENFN->image_data_put(ENDT, pixels, data); free(encoding); @@ -1737,6 +1741,8 @@ _evas_image_pixels_import(Eo *eo_obj, Evas_Image_Data *o, Evas_Pixel_Import_Sour { Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); + // FIXME: This function is not really useful, and can't work with GL. + evas_object_async_block(obj); _evas_object_image_cleanup(eo_obj, obj, o); if ((pixels->w != o->cur->image.w) || (pixels->h != o->cur->image.h)) return EINA_FALSE; @@ -1776,7 +1782,7 @@ _evas_image_pixels_import(Eo *eo_obj, Evas_Image_Data *o, Evas_Pixel_Import_Sour { DATA32 *image_pixels = NULL; - o->engine_data = ENFN->image_data_get(ENDT, o->engine_data, 1, &image_pixels,&o->load_error); + o->engine_data = ENFN->image_data_get(ENDT, o->engine_data, 1, &image_pixels,&o->load_error, NULL); 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) diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 24f70cd9cc..32e899c436 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -1301,7 +1301,7 @@ struct _Evas_Func void *(*image_size_set) (void *data, void *image, int w, int h); void (*image_stride_get) (void *data, void *image, int *stride); void *(*image_dirty_region) (void *data, void *image, int x, int y, int w, int h); - void *(*image_data_get) (void *data, void *image, int to_write, DATA32 **image_data, int *err); + void *(*image_data_get) (void *data, void *image, int to_write, DATA32 **image_data, int *err, Eina_Bool *tofree); void *(*image_data_put) (void *data, void *image, DATA32 *image_data); void (*image_data_preload_request) (void *data, void *image, const Eo *target); void (*image_data_preload_cancel) (void *data, void *image, const Eo *target); diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c index 35005ea140..3d2271e1de 100644 --- a/src/modules/evas/engines/gl_generic/evas_engine.c +++ b/src/modules/evas/engines/gl_generic/evas_engine.c @@ -53,8 +53,11 @@ static int _evas_engine_GL_log_dom = -1; # endif #endif -static int eng_gl_image_direct_get(void *data EINA_UNUSED, void *image); +static int eng_gl_image_direct_get(void *data, void *image); static int eng_gl_surface_destroy(void *data, void *surface); +static Eina_Bool eng_gl_surface_lock(void *data, void *surface); +static Eina_Bool eng_gl_surface_unlock(void *data, void *surface); +static Eina_Bool eng_gl_surface_read_pixels(void *data, void *surface, int x, int y, int w, int h, Evas_Colorspace cspace, void *pixels); static void eng_rectangle_draw(void *data, void *context, void *surface, int x, int y, int w, int h, Eina_Bool do_async EINA_UNUSED) @@ -573,23 +576,20 @@ eng_image_dirty_region(void *data, void *image, int x, int y, int w, int h) } static Evas_GL_Image * -_rotate_image_data(void *data, void *img) +_rotate_image_data(Render_Engine_GL_Generic *re, Evas_GL_Image *im1) { int alpha; - Evas_GL_Image *im1, *im2; + Evas_GL_Image *im2; Evas_Engine_GL_Context *gl_context; - Render_Engine_GL_Generic *re = data; RGBA_Draw_Context *dc; - DATA32 *pixels; int w, h; re->window_use(re->software.ob); gl_context = re->window_gl_context_get(re->software.ob); - im1 = img; w = im1->w; h = im1->h; - alpha = eng_image_alpha_get(data, img); + alpha = eng_image_alpha_get(re, im1); if (im1->orient == EVAS_IMAGE_ORIENT_90 || im1->orient == EVAS_IMAGE_ORIENT_270 || @@ -614,71 +614,51 @@ _rotate_image_data(void *data, void *img) 0, 0, w, h, 0, 0, im2->w, im2->h, 0); - // Do not forget to flush everything or you will have nothing in your buffer - evas_gl_common_context_flush(gl_context); gl_context->dc = NULL; evas_common_draw_context_free(dc); - glsym_glBindFramebuffer(GL_FRAMEBUFFER, im2->tex->pt->fb); + // flush everything + eng_gl_surface_lock(re, im2); // Rely on Evas_GL_Image infrastructure to allocate pixels im2->im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); if (!im2->im) return NULL; im2->im->cache_entry.flags.alpha = !!alpha; evas_gl_common_image_alloc_ensure(im2); - pixels = im2->im->image.data; - if (im2->tex->pt->format == GL_BGRA) - { - glReadPixels(0, 0, im2->w, im2->h, GL_BGRA, - GL_UNSIGNED_BYTE, pixels); - } - else - { - DATA32 *ptr = pixels; - unsigned int k; - - glReadPixels(0, 0, im2->w, im2->h, GL_RGBA, - GL_UNSIGNED_BYTE, pixels); - for (k = im2->w * im2->h; k; --k) - { - const DATA32 v = *ptr; - *ptr++ = (v & 0xFF00FF00) - | ((v & 0x00FF0000) >> 16) - | ((v & 0x000000FF) << 16); - } - } - - glsym_glBindFramebuffer(GL_FRAMEBUFFER, 0); + eng_gl_surface_read_pixels(re, im2, 0, 0, im2->w, im2->h, + EVAS_COLORSPACE_ARGB8888, im2->im->image.data); + eng_gl_surface_unlock(re, im2); return im2; } static void * -eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, int *err) +eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, int *err, Eina_Bool *tofree) { Render_Engine_GL_Generic *re = data; - Evas_GL_Image *im; + Evas_GL_Image *im_new = NULL; + Evas_GL_Image *im = image; int error; *image_data = NULL; + if (tofree) tofree = EINA_FALSE; + if (err) *err = EVAS_LOAD_ERROR_NONE; - if (!image) + if (!im) { if (err) *err = EVAS_LOAD_ERROR_GENERIC; return NULL; } - im = image; - if (im->native.data) - { - if (err) *err = EVAS_LOAD_ERROR_NONE; - return im; - } - if (im->orient != EVAS_IMAGE_ORIENT_NONE) + if (im->native.data) + return im; + + if (im->im && + im->orient != EVAS_IMAGE_ORIENT_NONE) { - Evas_GL_Image *im_new = _rotate_image_data(data, image); + im_new = _rotate_image_data(data, image); if (!im_new) { if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; @@ -694,13 +674,14 @@ eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, i re->window_use(re->software.ob); if ((im->tex) && (im->tex->pt) && (im->tex->pt->dyn.img) && - (im->cs.space == EVAS_COLORSPACE_ARGB8888)) + (im->cs.space == EVAS_COLORSPACE_ARGB8888) && + secsym_tbm_surface_map && + secsym_eglMapImageSEC) { if (im->tex->pt->dyn.checked_out > 0) { im->tex->pt->dyn.checked_out++; *image_data = im->tex->pt->dyn.data; - if (err) *err = EVAS_LOAD_ERROR_NONE; return im; } if (im->gc->shared->info.sec_tbm_surface) @@ -734,22 +715,73 @@ eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, i if ((im->tex) && (im->tex->pt) && (im->tex->pt->dyn.data)) { *image_data = im->tex->pt->dyn.data; - if (err) *err = EVAS_LOAD_ERROR_NONE; return im; } re->window_use(re->software.ob); #endif + /* use glReadPixels for FBOs (assume fbo > 0) */ + if (!im->im && im->tex && im->tex->pt && im->tex->pt->fb) + { + Eina_Bool ok; + + if (to_write) + { + // This could be implemented, but can't be efficient at all. + // Apps should avoid this situation. + ERR("Can not retrieve image data from FBO to write it back."); + if (err) *err = EVAS_LOAD_ERROR_GENERIC; + return NULL; + } + + if (!tofree) + { + ERR("FBO image must be freed after image_data_get."); + if (err) *err = EVAS_LOAD_ERROR_GENERIC; + return NULL; + } + + ok = eng_gl_surface_lock(data, im); + if (!ok) + { + if (err) *err = EVAS_LOAD_ERROR_GENERIC; + return NULL; + } + + im_new = evas_gl_common_image_new_from_copied_data + (im->gc, im->tex->w, im->tex->h, NULL, + eng_image_alpha_get(data, image), EVAS_COLORSPACE_ARGB8888); + if (!im_new) + { + if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + ok = eng_gl_surface_read_pixels + (data, im, 0, 0, im_new->w, im_new->h, + EVAS_COLORSPACE_ARGB8888, im_new->im->image.data); + eng_gl_surface_unlock(data, im); + if (!ok) + { + if (err) *err = EVAS_LOAD_ERROR_GENERIC; + return NULL; + } + *image_data = im_new->im->image.data; + if (tofree) *tofree = EINA_TRUE; + return im_new; + } + /* Engine can be fail to create texture after cache drop like eng_image_content_hint_set function, so it is need to add code which check im->im's NULL value*/ if (!im->im) - { - *image_data = NULL; - if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; - return NULL; - } + { + // FIXME: Should we create an FBO and draw the texture there, to then read it back? + ERR("GL image has no source data, failed to get pixel data"); + if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } #ifdef EVAS_CSERVE2 if (evas_cserve2_use_get() && evas_cache2_image_cached(&im->im->cache_entry)) @@ -767,8 +799,6 @@ eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, i { if (im->references > 1) { - Evas_GL_Image *im_new; - im_new = evas_gl_common_image_new_from_copied_data (im->gc, im->im->cache_entry.w, im->im->cache_entry.h, im->im->image.data, @@ -776,7 +806,6 @@ eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, i eng_image_colorspace_get(data, image)); if (!im_new) { - *image_data = NULL; if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } @@ -801,7 +830,6 @@ eng_image_data_get(void *data, void *image, int to_write, DATA32 **image_data, i case EVAS_COLORSPACE_ETC1_ALPHA: ERR("This image is encoded in ETC1 or ETC2, not returning any data"); error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; - *image_data = NULL; break; default: abort(); @@ -1599,7 +1627,7 @@ eng_gl_surface_lock(void *data EINA_UNUSED, void *surface) { Evas_GL_Image *im = surface; - if (!im->tex || !im->tex->pt) + if (!im || !im->tex || !im->tex->pt) { ERR("Can not lock image that is not a surface!"); return EINA_FALSE; @@ -1625,7 +1653,7 @@ eng_gl_surface_read_pixels(void *data EINA_UNUSED, void *surface, Evas_Colorspace cspace, void *pixels) { Evas_GL_Image *im = surface; - GLint fmt = GL_BGRA; + GLint fmt = GL_BGRA, fbo = 0; int done = 0; EINA_SAFETY_ON_NULL_RETURN_VAL(pixels, EINA_FALSE); @@ -1647,7 +1675,9 @@ eng_gl_surface_read_pixels(void *data EINA_UNUSED, void *surface, * But some devices don't support GL_BGRA, so we still need to convert. */ - glsym_glBindFramebuffer(GL_FRAMEBUFFER, im->tex->pt->fb); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); + if (fbo != (GLint) im->tex->pt->fb) + glsym_glBindFramebuffer(GL_FRAMEBUFFER, im->tex->pt->fb); glPixelStorei(GL_PACK_ALIGNMENT, 4); // With GLX we will try to read BGRA even if the driver reports RGBA @@ -1676,7 +1706,8 @@ eng_gl_surface_read_pixels(void *data EINA_UNUSED, void *surface, } } - glsym_glBindFramebuffer(GL_FRAMEBUFFER, 0); + if (fbo != (GLint) im->tex->pt->fb) + glsym_glBindFramebuffer(GL_FRAMEBUFFER, fbo); return EINA_TRUE; } diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index b272f08186..8199ab3be7 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -1212,17 +1212,20 @@ eng_image_dirty_region(void *data EINA_UNUSED, void *image, int x, int y, int w, } static void * -eng_image_data_get(void *data EINA_UNUSED, void *image, int to_write, DATA32 **image_data, int *err) +eng_image_data_get(void *data EINA_UNUSED, void *image, int to_write, DATA32 **image_data, int *err, Eina_Bool *tofree) { - RGBA_Image *im; + RGBA_Image *im = image; int error = EVAS_LOAD_ERROR_NONE; - if (!image) + *image_data = NULL; + if (err) *err = EVAS_LOAD_ERROR_NONE; + if (tofree) *tofree = EINA_FALSE; + + if (!im) { - *image_data = NULL; - return NULL; + if (err) *err = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return NULL; } - im = image; #ifdef EVAS_CSERVE2 if (evas_cserve2_use_get() && evas_cache2_image_cached(&im->cache_entry)) @@ -1235,7 +1238,7 @@ eng_image_data_get(void *data EINA_UNUSED, void *image, int to_write, DATA32 **i im = (RGBA_Image *)evas_cache2_image_writable(&im->cache_entry); if (!im) { - *image_data = NULL; + if (err) *err = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } }