#include "evas_gl_private.h" void evas_gl_common_image_all_unload(Evas_Engine_GL_Context *gc) { Eina_List *l; Evas_GL_Image *im; EINA_LIST_FOREACH(gc->shared->images, l, im) { if (im->im) evas_cache_image_unload_data(&im->im->cache_entry); if (im->tex) { if (!im->tex->pt->dyn.img) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } } } } static void _evas_gl_image_cache_trim(Evas_Engine_GL_Context *gc) { int size = evas_common_image_get_cache(); while (gc->shared->images_size > size) { Evas_GL_Image *im2; Eina_List *l = NULL; EINA_LIST_REVERSE_FOREACH(gc->shared->images, l, im2) { if (im2->references == 0) { im2->cached = 0; im2->gc->shared->images = eina_list_remove_list(im2->gc->shared->images, l); im2->gc->shared->images_size -= (im2->csize); evas_gl_common_image_free(im2); l = NULL; break; } } if ((gc->shared->images_size > size) && (l)) { printf("EEK %i > %i, no 0 ref imgs\n", gc->shared->images_size, size); break; } if (!gc->shared->images) { printf("EEK %i > %i, no imgs\n", gc->shared->images_size, size); break; } } } static Eina_Bool _evas_gl_image_cache_add(Evas_GL_Image *im) { if (im->references == 0) { im->csize = im->w * im->h * 4; im->gc->shared->images_size += im->csize; _evas_gl_image_cache_trim(im->gc); return EINA_TRUE; } else { im->gc->shared->images = eina_list_remove(im->gc->shared->images, im); im->cached = 0; } return EINA_FALSE; } void evas_gl_common_image_ref(Evas_GL_Image *im) { if (im->references == 0) { im->gc->shared->images_size -= (im->csize); } im->references++; } void evas_gl_common_image_unref(Evas_GL_Image *im) { im->references--; if (im->references == 0) { _evas_gl_image_cache_add(im); } } Evas_GL_Image * evas_gl_common_image_load(Evas_Engine_GL_Context *gc, const char *file, const char *key, Evas_Image_Load_Opts *lo, int *error) { Evas_GL_Image *im; RGBA_Image *im_im; Eina_List *l; im_im = evas_common_load_image_from_file(file, key, lo, error); if (!im_im) return NULL; // FIXME: keep unreffed shared images around EINA_LIST_FOREACH(gc->shared->images, l, im) { if (im->im == im_im) { // why did i put this here? i think to free the rgba pixel data once a texture // exists. // evas_cache_image_drop(&im_im->cache_entry); gc->shared->images = eina_list_remove_list(gc->shared->images, l); gc->shared->images = eina_list_prepend(gc->shared->images, im); evas_gl_common_image_ref(im); *error = EVAS_LOAD_ERROR_NONE; return im; } } im = calloc(1, sizeof(Evas_GL_Image)); if (!im) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } im->references = 1; im->im = im_im; im->gc = gc; im->cached = 1; im->cs.space = EVAS_COLORSPACE_ARGB8888; im->alpha = im->im->cache_entry.flags.alpha; im->w = im->im->cache_entry.w; im->h = im->im->cache_entry.h; if (lo) im->load_opts = *lo; gc->shared->images = eina_list_prepend(gc->shared->images, im); return im; } Evas_GL_Image * evas_gl_common_image_new_from_data(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, DATA32 *data, int alpha, int cspace) { Evas_GL_Image *im; Eina_List *l; if (data) { EINA_LIST_FOREACH(gc->shared->images, l, im) { if (((void *)(im->im->image.data) == (void *)data) && (im->im->cache_entry.w == w) && (im->im->cache_entry.h == h)) { gc->shared->images = eina_list_remove_list(gc->shared->images, l); gc->shared->images = eina_list_prepend(gc->shared->images, im); evas_gl_common_image_ref(im); return im; } } } im = calloc(1, sizeof(Evas_GL_Image)); if (!im) return NULL; im->references = 1; im->im = (RGBA_Image *) evas_cache_image_data(evas_common_image_cache_get(), w, h, data, alpha, cspace); if (!im->im) { free(im); return NULL; } im->gc = gc; im->cs.space = cspace; im->alpha = im->im->cache_entry.flags.alpha; im->w = im->im->cache_entry.w; im->h = im->im->cache_entry.h; switch (cspace) { case EVAS_COLORSPACE_ARGB8888: break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: if (im->tex) evas_gl_common_texture_free(im->tex); im->tex = NULL; im->cs.data = data; im->cs.no_free = 1; break; default: abort(); break; } return im; } Evas_GL_Image * evas_gl_common_image_new_from_copied_data(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, DATA32 *data, int alpha, int cspace) { Evas_GL_Image *im; im = calloc(1, sizeof(Evas_GL_Image)); if (!im) return NULL; im->references = 1; im->im = (RGBA_Image *) evas_cache_image_copied_data(evas_common_image_cache_get(), w, h, data, alpha, cspace); if (!im->im) { free(im); return NULL; } im->gc = gc; im->cs.space = cspace; im->alpha = im->im->cache_entry.flags.alpha; im->w = im->im->cache_entry.w; im->h = im->im->cache_entry.h; switch (cspace) { case EVAS_COLORSPACE_ARGB8888: break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: if (im->tex) evas_gl_common_texture_free(im->tex); im->tex = NULL; im->cs.no_free = 0; if (im->im->cache_entry.h > 0) im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2); if ((data) && (im->cs.data)) memcpy(im->cs.data, data, im->im->cache_entry.h * sizeof(unsigned char *) * 2); break; default: abort(); break; } return im; } Evas_GL_Image * evas_gl_common_image_new(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, int alpha, int cspace) { Evas_GL_Image *im; im = calloc(1, sizeof(Evas_GL_Image)); if (!im) return NULL; im->references = 1; im->im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); if (!im->im) { free(im); return NULL; } im->gc = gc; im->im->cache_entry.flags.alpha = alpha ? 1 : 0; im->cs.space = cspace; im->alpha = im->im->cache_entry.flags.alpha; im->im->cache_entry.w = w; im->im->cache_entry.h = h; im->w = im->im->cache_entry.w; im->h = im->im->cache_entry.h; evas_cache_image_colorspace(&im->im->cache_entry, cspace); im->im = (RGBA_Image *)evas_cache_image_size_set(&im->im->cache_entry, w, h); switch (cspace) { case EVAS_COLORSPACE_ARGB8888: break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: // if (im->tex) evas_gl_common_texture_free(im->tex); im->tex = NULL; im->cs.no_free = 0; if (im->im->cache_entry.h > 0) im->cs.data = calloc(1, im->im->cache_entry.h * sizeof(unsigned char *) * 2); break; default: abort(); break; } return im; } Evas_GL_Image * evas_gl_common_image_alpha_set(Evas_GL_Image *im, int alpha) { if (!im) return NULL; if (im->alpha == alpha) return im; im->alpha = alpha; if (!im->im) return im; im->im->cache_entry.flags.alpha = alpha ? 1 : 0; if (im->tex) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } if (!im->tex) im->tex = evas_gl_common_texture_new(im->gc, im->im); return im; } void evas_gl_common_image_native_enable(Evas_GL_Image *im) { if (im->cs.data) { if (!im->cs.no_free) free(im->cs.data); im->cs.data = NULL; } im->cs.no_free = 0; if (im->cached) { if (im->references == 0) im->gc->shared->images_size -= (im->csize); im->gc->shared->images = eina_list_remove(im->gc->shared->images, im); im->cached = 0; } if (im->im) { evas_cache_image_drop(&im->im->cache_entry); im->im = NULL; } if (im->tex) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } im->cs.space = EVAS_COLORSPACE_ARGB8888; im->tex = evas_gl_common_texture_native_new(im->gc, im->w, im->h, im->alpha, im); im->tex_only = 1; } void evas_gl_common_image_native_disable(Evas_GL_Image *im) { if (im->im) { evas_cache_image_drop(&im->im->cache_entry); im->im = NULL; } if (im->tex) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } im->tex_only = 0; im->im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); im->im->cache_entry.flags.alpha = im->alpha; im->cs.space = EVAS_COLORSPACE_ARGB8888; evas_cache_image_colorspace(&im->im->cache_entry, im->cs.space); im->im = (RGBA_Image *)evas_cache_image_size_set(&im->im->cache_entry, im->w, im->h); if (!im->tex) im->tex = evas_gl_common_texture_new(im->gc, im->im); } void evas_gl_common_image_scale_hint_set(Evas_GL_Image *im, int hint) { im->scale_hint = hint; // FIXME: take advantage of this even in gl (eg if image is // 1600x1200 but we always use it at 800x600 or even less - drop // the texture res down for "non dynamic" stuff to save memory) } void evas_gl_common_image_content_hint_set(Evas_GL_Image *im, int hint) { if (im->content_hint == hint) return; im->content_hint = hint; if (!im->gc) return; if (!im->gc->shared->info.sec_image_map) return; if (!im->gc->shared->info.bgra) return; // does not handle yuv yet. if (im->cs.space != EVAS_COLORSPACE_ARGB8888) return; if (im->content_hint == EVAS_IMAGE_CONTENT_HINT_DYNAMIC) { if (im->cs.data) { if (!im->cs.no_free) free(im->cs.data); im->cs.data = NULL; } im->cs.no_free = 0; if (im->cached) { if (im->references == 0) im->gc->shared->images_size -= im->csize; im->gc->shared->images = eina_list_remove(im->gc->shared->images, im); im->cached = 0; } if (im->im) { evas_cache_image_drop(&im->im->cache_entry); im->im = NULL; } if (im->tex) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } im->tex = evas_gl_common_texture_dynamic_new(im->gc, im); im->tex_only = 1; } else { if (im->im) { evas_cache_image_drop(&im->im->cache_entry); im->im = NULL; } if (im->tex) { evas_gl_common_texture_free(im->tex); im->tex = NULL; } im->tex_only = 0; im->im = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get()); im->im->cache_entry.flags.alpha = im->alpha; im->cs.space = EVAS_COLORSPACE_ARGB8888; evas_cache_image_colorspace(&im->im->cache_entry, im->cs.space); im->im = (RGBA_Image *)evas_cache_image_size_set(&im->im->cache_entry, im->w, im->h); if (!im->tex) im->tex = evas_gl_common_texture_new(im->gc, im->im); } } void evas_gl_common_image_cache_flush(Evas_Engine_GL_Context *gc) { _evas_gl_image_cache_trim(gc); } void evas_gl_common_image_free(Evas_GL_Image *im) { Filtered_Image *fi; evas_gl_common_context_flush(im->gc); im->references--; if (im->references > 0) return; if (im->native.func.free) im->native.func.free(im->native.func.data, im); if (im->cs.data) { if (!im->cs.no_free) free(im->cs.data); } if (im->cached) { if (_evas_gl_image_cache_add(im)) return; } if (im->im) evas_cache_image_drop(&im->im->cache_entry); if (im->tex) evas_gl_common_texture_free(im->tex); EINA_LIST_FREE(im->filtered, fi) { free(fi->key); evas_gl_common_image_free((Evas_GL_Image *)fi->image); free(fi); } free(im); } Evas_GL_Image * evas_gl_common_image_surface_new(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, int alpha) { Evas_GL_Image *im; im = calloc(1, sizeof(Evas_GL_Image)); if (!im) return NULL; im->references = 1; im->gc = gc; im->cs.space = EVAS_COLORSPACE_ARGB8888; im->alpha = alpha; im->w = w; im->h = h; im->tex = evas_gl_common_texture_render_new(gc, w, h, alpha); im->tex_only = 1; return im; } void evas_gl_common_image_dirty(Evas_GL_Image *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h) { if ((w == 0) && (h == 0) && (x == 0) && (y == 0)) { w = im->w; h = im->h; } if (im->im) { im->im = (RGBA_Image *)evas_cache_image_dirty(&im->im->cache_entry, x, y, w, h); } im->dirty = 1; } void evas_gl_common_image_update(Evas_Engine_GL_Context *gc, Evas_GL_Image *im) { if (!im->im) return; /* if ((im->cs.space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->cs.space == EVAS_COLORSPACE_YCBCR422P709_PL)) { // SOFTWARE convert. do multi texture later if ((im->cs.data) && (*((unsigned char **)im->cs.data))) { if (im->dirty || !im->im->image.data) { free(im->im->image.data); im->im->image.data = malloc(im->im->cache_entry.w * im->im->cache_entry.h * sizeof(DATA32)); if (im->im->image.data) evas_common_convert_yuv_420p_601_rgba(im->cs.data, (void *)im->im->image.data, im->im->cache_entry.w, im->im->cache_entry.h); } } space = EVAS_COLORSPACE_ARGB8888; } else */ switch (im->cs.space) { case EVAS_COLORSPACE_ARGB8888: if ((im->tex) && (im->dirty)) { evas_cache_image_load_data(&im->im->cache_entry); evas_gl_common_texture_update(im->tex, im->im); evas_cache_image_unload_data(&im->im->cache_entry); } if (!im->tex) { evas_cache_image_load_data(&im->im->cache_entry); im->tex = evas_gl_common_texture_new(gc, im->im); evas_cache_image_unload_data(&im->im->cache_entry); } im->dirty = 0; if (!im->tex) return; break; case EVAS_COLORSPACE_YCBCR422P601_PL: case EVAS_COLORSPACE_YCBCR422P709_PL: if ((im->tex) && (im->dirty)) { evas_gl_common_texture_yuv_update(im->tex, im->cs.data, im->im->cache_entry.w, im->im->cache_entry.h); im->dirty = 0; } if ((!im->tex) && (im->cs.data) && (*((unsigned char **)im->cs.data))) { im->tex = evas_gl_common_texture_yuv_new(gc, im->cs.data, im->im->cache_entry.w, im->im->cache_entry.h); im->dirty = 0; } if (!im->tex) return; break; default: ERR("unhandled img format colorspace=%d", im->cs.space); break; } } void evas_gl_common_image_map_draw(Evas_Engine_GL_Context *gc, Evas_GL_Image *im, int npoints, RGBA_Map_Point *p, int smooth, int level __UNUSED__) { RGBA_Draw_Context *dc; int r, g, b, a; int c, cx, cy, cw, ch; Eina_Bool yuv = 0; dc = gc->dc; if (dc->mul.use) { a = (dc->mul.col >> 24) & 0xff; r = (dc->mul.col >> 16) & 0xff; g = (dc->mul.col >> 8 ) & 0xff; b = (dc->mul.col ) & 0xff; } else { r = g = b = a = 255; } evas_gl_common_image_update(gc, im); c = gc->dc->clip.use; cx = gc->dc->clip.x; cy = gc->dc->clip.y; cw = gc->dc->clip.w; ch = gc->dc->clip.h; im->tex->im = im; if ((im->cs.space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->cs.space == EVAS_COLORSPACE_YCBCR422P709_PL)) yuv = 1; evas_gl_common_context_image_map_push(gc, im->tex, npoints, p, c, cx, cy, cw, ch, r, g, b, a, smooth, im->tex_only, yuv); } void evas_gl_common_image_draw(Evas_Engine_GL_Context *gc, Evas_GL_Image *im, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int smooth) { RGBA_Draw_Context *dc; int r, g, b, a; double ssx, ssy, ssw, ssh; Cutout_Rects *rects; Cutout_Rect *rct; int c, cx, cy, cw, ch; int i; int yuv = 0; if (sw < 1) sw = 1; if (sh < 1) sh = 1; dc = gc->dc; if (dc->mul.use) { a = (dc->mul.col >> 24) & 0xff; r = (dc->mul.col >> 16) & 0xff; g = (dc->mul.col >> 8 ) & 0xff; b = (dc->mul.col ) & 0xff; } else { r = g = b = a = 255; } evas_gl_common_image_update(gc, im); if (!im->tex) { evas_gl_common_rect_draw(gc, dx, dy, dw, dh); return; } if ((im->cs.space == EVAS_COLORSPACE_YCBCR422P601_PL) || (im->cs.space == EVAS_COLORSPACE_YCBCR422P709_PL)) yuv = 1; im->tex->im = im; if ((!gc->dc->cutout.rects) || ((gc->shared->info.tune.cutout.max > 0) && (gc->dc->cutout.active > gc->shared->info.tune.cutout.max))) { if (gc->dc->clip.use) { int nx, ny, nw, nh; nx = dx; ny = dy; nw = dw; nh = dh; RECTS_CLIP_TO_RECT(nx, ny, nw, nh, gc->dc->clip.x, gc->dc->clip.y, gc->dc->clip.w, gc->dc->clip.h); if ((nw < 1) || (nh < 1)) return; if ((nx == dx) && (ny == dy) && (nw == dw) && (nh == dh)) { if (yuv) evas_gl_common_context_yuv_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth); else evas_gl_common_context_image_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth, im->tex_only); return; } ssx = (double)sx + ((double)(sw * (nx - dx)) / (double)(dw)); ssy = (double)sy + ((double)(sh * (ny - dy)) / (double)(dh)); ssw = ((double)sw * (double)(nw)) / (double)(dw); ssh = ((double)sh * (double)(nh)) / (double)(dh); if (yuv) evas_gl_common_context_yuv_push(gc, im->tex, ssx, ssy, ssw, ssh, nx, ny, nw, nh, r, g, b, a, smooth); else evas_gl_common_context_image_push(gc, im->tex, ssx, ssy, ssw, ssh, nx, ny, nw, nh, r, g, b, a, smooth, im->tex_only); } else { if (yuv) evas_gl_common_context_yuv_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth); else evas_gl_common_context_image_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth, im->tex_only); } return; } /* save out clip info */ c = gc->dc->clip.use; cx = gc->dc->clip.x; cy = gc->dc->clip.y; cw = gc->dc->clip.w; ch = gc->dc->clip.h; evas_common_draw_context_clip_clip(gc->dc, 0, 0, gc->w, gc->h); evas_common_draw_context_clip_clip(gc->dc, dx, dy, dw, dh); /* our clip is 0 size.. abort */ if ((gc->dc->clip.w <= 0) || (gc->dc->clip.h <= 0)) { gc->dc->clip.use = c; gc->dc->clip.x = cx; gc->dc->clip.y = cy; gc->dc->clip.w = cw; gc->dc->clip.h = ch; return; } rects = evas_common_draw_context_apply_cutouts(dc); for (i = 0; i < rects->active; ++i) { int nx, ny, nw, nh; rct = rects->rects + i; nx = dx; ny = dy; nw = dw; nh = dh; RECTS_CLIP_TO_RECT(nx, ny, nw, nh, rct->x, rct->y, rct->w, rct->h); if ((nw < 1) || (nh < 1)) continue; if ((nx == dx) && (ny == dy) && (nw == dw) && (nh == dh)) { if (yuv) evas_gl_common_context_yuv_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth); else evas_gl_common_context_image_push(gc, im->tex, sx, sy, sw, sh, dx, dy, dw, dh, r, g, b, a, smooth, im->tex_only); continue; } ssx = (double)sx + ((double)(sw * (nx - dx)) / (double)(dw)); ssy = (double)sy + ((double)(sh * (ny - dy)) / (double)(dh)); ssw = ((double)sw * (double)(nw)) / (double)(dw); ssh = ((double)sh * (double)(nh)) / (double)(dh); if (yuv) evas_gl_common_context_yuv_push(gc, im->tex, ssx, ssy, ssw, ssh, nx, ny, nw, nh, r, g, b, a, smooth); else evas_gl_common_context_image_push(gc, im->tex, ssx, ssy, ssw, ssh, nx, ny, nw, nh, r, g, b, a, smooth, im->tex_only); } evas_common_draw_context_apply_clear_cutouts(rects); /* restore clip info */ gc->dc->clip.use = c; gc->dc->clip.x = cx; gc->dc->clip.y = cy; gc->dc->clip.w = cw; gc->dc->clip.h = ch; }