efl/src/modules/evas/engines/gl_common/evas_gl_texture.c

2274 lines
73 KiB
C

#include "evas_gl_private.h"
static const GLenum rgba_fmt = GL_RGBA;
static const GLenum rgba_ifmt = GL_RGBA;
static const GLenum rgb_fmt = GL_RGBA;
static const GLenum rgb_ifmt = GL_RGB;
#ifdef GL_GLES
static const GLenum bgra_fmt = GL_BGRA;
static const GLenum bgra_ifmt = GL_BGRA;
static const GLenum bgr_fmt = GL_BGRA;
static const GLenum bgr_ifmt = GL_BGRA;
#else
static const GLenum bgra_fmt = GL_BGRA;
static const GLenum bgra_ifmt = GL_RGBA;
static const GLenum bgr_fmt = GL_BGRA;
static const GLenum bgr_ifmt = GL_RGB;
#endif
#ifdef GL_GLES
static const GLenum alpha_fmt = GL_ALPHA;
static const GLenum alpha_ifmt = GL_ALPHA;
#else
static const GLenum alpha_fmt = GL_ALPHA;
static const GLenum alpha_ifmt = GL_ALPHA4;
#endif
static const GLenum lum_fmt = GL_LUMINANCE;
static const GLenum lum_ifmt = GL_LUMINANCE;
static const GLenum lum_alpha_fmt = GL_LUMINANCE_ALPHA;
static const GLenum lum_alpha_ifmt = GL_LUMINANCE_ALPHA;
static const GLenum rgba8_ifmt = GL_RGBA;
static const GLenum rgba8_fmt = GL_BGRA;
/* FIXME: RGB8_ETC2 is a superset of ETC1,
* but is GL_ETC1_RGB8_OES supported whenever GL_COMPRESSED_RGB8_ETC2 is?
*/
static const GLenum etc1_fmt = GL_ETC1_RGB8_OES;
static const GLenum etc2_rgb_fmt = GL_COMPRESSED_RGB8_ETC2;
static const GLenum etc2_rgba_fmt = GL_COMPRESSED_RGBA8_ETC2_EAC;
static const GLenum s3tc_rgb_dxt1_fmt = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
static const GLenum s3tc_rgba_dxt1_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
static const GLenum s3tc_rgba_dxt23_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
static const GLenum s3tc_rgba_dxt45_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
static struct {
struct {
int num, pix;
} c, a, v, r, n, d;
} texinfo = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
typedef enum {
MATCH_FALSE = EINA_FALSE,
MATCH_TRUE = EINA_TRUE,
MATCH_ANY = 2
} Eina_Bool_Match;
static const struct {
Eina_Bool_Match alpha;
Eina_Bool_Match bgra;
Evas_Colorspace cspace;
const GLenum *intformat;
const GLenum *format;
} matching_format[] = {
{ MATCH_TRUE, MATCH_TRUE, EVAS_COLORSPACE_ARGB8888, &bgra_ifmt, &bgra_fmt },
{ MATCH_TRUE, MATCH_FALSE, EVAS_COLORSPACE_ARGB8888, &rgba_ifmt, &rgba_fmt },
{ MATCH_FALSE, MATCH_TRUE, EVAS_COLORSPACE_ARGB8888, &bgr_ifmt, &bgr_fmt },
#ifdef GL_GLES
{ MATCH_FALSE, MATCH_FALSE, EVAS_COLORSPACE_ARGB8888, &rgba_ifmt, &rgba_fmt },
#else
{ MATCH_FALSE, MATCH_FALSE, EVAS_COLORSPACE_ARGB8888, &rgb_ifmt, &rgb_fmt },
#endif
{ MATCH_FALSE, MATCH_ANY, EVAS_COLORSPACE_GRY8, &lum_fmt, &lum_ifmt },
{ MATCH_TRUE, MATCH_ANY, EVAS_COLORSPACE_AGRY88, &lum_alpha_fmt, &lum_alpha_ifmt },
// ETC1/2 support
{ MATCH_FALSE, MATCH_ANY, EVAS_COLORSPACE_ETC1, &etc1_fmt, &etc1_fmt },
{ MATCH_FALSE, MATCH_ANY, EVAS_COLORSPACE_RGB8_ETC2, &etc2_rgb_fmt, &etc2_rgb_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA8_ETC2_EAC, &etc2_rgba_fmt, &etc2_rgba_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_ETC1_ALPHA, &etc1_fmt, &etc1_fmt },
// S3TC support
{ MATCH_FALSE, MATCH_ANY, EVAS_COLORSPACE_RGB_S3TC_DXT1, &s3tc_rgb_dxt1_fmt, &s3tc_rgb_dxt1_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA_S3TC_DXT1, &s3tc_rgba_dxt1_fmt, &s3tc_rgba_dxt1_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA_S3TC_DXT2, &s3tc_rgba_dxt23_fmt, &s3tc_rgba_dxt23_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA_S3TC_DXT3, &s3tc_rgba_dxt23_fmt, &s3tc_rgba_dxt23_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA_S3TC_DXT4, &s3tc_rgba_dxt45_fmt, &s3tc_rgba_dxt45_fmt },
{ MATCH_ANY, MATCH_ANY, EVAS_COLORSPACE_RGBA_S3TC_DXT5, &s3tc_rgba_dxt45_fmt, &s3tc_rgba_dxt45_fmt }
};
static const GLenum matching_rgb[] = { GL_RGB4, GL_RGB8, GL_RGB12, GL_RGB16, 0x0 };
static const GLenum matching_rgba[] = { GL_RGBA4, GL_RGBA8, GL_RGBA12, GL_RGBA16, 0x0 };
static const GLenum matching_alpha[] = { GL_ALPHA4, GL_ALPHA8, GL_ALPHA12, GL_ALPHA16, 0x0 };
static const GLenum matching_luminance[] = { GL_LUMINANCE4, GL_LUMINANCE8, GL_LUMINANCE12, GL_LUMINANCE16, 0x0 };
static const GLenum matching_luminance_alpha[] = { GL_LUMINANCE4_ALPHA4, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE12_ALPHA12, GL_LUMINANCE16_ALPHA16, 0x0 };
static const struct {
GLenum master;
const GLenum *matching;
} matching_fmt[] = {
{ GL_RGB, matching_rgb },
{ GL_RGBA, matching_rgba },
{ GL_ALPHA, matching_alpha },
{ GL_ALPHA4, matching_alpha },
{ GL_LUMINANCE, matching_luminance },
{ GL_LUMINANCE_ALPHA, matching_luminance_alpha }
};
#define MATCH(_r, _v) ((_r == MATCH_ANY) || (_v == MATCH_ANY) || (_r == _v))
#ifdef GL_TEXTURE_INTERNAL_FORMAT
# ifndef GL_GLES
static Eina_Bool
_evas_gl_texture_match(GLenum intfmt, GLenum intfmtret)
{
unsigned int i;
if (intfmt == intfmtret) return EINA_TRUE;
for (i = 0; i < sizeof (matching_fmt) / sizeof (matching_fmt[0]); i++)
if (matching_fmt[i].master == intfmt)
{
unsigned int j;
for (j = 0; matching_fmt[i].matching[j] != 0x0; j++)
if (matching_fmt[i].matching[j] == intfmtret)
return EINA_TRUE;
return EINA_FALSE;
}
return EINA_FALSE;
}
# endif
#endif
static int
_evas_gl_texture_search_format(Eina_Bool alpha, Eina_Bool bgra, Evas_Colorspace cspace)
{
unsigned int i;
alpha = !!alpha;
bgra = !!bgra;
for (i = 0; i < sizeof (matching_format) / sizeof (matching_format[0]); ++i)
if (MATCH(matching_format[i].alpha, alpha) &&
MATCH(matching_format[i].bgra, bgra) &&
matching_format[i].cspace == cspace)
return i;
CRI("There is no supported texture format for this colorspace: "
"cspace(%d) alpha(%d) bgra(%d)", cspace, alpha, bgra);
return -1;
}
static void
_print_tex_count(void)
{
if (getenv("EVAS_GL_MEMINFO"))
{
fprintf(stderr,
"T: c:%i/%ik | a:%i/%ik | v:%i/%ik | r:%i/%ik | n:%i/%ik | d:%i/%ik\n",
texinfo.c.num, (texinfo.c.pix * 4) / 1024,
texinfo.a.num, (texinfo.a.pix ) / 1024,
texinfo.v.num, (texinfo.v.pix ) / 1024,
texinfo.r.num, (texinfo.r.pix * 4) / 1024,
texinfo.n.num, (texinfo.n.pix * 4) / 1024,
texinfo.d.num, (texinfo.d.pix * 4) / 1024
);
}
}
static int
_nearest_pow2(int num)
{
unsigned int n = num - 1;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
static void
_tex_adjust(Evas_Engine_GL_Context *gc, int *w, int *h)
{
if (gc->shared->info.tex_npo2) return;
/*if (gc->shared->info.tex_rect) return;*/
*w = _nearest_pow2(*w);
*h = _nearest_pow2(*h);
}
static int
_tex_format_index(GLuint format)
{
switch (format)
{
case GL_RGBA:
case GL_RGBA4:
case GL_RGBA8:
case GL_RGBA12:
case GL_RGBA16:
case GL_BGRA:
return 0;
case GL_RGB:
case GL_R3_G3_B2:
case GL_RGB4:
case GL_RGB5:
case GL_RGB8:
case GL_RGB10:
case GL_RGB12:
case GL_RGB16:
return 1;
case GL_ALPHA:
case GL_ALPHA4:
case GL_ALPHA8:
case GL_ALPHA12:
case GL_ALPHA16:
return 2;
case GL_ETC1_RGB8_OES:
return 3;
case GL_COMPRESSED_RGB8_ETC2:
return 4;
case GL_COMPRESSED_RGBA8_ETC2_EAC:
return 5;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
return 6;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
return 7;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // dxt2 as well
return 8;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: // dxt4 as well
return 9;
case GL_LUMINANCE: // never used in atlas
case GL_LUMINANCE4:
case GL_LUMINANCE8:
case GL_LUMINANCE12:
case GL_LUMINANCE16:
return 10;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE4_ALPHA4:
case GL_LUMINANCE8_ALPHA8:
case GL_LUMINANCE12_ALPHA12:
case GL_LUMINANCE16_ALPHA16:
return 11;
default:
ERR("Unknown format returned specified by GL stack: %x", format);
return -1;
}
return 0;
}
static inline int
_evas_gl_texture_size_get(int w, int h, int intfmt, Eina_Bool *comp)
{
if (comp) *comp = EINA_FALSE;
switch (intfmt)
{
case GL_RGBA:
case GL_BGRA:
case GL_RGB:
return w * h * 4;
case GL_ALPHA:
return w * h * 1;
case GL_ALPHA4:
return w * h / 2; // TODO: Check this
case GL_LUMINANCE:
return w * h * 1;
case GL_LUMINANCE_ALPHA:
return w * h * 2;
case GL_ETC1_RGB8_OES:
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
if (comp) *comp = EINA_TRUE;
return ((w + 3) >> 2) * ((h + 3) >> 2) * 8;
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
if (comp) *comp = EINA_TRUE;
return ((w + 3) >> 2) * ((h + 3) >> 2) * 16;
default:
return 0;
}
}
static Eina_Bool
_tex_2d(Evas_Engine_GL_Context *gc, int intfmt, int w, int h, int fmt, int type)
{
Eina_Bool comp;
int sz;
if ((w > gc->shared->info.max_texture_size) ||
(h > gc->shared->info.max_texture_size))
{
ERR("Fail tex too big %ix%i", w, h);
return EINA_FALSE;
}
sz = _evas_gl_texture_size_get(w, h, intfmt, &comp);
if (!comp)
glTexImage2D(GL_TEXTURE_2D, 0, intfmt, w, h, 0, fmt, type, NULL);
else
glCompressedTexImage2D(GL_TEXTURE_2D, 0, intfmt, w, h, 0, sz, NULL);
#ifdef GL_TEXTURE_INTERNAL_FORMAT
# ifdef GL_GLES
# else
// this is not in opengles!!! hrrrm
// if (glGetTexLevelParameteriv) // in case of weak symbols?
{
int intfmtret = -1;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
GL_TEXTURE_INTERNAL_FORMAT, &intfmtret);
if (!_evas_gl_texture_match(intfmt, intfmtret))
{
ERR("Fail tex alloc %ix%i, intfmt: %X intfmtret: %X", w, h, intfmt, intfmtret);
// XXX send async err to evas
return EINA_FALSE;
}
}
// else
// {
// ERR("GL_TEXTURE_INTERNAL_FORMAT defined but no symbol loaded.");
// }
# endif
#endif
return EINA_TRUE;
}
static Evas_GL_Texture *
evas_gl_common_texture_alloc(Evas_Engine_GL_Context *gc,
Evas_Coord w, Evas_Coord h,
Eina_Bool alpha)
{
Evas_GL_Texture *tex;
tex = calloc(1, sizeof(Evas_GL_Texture));
if (!tex) return NULL;
tex->gc = gc;
tex->references = 1;
tex->alpha = alpha;
tex->w = w;
tex->h = h;
return tex;
}
static void
evas_gl_common_texture_light_free(Evas_GL_Texture *tex)
{
free(tex);
}
static Evas_GL_Texture_Pool *
_pool_tex_new(Evas_Engine_GL_Context *gc, int w, int h, GLenum intformat, GLenum format)
{
Evas_GL_Texture_Pool *pt;
Eina_Bool ok, no_rounding = EINA_FALSE;
if ((w > gc->shared->info.max_texture_size) ||
(h > gc->shared->info.max_texture_size))
{
ERR("Fail tex too big %ix%i", w, h);
return NULL;
}
pt = calloc(1, sizeof(Evas_GL_Texture_Pool));
if (!pt) return NULL;
if (!gc->shared->info.etc1_subimage && (intformat == etc1_fmt))
no_rounding = EINA_TRUE;
if (!no_rounding)
{
_tex_adjust(gc, &w, &h);
}
pt->gc = gc;
pt->w = w;
pt->h = h;
pt->intformat = intformat;
pt->format = format;
pt->dataformat = GL_UNSIGNED_BYTE;
pt->references = 0;
pt->eina_pool = eina_rectangle_pool_new(w, h);
glGenTextures(1, &(pt->texture));
glBindTexture(GL_TEXTURE_2D, pt->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
ok = _tex_2d(gc, pt->intformat, w, h, pt->format, pt->dataformat);
glBindTexture(GL_TEXTURE_2D, gc->pipe[0].shader.cur_tex);
if (!ok)
{
glDeleteTextures(1, &(pt->texture));
if (pt->eina_pool)
eina_rectangle_pool_free(pt->eina_pool);
free(pt);
return NULL;
}
if (format == alpha_fmt)
{
texinfo.a.num++;
texinfo.a.pix += pt->w * pt->h;
}
else if (format == lum_fmt)
{
texinfo.v.num++;
texinfo.v.pix += pt->w * pt->h;
}
else
{
texinfo.c.num++;
texinfo.c.pix += pt->w * pt->h;
}
_print_tex_count();
return pt;
}
static Eina_Rectangle *
_pool_tex_alloc(Evas_GL_Texture_Pool *pt, int w, int h, int *u, int *v)
{
Eina_Rectangle *r;
r = eina_rectangle_pool_request( pt->eina_pool, w, h);
if (r)
{
*v = r->y;
*u = r->x;
pt->allocations = eina_list_prepend(pt->allocations, r);
}
return r;
}
static Evas_GL_Texture_Pool *
_pool_tex_find(Evas_Engine_GL_Context *gc, int w, int h,
GLenum intformat, GLenum format, int *u, int *v,
Eina_Rectangle **apt, int atlas_w)
{
Evas_GL_Texture_Pool *pt = NULL;
Eina_List *l;
int th2;
int pool_h;
if (atlas_w > gc->shared->info.max_texture_size)
atlas_w = gc->shared->info.max_texture_size;
if ((w > gc->shared->info.tune.atlas.max_w) ||
(h > gc->shared->info.tune.atlas.max_h) ||
(!gc->shared->info.etc1_subimage && (intformat == etc1_fmt)))
{
pt = _pool_tex_new(gc, w, h, intformat, format);
if (!pt) return NULL;
gc->shared->tex.whole = eina_list_prepend(gc->shared->tex.whole, pt);
pt->fslot = -1;
pt->whole = 1;
*apt = _pool_tex_alloc(pt, w, h, u, v);
return pt;
}
th2 = _tex_format_index(intformat);
if (th2 < 0) return NULL;
EINA_LIST_FOREACH(gc->shared->tex.atlas[th2], l, pt)
{
if ((*apt = _pool_tex_alloc(pt, w, h, u, v)) != NULL)
{
gc->shared->tex.atlas[th2] =
eina_list_promote_list(gc->shared->tex.atlas[th2], l);
return pt;
}
}
pool_h = atlas_w;
if ( h > pool_h || w > atlas_w )
{
atlas_w = gc->shared->info.tune.atlas.max_w;
pool_h = gc->shared->info.tune.atlas.max_h;
}
pt = _pool_tex_new(gc, atlas_w, pool_h, intformat, format);
if (!pt) return NULL;
gc->shared->tex.atlas[th2] =
eina_list_prepend(gc->shared->tex.atlas[th2], pt);
pt->fslot = th2;
*apt = _pool_tex_alloc(pt, w, h, u, v);
return pt;
}
Evas_GL_Texture *
evas_gl_common_texture_new(Evas_Engine_GL_Context *gc, RGBA_Image *im)
{
Evas_GL_Texture *tex;
GLsizei w, h;
int u = 0, v = 0, xoffset = 1, yoffset = 1;
int lformat;
lformat = _evas_gl_texture_search_format(im->cache_entry.flags.alpha, gc->shared->info.bgra, im->cache_entry.space);
if (lformat < 0) return NULL;
switch (im->cache_entry.space)
{
case EVAS_COLORSPACE_ETC1:
case EVAS_COLORSPACE_RGB8_ETC2:
case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
case EVAS_COLORSPACE_RGB_S3TC_DXT1:
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:
// Add border to avoid artifacts
w = im->cache_entry.w + im->cache_entry.borders.l + im->cache_entry.borders.r;
h = im->cache_entry.h + im->cache_entry.borders.t + im->cache_entry.borders.b;
EINA_SAFETY_ON_FALSE_RETURN_VAL(!(w & 0x3) && !(h & 0x3), NULL);
xoffset = im->cache_entry.borders.l;
yoffset = im->cache_entry.borders.t;
break;
case EVAS_COLORSPACE_ETC1_ALPHA:
// One must call evas_gl_common_texture_rgb_a_pair_new() instead.
ERR("Trying to upload ETC1+Alpha texture as a normal texture. Abort.");
return NULL;
default:
// One pixel gap and two pixels for duplicated borders
w = im->cache_entry.w + 3;
h = im->cache_entry.h + 3;
break;
}
tex = evas_gl_common_texture_alloc(gc, im->cache_entry.w, im->cache_entry.h,
im->cache_entry.flags.alpha);
if (!tex) return NULL;
tex->pt = _pool_tex_find(gc, w, h,
*matching_format[lformat].intformat,
*matching_format[lformat].format,
&u, &v, &tex->apt,
gc->shared->info.tune.atlas.max_alloc_size);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
tex->x = u + xoffset;
tex->y = v + yoffset;
tex->pt->references++;
evas_gl_common_texture_update(tex, im);
return tex;
}
static Evas_GL_Texture_Pool *
_pool_tex_render_new(Evas_Engine_GL_Context *gc, int w, int h, int intformat, int format)
{
Evas_GL_Texture_Pool *pt;
int fnum;
Eina_Bool ok;
if ((w > gc->shared->info.max_texture_size) ||
(h > gc->shared->info.max_texture_size))
{
ERR("Fail tex too big %ix%i", w, h);
return NULL;
}
pt = calloc(1, sizeof(Evas_GL_Texture_Pool));
if (!pt) return NULL;
_tex_adjust(gc, &w, &h);
pt->gc = gc;
pt->w = w;
pt->h = h;
pt->intformat = intformat;
pt->format = format;
pt->dataformat = GL_UNSIGNED_BYTE;
pt->render = 1;
pt->references = 0;
pt->eina_pool = eina_rectangle_pool_new(w, h);
#ifdef GL_GLES
# ifndef GL_FRAMEBUFFER
# define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
# endif
# ifndef GL_COLOR_ATTACHMENT0
# define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES
# endif
#else
# ifndef GL_FRAMEBUFFER
# define GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
# endif
# ifndef GL_COLOR_ATTACHMENT0
# define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
# endif
#endif
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fnum);
glGenTextures(1, &(pt->texture));
glBindTexture(GL_TEXTURE_2D, pt->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
ok = _tex_2d(gc, pt->intformat, w, h, pt->format, pt->dataformat);
glsym_glGenFramebuffers(1, &(pt->fb));
GLERRV("glsym_glGenFramebuffers");
glsym_glBindFramebuffer(GL_FRAMEBUFFER, pt->fb);
GLERRV("glsym_glBindFramebuffer");
glsym_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pt->texture, 0);
GLERRV("glsym_glFramebufferTexture2D");
glsym_glBindFramebuffer(GL_FRAMEBUFFER, fnum);
GLERRV("glsym_glBindFramebuffer");
glBindTexture(GL_TEXTURE_2D, gc->pipe[0].shader.cur_tex);
if (!ok)
{
glDeleteTextures(1, &(pt->texture));
if (pt->eina_pool)
eina_rectangle_pool_free(pt->eina_pool);
free(pt);
return NULL;
}
texinfo.r.num++;
texinfo.r.pix += pt->w * pt->h;
_print_tex_count();
return pt;
}
static Evas_GL_Texture_Pool *
_pool_tex_native_new(Evas_Engine_GL_Context *gc, int w, int h, int intformat, int format, Evas_GL_Image *im)
{
Evas_GL_Texture_Pool *pt;
if ((w > gc->shared->info.max_texture_size) ||
(h > gc->shared->info.max_texture_size))
{
ERR("Fail tex too big %ix%i", w, h);
return NULL;
}
pt = calloc(1, sizeof(Evas_GL_Texture_Pool));
if (!pt) return NULL;
pt->gc = gc;
#ifdef GL_TEXTURE_RECTANGLE_ARB
if (im->native.target == GL_TEXTURE_RECTANGLE_ARB)
{
printf("REEEEEEEEECT\n");
pt->w = w;
pt->h = h;
}
else
#endif
{
// FIXME: handle po2 only textures
pt->w = w;
pt->h = h;
}
pt->intformat = intformat;
pt->format = format;
pt->dataformat = GL_UNSIGNED_BYTE;
pt->references = 0;
pt->native = 1;
pt->eina_pool = eina_rectangle_pool_new(w, h);
glGenTextures(1, &(pt->texture));
glBindTexture(im->native.target, pt->texture);
#ifdef GL_GLES
#else
if (im->native.loose)
{
if (im->native.func.bind)
im->native.func.bind(im->native.func.data, im);
}
#endif
glTexParameteri(im->native.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(im->native.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(im->native.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(im->native.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(im->native.target, 0);
glBindTexture(im->native.target, gc->pipe[0].shader.cur_tex);
texinfo.n.num++;
texinfo.n.pix += pt->w * pt->h;
_print_tex_count();
return pt;
}
static Evas_GL_Texture_Pool *
_pool_tex_dynamic_new(Evas_Engine_GL_Context *gc, int w, int h, int intformat, int format)
{
Evas_GL_Texture_Pool *pt = NULL;
#ifdef GL_GLES
void *egldisplay;
if (intformat != format) return NULL;
pt = calloc(1, sizeof(Evas_GL_Texture_Pool));
if (!pt) return NULL;
_tex_adjust(gc, &w, &h);
pt->gc = gc;
pt->w = w;
pt->h = h;
pt->intformat = intformat;
pt->format = format;
pt->dataformat = GL_UNSIGNED_BYTE;
pt->render = 1;
pt->references = 0;
pt->eina_pool = eina_rectangle_pool_new(w, h);
texinfo.d.num++;
texinfo.d.pix += pt->w * pt->h;
_print_tex_count();
glGenTextures(1, &(pt->texture));
glBindTexture(GL_TEXTURE_2D, pt->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
egldisplay = pt->gc->egldisp;
if (gc->shared->info.sec_tbm_surface)
{
tbm_format buffer_format = TBM_FORMAT_RGBA8888;
tbm_surface_info_s info;
int attr[] =
{
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE,
};
switch (intformat)
{
case GL_LUMINANCE: buffer_format = TBM_FORMAT_C8; break;
case GL_LUMINANCE_ALPHA: buffer_format = TBM_FORMAT_C8; break;
case GL_RGBA: buffer_format = TBM_FORMAT_RGBA8888; break;
case GL_BGRA: buffer_format = TBM_FORMAT_BGRA8888; break;
case GL_RGB: buffer_format = TBM_FORMAT_RGB888; break;
default: ERR("TBM: unknown format"); return NULL;
}
pt->dyn.buffer = (void *)secsym_tbm_surface_create(pt->w, pt->h,
buffer_format);
if (!pt->dyn.buffer) goto error;
pt->dyn.img = secsym_eglCreateImage(egldisplay,
EGL_NO_CONTEXT,
EGL_NATIVE_SURFACE_TIZEN,
pt->dyn.buffer, attr);
if (!pt->dyn.img)
{
secsym_tbm_surface_destroy(pt->dyn.buffer);
goto error;
}
secsym_tbm_surface_get_info(pt->dyn.buffer, &info);
pt->dyn.w = info.width;
pt->dyn.h = info.height;
pt->dyn.stride = info.planes[0].stride;
}
else if (gc->shared->info.sec_image_map)
{
int fmt; // EGL_MAP_GL_TEXTURE_RGBA_SEC or EGL_MAP_GL_TEXTURE_RGB_SEC or bust
int pixtype; // EGL_MAP_GL_TEXTURE_UNSIGNED_BYTE_SEC or bust
int attr[] =
{
EGL_MAP_GL_TEXTURE_WIDTH_SEC, 32,
EGL_MAP_GL_TEXTURE_HEIGHT_SEC, 32,
EGL_MAP_GL_TEXTURE_FORMAT_SEC, EGL_MAP_GL_TEXTURE_RGBA_SEC,
EGL_MAP_GL_TEXTURE_PIXEL_TYPE_SEC, EGL_MAP_GL_TEXTURE_UNSIGNED_BYTE_SEC,
EGL_NONE
};
switch (intformat)
{
#ifdef EGL_MAP_GL_TEXTURE_LUMINANCE_SEC
case GL_LUMINANCE: attr[5] = EGL_MAP_GL_TEXTURE_LUMINANCE_SEC; break;
#endif
#ifdef EGL_MAP_GL_TEXTURE_LUMINANCE_ALPHA_SEC
case GL_LUMINANCE_ALPHA: attr[5] = EGL_MAP_GL_TEXTURE_LUMINANCE_ALPHA_SEC; break;
#endif
case GL_RGBA: attr[5] = EGL_MAP_GL_TEXTURE_RGBA_SEC; break;
case GL_BGRA: attr[5] = EGL_MAP_GL_TEXTURE_BGRA_SEC; break;
default: ERR("SEC map: unknown format"); return NULL;
}
attr[1] = pt->w;
attr[3] = pt->h;
// FIXME: seems a bit slower than i'd like - maybe too many flushes?
// FIXME: YCbCr no support as yet
pt->dyn.img = secsym_eglCreateImage(egldisplay,
EGL_NO_CONTEXT,
EGL_MAP_GL_TEXTURE_2D_SEC,
0, attr);
GLERRV("secsym_eglCreateImage");
if (!pt->dyn.img) goto error;
if (secsym_eglGetImageAttribSEC(egldisplay,
pt->dyn.img,
EGL_MAP_GL_TEXTURE_WIDTH_SEC,
&(pt->dyn.w)) != EGL_TRUE) goto error;
if (secsym_eglGetImageAttribSEC(egldisplay,
pt->dyn.img,
EGL_MAP_GL_TEXTURE_HEIGHT_SEC,
&(pt->dyn.h)) != EGL_TRUE) goto error;
if (secsym_eglGetImageAttribSEC(egldisplay,
pt->dyn.img,
EGL_MAP_GL_TEXTURE_STRIDE_IN_BYTES_SEC,
&(pt->dyn.stride)) != EGL_TRUE) goto error;
if (secsym_eglGetImageAttribSEC(egldisplay,
pt->dyn.img,
EGL_MAP_GL_TEXTURE_FORMAT_SEC,
&(fmt)) != EGL_TRUE) goto error;
if (secsym_eglGetImageAttribSEC(egldisplay,
pt->dyn.img,
EGL_MAP_GL_TEXTURE_PIXEL_TYPE_SEC,
&(pixtype)) != EGL_TRUE) goto error;
if (pixtype != EGL_MAP_GL_TEXTURE_UNSIGNED_BYTE_SEC) goto error;
}
else
{
ERR("TBM surface or SEC image map should be enabled!");
goto error;
}
glBindTexture(GL_TEXTURE_2D, gc->pipe[0].shader.cur_tex);
#else
if (gc + w + h + intformat + format) return pt;
#endif
return pt;
/* ERROR HANDLING */
#ifdef GL_GLES
error:
if (pt->dyn.img)
{
secsym_eglDestroyImage(egldisplay, pt->dyn.img);
GLERRV("secsym_eglDestroyImage");
pt->dyn.img = NULL;
}
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &(pt->texture));
if (pt->eina_pool)
eina_rectangle_pool_free(pt->eina_pool);
free(pt);
return NULL;
#endif
}
void
evas_gl_texture_pool_empty(Evas_GL_Texture_Pool *pt)
{
Eina_Rectangle *apt;
if (!pt->gc) return;
if (pt->format == alpha_fmt)
{
texinfo.a.num--;
texinfo.a.pix -= pt->w * pt->h;
}
else if (pt->format == lum_fmt)
{
texinfo.v.num--;
texinfo.v.pix -= pt->w * pt->h;
}
else if (pt->dyn.img)
{
texinfo.d.num--;
texinfo.d.pix -= pt->w * pt->h;
}
else if (pt->render)
{
texinfo.r.num--;
texinfo.r.pix -= pt->w * pt->h;
}
else if (pt->native)
{
texinfo.n.num--;
texinfo.n.pix -= pt->w * pt->h;
}
else
{
texinfo.c.num--;
texinfo.c.pix -= pt->w * pt->h;
}
_print_tex_count();
#ifdef GL_GLES
if (pt->dyn.img)
{
if (pt->dyn.checked_out > 0)
{
if (pt->gc->shared->info.sec_tbm_surface)
secsym_tbm_surface_unmap(pt->dyn.buffer);
else if (pt->gc->shared->info.sec_image_map)
secsym_eglUnmapImageSEC(pt->gc->egldisp, pt->dyn.img, EGL_MAP_GL_TEXTURE_DEVICE_CPU_SEC);
}
if (pt->dyn.buffer)
secsym_tbm_surface_destroy(pt->dyn.buffer);
secsym_eglDestroyImage(pt->gc->egldisp, pt->dyn.img);
pt->dyn.img = NULL;
pt->dyn.buffer = NULL;
pt->dyn.data = NULL;
pt->dyn.w = 0;
pt->dyn.h = 0;
pt->dyn.stride = 0;
pt->dyn.checked_out = 0;
}
#endif
glDeleteTextures(1, &(pt->texture));
if (pt->gc->pipe[0].shader.cur_tex == pt->texture)
pt->gc->pipe[0].shader.cur_tex = 0;
if (pt->fb)
{
glsym_glDeleteFramebuffers(1, &(pt->fb));
GLERRV("glsym_glDeleteFramebuffers");
pt->fb = 0;
}
EINA_LIST_FREE(pt->allocations, apt)
eina_rectangle_pool_release(apt);
pt->texture = 0;
pt->gc = NULL;
pt->w = 0;
pt->h = 0;
}
void
pt_unref(Evas_GL_Texture_Pool *pt)
{
if (!pt) return;
if (!pt->gc) return;
pt->references--;
if (pt->references != 0) return;
if (!((pt->render) || (pt->native)))
{
if (pt->whole)
pt->gc->shared->tex.whole =
eina_list_remove(pt->gc->shared->tex.whole, pt);
else
pt->gc->shared->tex.atlas [pt->fslot] =
eina_list_remove(pt->gc->shared->tex.atlas[pt->fslot], pt);
}
evas_gl_texture_pool_empty(pt);
if (pt->eina_pool)
eina_rectangle_pool_free(pt->eina_pool);
free(pt);
}
static void
pt_link(Evas_Engine_GL_Context *gc, Evas_GL_Texture_Pool *pt)
{
gc->shared->tex.whole = eina_list_prepend(gc->shared->tex.whole, pt);
pt->fslot = -1;
pt->whole = 1;
pt->references++;
}
Evas_GL_Texture *
evas_gl_common_texture_native_new(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, int alpha, Evas_GL_Image *im)
{
Evas_GL_Texture *tex;
int lformat;
lformat = _evas_gl_texture_search_format(alpha, gc->shared->info.bgra, EVAS_COLORSPACE_ARGB8888);
if (lformat < 0) return NULL;
tex = evas_gl_common_texture_alloc(gc, w, h, alpha);
if (!tex) return NULL;
tex->pt = _pool_tex_native_new(gc, w, h,
*matching_format[lformat].intformat,
*matching_format[lformat].format,
im);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
tex->pt->references++;
return tex;
}
Evas_GL_Texture *
evas_gl_common_texture_render_new(Evas_Engine_GL_Context *gc, unsigned int w, unsigned int h, int alpha)
{
Evas_GL_Texture *tex;
int lformat;
lformat = _evas_gl_texture_search_format(alpha, gc->shared->info.bgra, EVAS_COLORSPACE_ARGB8888);
if (lformat < 0) return NULL;
tex = evas_gl_common_texture_alloc(gc, w, h, alpha);
if (!tex) return NULL;
tex->pt = _pool_tex_render_new(gc, w, h,
*matching_format[lformat].intformat,
*matching_format[lformat].format);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
tex->pt->references++;
return tex;
}
Evas_GL_Texture *
evas_gl_common_texture_dynamic_new(Evas_Engine_GL_Context *gc, Evas_GL_Image *im)
{
Evas_GL_Texture *tex;
int lformat;
lformat = _evas_gl_texture_search_format(im->alpha, gc->shared->info.bgra, EVAS_COLORSPACE_ARGB8888);
if (lformat < 0) return NULL;
tex = evas_gl_common_texture_alloc(gc, im->w, im->h, im->alpha);
if (!tex) return NULL;
tex->pt = _pool_tex_dynamic_new(gc, tex->w, tex->h,
*matching_format[lformat].intformat,
*matching_format[lformat].format);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
tex->pt->references++;
return tex;
}
void
evas_gl_common_texture_upload(Evas_GL_Texture *tex, RGBA_Image *im, unsigned int bytes_count)
{
GLuint fmt;
fmt = tex->pt->format;
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// printf("tex upload %ix%i\n", im->cache_entry.w, im->cache_entry.h);
// +-+
// +-+
//
_tex_sub_2d(tex->gc, tex->x, tex->y,
im->cache_entry.w, im->cache_entry.h,
fmt, tex->pt->dataformat,
im->image.data);
// xxx
// xxx
// ---
_tex_sub_2d(tex->gc, tex->x, tex->y + im->cache_entry.h,
im->cache_entry.w, 1,
fmt, tex->pt->dataformat,
im->image.data8 + (((im->cache_entry.h - 1) * im->cache_entry.w)) * bytes_count);
// xxx
// xxx
// o
_tex_sub_2d(tex->gc, tex->x - 1, tex->y + im->cache_entry.h,
1, 1,
fmt, tex->pt->dataformat,
im->image.data8 + (((im->cache_entry.h - 1) * im->cache_entry.w)) * bytes_count);
// xxx
// xxx
// o
_tex_sub_2d(tex->gc, tex->x + im->cache_entry.w, tex->y + im->cache_entry.h,
1, 1,
fmt, tex->pt->dataformat,
im->image.data8 + (im->cache_entry.h * im->cache_entry.w - 1) * bytes_count);
//2D packing
// ---
// xxx
// xxx
_tex_sub_2d(tex->gc, tex->x, tex->y - 1,
im->cache_entry.w, 1,
fmt, tex->pt->dataformat,
im->image.data);
// o
// xxx
// xxx
_tex_sub_2d(tex->gc, tex->x - 1, tex->y - 1,
1, 1,
fmt, tex->pt->dataformat,
im->image.data);
// o
// xxx
// xxx
_tex_sub_2d(tex->gc, tex->x + im->cache_entry.w, tex->y - 1,
1, 1,
fmt, tex->pt->dataformat,
im->image.data8 + (im->cache_entry.w - 1) * bytes_count);
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, im->cache_entry.w);
// |xxx
// |xxx
//
_tex_sub_2d(tex->gc, tex->x - 1, tex->y,
1, im->cache_entry.h,
fmt, tex->pt->dataformat,
im->image.data);
// xxx|
// xxx|
//
_tex_sub_2d(tex->gc, tex->x + im->cache_entry.w, tex->y,
1, im->cache_entry.h,
fmt, tex->pt->dataformat,
im->image.data8 + (im->cache_entry.w - 1) * bytes_count);
}
else
{
DATA8 *tpix, *ps, *pd;
int i;
tpix = alloca(im->cache_entry.h * bytes_count);
pd = tpix;
ps = im->image.data8;
for (i = 0; i < (int)im->cache_entry.h; i++)
{
memcpy(pd, ps, bytes_count);
pd += bytes_count;
ps += im->cache_entry.w * bytes_count;
}
// |xxx
// |xxx
//
_tex_sub_2d(tex->gc, tex->x - 1, tex->y,
1, im->cache_entry.h,
fmt, tex->pt->dataformat,
tpix);
pd = tpix;
ps = im->image.data8 + (im->cache_entry.w - 1) * bytes_count;
for (i = 0; i < (int)im->cache_entry.h; i++)
{
memcpy(pd, ps, bytes_count);
pd += bytes_count;
ps += im->cache_entry.w * bytes_count;
}
// xxx|
// xxx|
//
_tex_sub_2d(tex->gc, tex->x + im->cache_entry.w, tex->y,
1, im->cache_entry.h,
fmt, tex->pt->dataformat,
tpix);
}
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
void
evas_gl_common_texture_update(Evas_GL_Texture *tex, RGBA_Image *im)
{
unsigned int bytes_count, bsize = 8;
if (tex->alpha != im->cache_entry.flags.alpha)
{
int lformat;
lformat = _evas_gl_texture_search_format(tex->alpha, tex->gc->shared->info.bgra, im->cache_entry.space);
if (lformat < 0) return;
tex->pt->allocations = eina_list_remove(tex->pt->allocations, tex->apt);
if (tex->apt)
eina_rectangle_pool_release(tex->apt);
// FIXME: why a 'render' new here ??? Should already have been allocated, quite a weird path.
tex->pt = _pool_tex_render_new(tex->gc, tex->w, tex->h,
*matching_format[lformat].intformat,
*matching_format[lformat].format);
}
// If image was preloaded then we need a ptt
if (!tex->pt) return;
if (!im->image.data) return;
switch (im->cache_entry.space)
{
case EVAS_COLORSPACE_ARGB8888: bytes_count = 4; break;
case EVAS_COLORSPACE_GRY8: bytes_count = 1; break;
case EVAS_COLORSPACE_AGRY88: bytes_count = 2; break;
// Compressed texture formats: S3TC and ETC1/2
case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
case EVAS_COLORSPACE_RGBA_S3TC_DXT2:
case EVAS_COLORSPACE_RGBA_S3TC_DXT3:
case EVAS_COLORSPACE_RGBA_S3TC_DXT4:
case EVAS_COLORSPACE_RGBA_S3TC_DXT5:
bsize = 16;
// fallthrough
case EVAS_COLORSPACE_RGB_S3TC_DXT1:
case EVAS_COLORSPACE_RGBA_S3TC_DXT1:
case EVAS_COLORSPACE_ETC1:
case EVAS_COLORSPACE_RGB8_ETC2:
{
/*
ETC1/2 can't be scaled down on the fly and interpolated, like it is
required for preloading, so we don't take that path. Also as the content
already have duplicated border and we use a specific function to
upload the compressed data, there is no need to use the normal path at
all.
*/
GLsizei width, height;
GLint x, y;
x = tex->x - im->cache_entry.borders.l;
y = tex->y - im->cache_entry.borders.t;
width = im->cache_entry.w + im->cache_entry.borders.l + im->cache_entry.borders.r;
height = im->cache_entry.h + im->cache_entry.borders.t + im->cache_entry.borders.b;
EINA_SAFETY_ON_FALSE_RETURN(!(width & 0x3) && !(height & 0x3));
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if ((tex->gc->shared->info.etc1_subimage ||
(im->cache_entry.space != EVAS_COLORSPACE_ETC1))
&& (tex->pt->w != width || tex->pt->h != height))
{
int err;
err = glGetError();
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0,
x, y, width, height,
tex->pt->format,
((width * height) >> 4) * bsize,
im->image.data);
err = glGetError();
if (err != GL_NO_ERROR)
{
__evas_gl_err(err, __FILE__, __FUNCTION__, __LINE__,
"glCompressedTexSubImage2D");
// FIXME: Changing settings on the fly.
// The first texture will be black.
// How to fallback? We need a whole texture now.
if (im->cache_entry.space == EVAS_COLORSPACE_ETC1)
tex->gc->shared->info.etc1_subimage = EINA_FALSE;
}
}
else
{
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tex->pt->format,
width, height, 0,
((width * height) >> 4) * bsize,
im->image.data);
}
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
{
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
return;
}
case EVAS_COLORSPACE_ETC1_ALPHA:
// One must call evas_gl_common_texture_rgb_a_pair_update() instead.
ERR("Can't upload ETC1+Alpha texture as a normal texture. Abort.");
return;
default:
ERR("Can't upload texture in colorspace %i.", im->cache_entry.space);
return;
}
// if preloaded, then async push it in after uploading a miniature of it
if (im->cache_entry.flags.preload_done
&& (tex->w > (2 * EVAS_GL_TILE_SIZE))
&& (tex->h > (2 * EVAS_GL_TILE_SIZE))
&& evas_gl_preload_enabled())
{
Evas_GL_Texture_Async_Preload *async;
unsigned char *in;
unsigned char *out;
GLuint fmt;
float xstep, ystep;
float x, y;
int i, j;
int lformat;
int u, v;
if (tex->ptt) return ;
out = alloca(bytes_count * EVAS_GL_TILE_SIZE * EVAS_GL_TILE_SIZE);
xstep = (float)tex->w / (EVAS_GL_TILE_SIZE - 2);
ystep = (float)tex->h / (EVAS_GL_TILE_SIZE - 2);
in = im->image.data8;
// top-left
memcpy(&out[0],
&in[0],
bytes_count);
// top
for (x = xstep * 0.5f, i = 1; i < EVAS_GL_TILE_SIZE - 1; x += xstep, i++)
{
memcpy(&out[i * bytes_count],
&in[(int)x * bytes_count],
bytes_count);
}
// top-right
memcpy(&out[((EVAS_GL_TILE_SIZE - 1) * bytes_count)],
&in[(im->cache_entry.w - 1) * bytes_count],
bytes_count);
for (y = ystep * 0.5f, j = 1; j < EVAS_GL_TILE_SIZE - 1; y += ystep, j++)
{
// left
memcpy(&out[j * EVAS_GL_TILE_SIZE * bytes_count],
&in[((int)y * im->cache_entry.w) * bytes_count],
bytes_count);
// middle
for (x = xstep * 0.5f, i = 1; i < EVAS_GL_TILE_SIZE - 1; x += xstep, i++)
memcpy(&out[(j * EVAS_GL_TILE_SIZE + i) * bytes_count],
&in[(((int)y * im->cache_entry.w) + (int)x) * bytes_count],
bytes_count);
// right
memcpy(&out[(j * EVAS_GL_TILE_SIZE + i) * bytes_count],
&in[(((int)y * im->cache_entry.w) + (im->cache_entry.w - 1)) * bytes_count],
bytes_count);
}
// bottom-left
memcpy(&out[(j * EVAS_GL_TILE_SIZE) * bytes_count],
&in[((im->cache_entry.w * (im->cache_entry.h - 1)) + 1) * bytes_count],
bytes_count);
// bottom
for (x = xstep * 0.5f, i = 1; i < EVAS_GL_TILE_SIZE - 1; x += xstep, i++)
{
memcpy(&out[((EVAS_GL_TILE_SIZE * j) + i) * bytes_count],
&in[((int)x + im->cache_entry.w * (im->cache_entry.h - 1)) * bytes_count],
bytes_count);
}
// bottom-right
memcpy(&out[((EVAS_GL_TILE_SIZE * EVAS_GL_TILE_SIZE) - 1) * bytes_count],
&in[((im->cache_entry.w * im->cache_entry.h) - 1) * bytes_count],
bytes_count);
// out is a miniature of the texture, upload that now and schedule the data for later.
// Creating the mini picture texture
lformat = _evas_gl_texture_search_format(tex->alpha, tex->gc->shared->info.bgra, im->cache_entry.space);
if (lformat < 0) return;
tex->ptt = _pool_tex_find(tex->gc, EVAS_GL_TILE_SIZE, EVAS_GL_TILE_SIZE,
*matching_format[lformat].intformat,
*matching_format[lformat].format,
&u, &v, &tex->aptt,
tex->gc->shared->info.tune.atlas.max_alloc_size);
if (!tex->ptt)
goto upload;
tex->tx = u + 1;
tex->ty = v;
tex->ptt->references++;
// Bind and upload ! Vooom !
fmt = tex->ptt->format;
glBindTexture(GL_TEXTURE_2D, tex->ptt->texture);
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
_tex_sub_2d(tex->gc, u, tex->ty, EVAS_GL_TILE_SIZE, EVAS_GL_TILE_SIZE, fmt, tex->ptt->dataformat, out);
// Switch back to current texture
if (tex->ptt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
// Now prepare uploading the main texture before returning;
async = malloc(sizeof (Evas_GL_Texture_Async_Preload));
if (!async)
{
goto upload;
}
async->tex = tex;
async->tex->references++;
async->im = im;
#ifdef EVAS_CSERVE2
if (evas_cache2_image_cached(&async->im->cache_entry))
evas_cache2_image_ref(&async->im->cache_entry);
else
#endif
evas_cache_image_ref(&async->im->cache_entry);
async->unpack_row_length = tex->gc->shared->info.unpack_row_length;
if (evas_gl_preload_push(async))
return ;
// Failed to start asynchronous upload, likely due to preload not being supported by the backend
async->tex->references--;
#ifdef EVAS_CSERVE2
if (evas_cache2_image_cached(&async->im->cache_entry))
evas_cache2_image_close(&async->im->cache_entry);
else
#endif
evas_cache_image_drop(&async->im->cache_entry);
free(async);
upload:
pt_unref(tex->ptt);
tex->ptt = NULL;
}
evas_gl_common_texture_upload(tex, im, bytes_count);
}
void
evas_gl_common_texture_free(Evas_GL_Texture *tex, Eina_Bool force EINA_UNUSED)
{
if (!tex) return;
if (force)
{
evas_gl_preload_pop(tex);
while (tex->targets)
evas_gl_preload_target_unregister(tex, eina_list_data_get(tex->targets));
}
tex->references--;
if (tex->references != 0) return;
if (tex->fglyph)
{
tex->gc->font_glyph_textures = eina_list_remove(tex->gc->font_glyph_textures, tex);
tex->fglyph->ext_dat = NULL;
tex->fglyph->ext_dat_free = NULL;
}
if (tex->pt)
{
tex->pt->allocations = eina_list_remove(tex->pt->allocations, tex->apt);
if (tex->apt) eina_rectangle_pool_release(tex->apt);
tex->apt = NULL;
pt_unref(tex->pt);
tex->pt = NULL;
}
if (tex->ptt)
{
tex->ptt->allocations = eina_list_remove(tex->ptt->allocations, tex->aptt);
if (tex->aptt) eina_rectangle_pool_release(tex->aptt);
tex->aptt = NULL;
pt_unref(tex->ptt);
tex->ptt = NULL;
}
if (tex->ptu) pt_unref(tex->ptu);
if (tex->ptv) pt_unref(tex->ptv);
if (tex->ptuv) pt_unref(tex->ptuv);
tex->ptu = NULL;
tex->ptv = NULL;
tex->ptuv = NULL;
evas_gl_common_texture_light_free(tex);
}
Evas_GL_Texture *
evas_gl_common_texture_alpha_new(Evas_Engine_GL_Context *gc, DATA8 *pixels,
unsigned int w, unsigned int h, int fh)
{
Evas_GL_Texture *tex;
int u = 0, v = 0;
tex = evas_gl_common_texture_alloc(gc, w, h, EINA_FALSE);
if (!tex) return NULL;
tex->pt = _pool_tex_find(gc, w + 3, fh, alpha_ifmt, alpha_fmt, &u, &v,
&tex->apt,
gc->shared->info.tune.atlas.max_alloc_alpha_size);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
tex->x = u + 1;
tex->y = v;
tex->pt->references++;
evas_gl_common_texture_alpha_update(tex, pixels, w, h, fh);
return tex;
}
void
evas_gl_common_texture_alpha_update(Evas_GL_Texture *tex, DATA8 *pixels,
unsigned int w, unsigned int h, int fh EINA_UNUSED)
{
if (!tex->pt) return;
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
_tex_sub_2d(tex->gc, tex->x, tex->y, w, h, tex->pt->format,
tex->pt->dataformat, pixels);
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
Evas_GL_Texture *
evas_gl_common_texture_rgb_a_pair_new(Evas_Engine_GL_Context *gc,
RGBA_Image *im)
{
Evas_GL_Texture *tex;
int lformat, w, h;
// FIXME/TODO: We don't support texture atlasses here (a bit tricky)
// Some debugging. We could return.
if (im->cache_entry.space != EVAS_COLORSPACE_ETC1_ALPHA)
WRN("Using RGB+A texture pair with format %d", im->cache_entry.space);
w = im->cache_entry.w;
h = im->cache_entry.h;
lformat = _evas_gl_texture_search_format(EINA_TRUE, gc->shared->info.bgra,
im->cache_entry.space);
if (lformat < 0) return NULL;
tex = evas_gl_common_texture_alloc(gc, w, h, EINA_TRUE);
if (!tex) return NULL;
w += im->cache_entry.borders.l + im->cache_entry.borders.r;
h += im->cache_entry.borders.t + im->cache_entry.borders.b;
tex->x = im->cache_entry.borders.l;
tex->y = im->cache_entry.borders.t;
// Allocate RGB texture normally - as a 'whole'
tex->pt = _pool_tex_new(gc, w, h,
*matching_format[lformat].intformat,
*matching_format[lformat].format);
if (!tex->pt)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
pt_link(gc, tex->pt);
tex->pt->slot = -1;
// And now Alpha texture -- FIXME could intformat be different? (eg. ALPHA4)
tex->pta = _pool_tex_new(gc, w, h,
*matching_format[lformat].intformat,
*matching_format[lformat].format);
if (!tex->pta)
{
pt_unref(tex->pt);
evas_gl_common_texture_light_free(tex);
return NULL;
}
pt_link(gc, tex->pta);
tex->pta->slot = -1;
evas_gl_common_texture_rgb_a_pair_update(tex, im);
return tex;
}
void
evas_gl_common_texture_rgb_a_pair_update(Evas_GL_Texture *tex,
RGBA_Image *im)
{
DATA8 *data1, *data2;
Eina_Bool comp, upload, subimage = EINA_TRUE;
int w, h, sz, rowlen, ystep = 1;
if (!tex->pt) return;
// Handle compressed formats with 4x4 blocks format
if (((int) im->cache_entry.space >= (int) EVAS_COLORSPACE_ETC1) &&
((int) im->cache_entry.space <= (int) EVAS_COLORSPACE_RGBA_S3TC_DXT5))
ystep = 4;
if ((im->cache_entry.space == EVAS_COLORSPACE_ETC1) ||
(im->cache_entry.space == EVAS_COLORSPACE_ETC1_ALPHA))
subimage = tex->gc->shared->info.etc1_subimage;
w = im->cache_entry.w + im->cache_entry.borders.l + im->cache_entry.borders.r;
h = im->cache_entry.h + im->cache_entry.borders.t + im->cache_entry.borders.b;
rowlen = _evas_gl_texture_size_get(w, ystep, tex->pt->intformat, NULL);
sz = _evas_gl_texture_size_get(w, h, tex->pt->intformat, &comp);
data1 = im->image.data8;
data2 = data1 + sz;
upload = !!data1;
if ((w == tex->pt->w) && (h == tex->pt->h))
subimage = EINA_FALSE;
if (!subimage || tex->gc->shared->info.unpack_row_length)
{
if (tex->gc->shared->info.unpack_row_length)
glPixelStorei(GL_UNPACK_ROW_LENGTH, w);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
goto on_error;
if (upload)
{
if (comp)
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tex->pt->intformat, w, h, 0, sz, data1);
else
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, data1);
}
glBindTexture(GL_TEXTURE_2D, tex->pta->texture);
if (!_tex_2d(tex->gc, tex->pta->intformat, w, h, tex->pta->format, tex->pta->dataformat))
goto on_error;
if (upload)
{
if (comp)
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tex->pta->intformat, w, h, 0, sz, data2);
else
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pta->format, tex->pta->dataformat, data2);
}
}
else
{
int y;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format,
tex->pt->dataformat))
goto on_error;
if (upload)
{
if (w == tex->w)
{
if (comp)
_comp_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, sz, data1);
else
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, data1);
}
else
{
for (y = 0; y < h; y += ystep)
{
if (comp)
_comp_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, sz, data1);
else
_tex_sub_2d(tex->gc, 0, y, w, ystep, tex->pt->format,
tex->pt->dataformat, data1 + rowlen * y / ystep);
}
}
}
glBindTexture(GL_TEXTURE_2D, tex->pta->texture);
if (!_tex_2d(tex->gc, tex->pta->intformat, w, h, tex->pta->format,
tex->pta->dataformat))
goto on_error;
if (upload)
{
if (w == tex->w)
{
if (comp)
_comp_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pta->format, sz, data2);
else
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pta->format, tex->pta->dataformat, data2);
}
else
{
for (y = 0; y < h; y += ystep)
{
if (comp)
_comp_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pta->format, sz, data2);
else
_tex_sub_2d(tex->gc, 0, y, w, ystep, tex->pta->format,
tex->pta->dataformat, data2 + rowlen * y / ystep);
}
}
}
}
on_error:
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
Evas_GL_Texture *
evas_gl_common_texture_yuv_new(Evas_Engine_GL_Context *gc, DATA8 **rows, unsigned int w, unsigned int h)
{
Evas_GL_Texture *tex;
tex = evas_gl_common_texture_alloc(gc, w, h, EINA_FALSE);
if (!tex) return NULL;
tex->ptu = _pool_tex_new(gc, w / 2 + 1, h / 2 + 1, lum_ifmt, lum_fmt);
if (!tex->ptu)
{
evas_gl_common_texture_light_free(tex);
return NULL;
}
gc->shared->tex.whole = eina_list_prepend(gc->shared->tex.whole, tex->ptu);
tex->ptu->slot = -1;
tex->ptu->fslot = -1;
tex->ptu->whole = 1;
tex->ptv = _pool_tex_new(gc, tex->ptu->w, tex->ptu->h, lum_ifmt, lum_fmt);
if (!tex->ptv)
{
pt_unref(tex->ptu);
evas_gl_common_texture_light_free(tex);
return NULL;
}
gc->shared->tex.whole = eina_list_prepend(gc->shared->tex.whole, tex->ptv);
tex->ptv->slot = -1;
tex->ptv->fslot = -1;
tex->ptv->whole = 1;
tex->pt = _pool_tex_new(gc, tex->ptu->w * 2, tex->ptu->h * 2, lum_ifmt, lum_fmt);
if (!tex->pt)
{
pt_unref(tex->ptu);
pt_unref(tex->ptv);
evas_gl_common_texture_light_free(tex);
return NULL;
}
gc->shared->tex.whole = eina_list_prepend(gc->shared->tex.whole, tex->pt);
tex->pt->fslot = -1;
tex->pt->whole = 1;
tex->pt->references++;
tex->ptu->references++;
tex->ptv->references++;
evas_gl_common_texture_yuv_update(tex, rows, w, h);
return tex;
}
void
evas_gl_common_texture_yuv_update(Evas_GL_Texture *tex, DATA8 **rows, unsigned int w, unsigned int h)
{
if (!tex->pt) return;
// FIXME: works on lowest size 4 pixel high buffers. must also be multiple of 2
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, rows[1] - rows[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, rows[0]);
glBindTexture(GL_TEXTURE_2D, tex->ptu->texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, rows[h + 1] - rows[h]);
if (!_tex_2d(tex->gc, tex->ptu->intformat, w / 2, h / 2, tex->ptu->format, tex->ptu->dataformat))
return;
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptu->format, tex->ptu->dataformat, rows[h]);
glBindTexture(GL_TEXTURE_2D, tex->ptv->texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, rows[h + (h / 2) + 1] - rows[h + (h / 2)]);
if (!_tex_2d(tex->gc, tex->ptv->intformat, w / 2, h / 2, tex->ptv->format, tex->ptv->dataformat))
return;
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptv->format, tex->ptv->dataformat, rows[h + (h / 2)]);
}
else
{
unsigned int y;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
if ((rows[1] - rows[0]) == (int)w)
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, rows[0]);
else
{
for (y = 0; y < h; y++)
_tex_sub_2d(tex->gc, 0, y, w, 1, tex->pt->format, tex->pt->dataformat, rows[y]);
}
glBindTexture(GL_TEXTURE_2D, tex->ptu->texture);
if (!_tex_2d(tex->gc, tex->ptu->intformat, w / 2, h / 2, tex->ptu->format, tex->ptu->dataformat))
return;
if ((rows[h + 1] - rows[h]) == (int)(w / 2))
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptu->format, tex->ptu->dataformat, rows[h]);
else
{
for (y = 0; y < (h / 2); y++)
_tex_sub_2d(tex->gc, 0, y, w / 2, 1, tex->ptu->format, tex->ptu->dataformat, rows[h + y]);
}
glBindTexture(GL_TEXTURE_2D, tex->ptv->texture);
if (!_tex_2d(tex->gc, tex->ptv->intformat, w / 2, h / 2, tex->ptv->format, tex->ptv->dataformat))
return;
if ((rows[h + (h / 2) + 1] - rows[h + (h / 2)]) == (int)(w / 2))
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptv->format, tex->ptv->dataformat, rows[h + (h / 2)]);
else
{
for (y = 0; y < (h / 2); y++)
_tex_sub_2d(tex->gc, 0, y, w / 2, 1, tex->ptv->format, tex->ptv->dataformat, rows[h + (h / 2) + y]);
}
}
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
static Evas_GL_Texture *
_evas_gl_common_texture_y2uv_new(Evas_Engine_GL_Context *gc,
unsigned int yw, unsigned int yh,
Eina_Bool uv2w, Eina_Bool uv2h,
GLenum y_ifmt, GLenum y_fmt,
GLenum uv_ifmt, GLenum uv_fmt,
Eina_Bool dynamic)
{
Evas_GL_Texture_Pool *pt[2] = { NULL, NULL };
Evas_GL_Texture_Pool *ptuv[2] = { NULL, NULL };
Evas_GL_Texture *tex;
unsigned int uvw, uvh;
uvw = uv2w ? yw / 2 + 1 : yw + 1;
uvh = uv2h ? yh / 2 + 1 : yh + 1;
if (!dynamic)
{
ptuv[0] = _pool_tex_new(gc, uvw, uvh, uv_ifmt, uv_fmt);
ptuv[1] = _pool_tex_new(gc, uvw, uvh, uv_ifmt, uv_fmt);
if (ptuv[0] && ptuv[1])
{
pt[0] = _pool_tex_new(gc,
ptuv[0]->w * (uv2w ? 2 : 1),
ptuv[0]->h * (uv2h ? 2 : 1),
y_ifmt, y_fmt);
pt[1] = _pool_tex_new(gc,
ptuv[1]->w * (uv2w ? 2 : 1),
ptuv[1]->h * (uv2h ? 2 : 1),
y_ifmt, y_fmt);
}
}
else
{
ptuv[0] = _pool_tex_dynamic_new(gc, uvw, uvh, uv_ifmt, uv_fmt);
ptuv[1] = _pool_tex_dynamic_new(gc, uvw, uvh, uv_ifmt, uv_fmt);
if (ptuv[0] && ptuv[1])
{
pt[0] = _pool_tex_dynamic_new(gc,
ptuv[0]->w * (uv2w ? 2 : 1),
ptuv[0]->h * (uv2h ? 2 : 1),
y_ifmt, y_fmt);
pt[1] = _pool_tex_dynamic_new(gc,
ptuv[1]->w * (uv2w ? 2 : 1),
ptuv[1]->h * (uv2h ? 2 : 1),
y_ifmt, y_fmt);
}
}
if (!pt[0] || !pt[1] || !ptuv[0] || !ptuv[1])
goto on_error;
INF("YUV [%i, %i] => Y[%i, %i], UV[%i, %i]",
yw, yh,
pt[0]->w, pt[0]->h,
ptuv[0]->w, ptuv[0]->h);
tex = evas_gl_common_texture_alloc(gc, yw, yh, EINA_FALSE);
if (!tex)
goto on_error;
tex->pt = pt[0];
tex->ptuv = ptuv[0];
tex->dyn = dynamic;
pt_link(gc, pt[0]);
pt_link(gc, pt[1]);
pt_link(gc, ptuv[0]);
pt_link(gc, ptuv[1]);
tex->double_buffer.source = 0;
memcpy(tex->double_buffer.pt, pt, sizeof (Evas_GL_Texture_Pool *) * 2);
memcpy(tex->double_buffer.ptuv, ptuv, sizeof (Evas_GL_Texture_Pool *) * 2);
return tex;
on_error:
pt_unref(pt[0]);
pt_unref(pt[1]);
pt_unref(ptuv[0]);
pt_unref(ptuv[1]);
return NULL;
}
Evas_GL_Texture *
evas_gl_common_texture_yuy2_new(Evas_Engine_GL_Context *gc, DATA8 **rows, unsigned int w, unsigned int h)
{
Evas_GL_Texture *tex;
tex = _evas_gl_common_texture_y2uv_new(gc, w, h, EINA_TRUE, EINA_FALSE, lum_alpha_ifmt, lum_alpha_fmt, rgba8_ifmt, rgba8_fmt, 0);
evas_gl_common_texture_yuy2_update(tex, rows, w, h);
return tex;
}
Evas_GL_Texture *
evas_gl_common_texture_nv12_new(Evas_Engine_GL_Context *gc, DATA8 **rows, unsigned int w, unsigned int h)
{
Evas_GL_Texture *tex;
#ifdef GL_GLES
tex = _evas_gl_common_texture_y2uv_new(gc, w, h, EINA_TRUE, EINA_TRUE, lum_ifmt, lum_fmt, lum_alpha_ifmt, lum_alpha_fmt, 1);
if (!tex)
#endif
tex = _evas_gl_common_texture_y2uv_new(gc, w, h, EINA_TRUE, EINA_TRUE, lum_ifmt, lum_fmt, lum_alpha_ifmt, lum_alpha_fmt, 0);
evas_gl_common_texture_nv12_update(tex, rows, w, h);
return tex;
}
Evas_GL_Texture *
evas_gl_common_texture_nv12tiled_new(Evas_Engine_GL_Context *gc, DATA8 **rows, unsigned int w, unsigned int h)
{
Evas_GL_Texture *tex = NULL;
#ifdef GL_GLES
tex = _evas_gl_common_texture_y2uv_new(gc, w, h, EINA_TRUE, EINA_TRUE, lum_ifmt, lum_fmt, lum_alpha_ifmt, lum_alpha_fmt, 1);
if (!tex)
#endif
tex = _evas_gl_common_texture_y2uv_new(gc, w, h, EINA_TRUE, EINA_TRUE, lum_ifmt, lum_fmt, lum_alpha_ifmt, lum_alpha_fmt, 0);
evas_gl_common_texture_nv12tiled_update(tex, rows, w, h);
return tex;
}
void
evas_gl_common_texture_yuy2_update(Evas_GL_Texture *tex, DATA8 **rows, unsigned int w, unsigned int h)
{
if (!tex->pt) return;
// FIXME: works on lowest size 4 pixel high buffers. must also be multiple of 2
unsigned int y;
tex->double_buffer.source = 1 - tex->double_buffer.source;
tex->pt = tex->double_buffer.pt[tex->double_buffer.source];
tex->ptuv = tex->double_buffer.ptuv[tex->double_buffer.source];
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
if ((rows[1] - rows[0]) == (int)w * 4)
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, rows[0]);
else
{
for (y = 0; y < h; y++)
_tex_sub_2d(tex->gc, 0, y, w, 1, tex->pt->format, tex->pt->dataformat, rows[y]);
}
glBindTexture(GL_TEXTURE_2D, tex->ptuv->texture);
if (!_tex_2d(tex->gc, tex->ptuv->intformat, w / 2, h, tex->ptuv->format, tex->ptuv->dataformat))
return;
#if 0
/*
FIXME: this piece of code doesn't work anymore since texture width
is not anymore exactly w / 2. I don't understand why.
*/
if ((rows[1] - rows[0]) == (int)(w * 2))
_tex_sub_2d(tex->gc, 0, 0, w / 2, h, tex->ptuv->format, tex->ptuv->dataformat, rows[0]);
else
#endif
{
for (y = 0; y < h; y++)
_tex_sub_2d(tex->gc, 0, y, w / 2, 1, tex->ptuv->format, tex->ptuv->dataformat, rows[y]);
}
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
void
evas_gl_common_texture_nv12_update(Evas_GL_Texture *tex, DATA8 **rows, unsigned int w, unsigned int h)
{
if (!tex->pt) return;
tex->double_buffer.source = 1 - tex->double_buffer.source;
tex->pt = tex->double_buffer.pt[tex->double_buffer.source];
tex->ptuv = tex->double_buffer.ptuv[tex->double_buffer.source];
// FIXME: works on lowest size 4 pixel high buffers. must also be multiple of 2
if (tex->gc->shared->info.unpack_row_length)
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, rows[1] - rows[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, rows[0]);
glBindTexture(GL_TEXTURE_2D, tex->ptuv->texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, rows[h + 1] - rows[h]);
if (!_tex_2d(tex->gc, tex->ptuv->intformat, w / 2, h / 2, tex->ptuv->format, tex->ptuv->dataformat))
return;
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptuv->format, tex->ptuv->dataformat, rows[h]);
}
else
{
unsigned int y;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
if ((rows[1] - rows[0]) == (int)w)
_tex_sub_2d(tex->gc, 0, 0, w, h, tex->pt->format, tex->pt->dataformat, rows[0]);
else
{
for (y = 0; y < h; y++)
_tex_sub_2d(tex->gc, 0, y, w, 1, tex->pt->format, tex->pt->dataformat, rows[y]);
}
glBindTexture(GL_TEXTURE_2D, tex->ptuv->texture);
if (!_tex_2d(tex->gc, tex->ptuv->intformat, w / 2, h / 2, tex->ptuv->format, tex->ptuv->dataformat))
return;
if ((rows[h + 1] - rows[h]) == (int)(w / 2))
_tex_sub_2d(tex->gc, 0, 0, w / 2, h / 2, tex->ptuv->format, tex->ptuv->dataformat, rows[h]);
else
{
for (y = 0; y < (h / 2); y++)
_tex_sub_2d(tex->gc, 0, y, w / 2, 1, tex->ptuv->format, tex->ptuv->dataformat, rows[h + y]);
}
}
if (tex->pt->texture != tex->gc->pipe[0].shader.cur_tex)
glBindTexture(GL_TEXTURE_2D, tex->gc->pipe[0].shader.cur_tex);
}
void
evas_gl_common_texture_nv12tiled_update(Evas_GL_Texture *tex, DATA8 **rows, unsigned int w, unsigned int h)
{
unsigned int mb_x, mb_y, mb_w, mb_h;
unsigned int base_h;
if (!tex->pt) return;
tex->double_buffer.source = 1 - tex->double_buffer.source;
tex->pt = tex->double_buffer.pt[tex->double_buffer.source];
tex->ptuv = tex->double_buffer.ptuv[tex->double_buffer.source];
mb_w = w / 64 + (w % 64 ? 1 : 0);
mb_h = h / 32 + (h % 32 ? 1 : 0);
#ifdef GL_GLES
if (tex->dyn)
{
char *texture_addr;
char *tmp;
texture_addr = secsym_eglMapImageSEC(tex->gc->egldisp,
tex->pt->dyn.img,
EGL_MAP_GL_TEXTURE_DEVICE_CPU_SEC,
EGL_MAP_GL_TEXTURE_OPTION_WRITE_SEC);
/* Iterate each Y macroblock like we do in evas_convert_yuv.c */
for (mb_y = 0; mb_y < (mb_h >> 1); mb_y++)
{
int step = 2;
int offset = 0;
int x = 0;
int rmb_x = 0;
int ry[2];
ry[0] = mb_y * 2 * 32 * tex->pt->dyn.stride;
ry[1] = ry[0] + 32 * tex->pt->dyn.stride;
for (mb_x = 0; mb_x < mb_w * 2; mb_x++, rmb_x += 64 * 32)
{
unsigned int i;
tmp = texture_addr + x + ry[offset];
for (i = 0; i < 32 * 64; i += 64, tmp += tex->pt->dyn.stride)
memcpy(tmp, rows[mb_y] + rmb_x + i, 64);
step++;
if ((step & 0x3) == 0)
{
offset = 1 - offset;
x -= 64;
}
else
{
x += 64;
}
}
}
if (mb_h & 0x1)
{
int rmb_x = 0;
int x = 0;
int ry;
ry = mb_y * 2 * 32 * tex->pt->dyn.stride;
for (mb_x = 0; mb_x < mb_w; mb_x++, x += 64, rmb_x += 64 * 32)
{
unsigned int i;
tmp = texture_addr + x + ry;
for (i = 0; i < 32 * 64; i += 64, tmp += tex->pt->dyn.stride)
memcpy(tmp, rows[mb_y] + rmb_x + i, 64);
}
}
secsym_eglUnmapImageSEC(tex->gc->egldisp, tex->pt->dyn.img, EGL_MAP_GL_TEXTURE_DEVICE_CPU_SEC);
texture_addr = secsym_eglMapImageSEC(tex->gc->egldisp,
tex->ptuv->dyn.img,
EGL_MAP_GL_TEXTURE_DEVICE_CPU_SEC,
EGL_MAP_GL_TEXTURE_OPTION_WRITE_SEC);
/* Iterate each UV macroblock like we do in evas_convert_yuv.c */
base_h = (mb_h >> 1) + (mb_h & 0x1);
/* h is always a multiple of 32 */
mb_h = h / 2;
mb_h = (mb_h / 32 + (mb_h % 32 ? 1 : 0));
mb_w = w / 2;
mb_w = (mb_w / 64 + (mb_w % 64 ? 1 : 0));
for (mb_y = 0; mb_y < (mb_h >> 1); mb_y++)
{
int step = 2;
int offset = 0;
int x = 0;
int rmb_x = 0;
int ry[2];
ry[0] = mb_y * 2 * 32 * tex->ptuv->dyn.stride;
ry[1] = ry[0] + 32 * tex->ptuv->dyn.stride;
for (mb_x = 0; mb_x < mb_w * 4; mb_x++, rmb_x += 64 * 32)
{
unsigned int i = 0;
tmp = texture_addr + x + ry[offset];
for (i = 0; i < 32 * 64; i += 64, tmp += tex->ptuv->dyn.stride)
memcpy(tmp, rows[mb_y + base_h] + rmb_x + i, 64);
step++;
if ((step & 0x3) == 0)
{
offset = 1 - offset;
x -= 64;
}
else
{
x += 64;
}
}
}
if (mb_h & 0x1)
{
int rmb_x = 0;
int x = 0;
int ry;
ry = mb_y * 2 * 32 * tex->ptuv->dyn.stride;
for (mb_x = 0; mb_x < mb_w * 2; mb_x++, x += 64, rmb_x += 64 * 32)
{
unsigned int i;
tmp = texture_addr + x + ry;
/* It has horizontaly half the pixels, but they are double the size*/
for (i = 0; i < 32 * 64; i += 64, tmp += tex->ptuv->dyn.stride)
memcpy(tmp, rows[mb_y + base_h] + rmb_x + i, 64);
}
}
secsym_eglUnmapImageSEC(tex->gc->egldisp, tex->ptuv->dyn.img, EGL_MAP_GL_TEXTURE_DEVICE_CPU_SEC);
return;
}
#endif
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, tex->pt->texture);
// We are telling the driver to not swizzle back the buffer as we are going to replace all pixel
if (!_tex_2d(tex->gc, tex->pt->intformat, w, h, tex->pt->format, tex->pt->dataformat))
return;
/* Iterate each Y macroblock like we do in evas_convert_yuv.c */
for (mb_y = 0; mb_y < (mb_h >> 1); mb_y++)
{
int step = 2;
int offset = 0;
int x = 0;
int rmb_x = 0;
int ry[2];
ry[0] = mb_y * 2 * 32;
ry[1] = ry[0] + 32;
for (mb_x = 0; mb_x < mb_w * 2; mb_x++, rmb_x += 64 * 32)
{
_tex_sub_2d(tex->gc, x, ry[offset], 64, 32, tex->pt->format, tex->pt->dataformat, rows[mb_y] + rmb_x);
step++;
if ((step & 0x3) == 0)
{
offset = 1 - offset;
x -= 64;
}
else
{
x += 64;
}
}
}
if (mb_h & 0x1)
{
int rmb_x = 0;
int x = 0;
int ry;
ry = mb_y * 2 * 32;
for (mb_x = 0; mb_x < mb_w; mb_x++, x += 64, rmb_x += 64 * 32)
_tex_sub_2d(tex->gc, x, ry, 64, 32, tex->pt->format, tex->pt->dataformat, rows[mb_y] + rmb_x);
}
glBindTexture(GL_TEXTURE_2D, tex->ptuv->texture);
if (!_tex_2d(tex->gc, tex->ptuv->intformat, w, h, tex->ptuv->format, tex->ptuv->dataformat))
return;
/* Iterate each UV macroblock like we do in evas_convert_yuv.c */
base_h = (mb_h >> 1) + (mb_h & 0x1);
/* h is always a multiple of 32 */
mb_h = h / 2;
mb_h = (mb_h / 32 + (mb_h % 32 ? 1 : 0));
mb_w = w / 2;
mb_w = (mb_w / 32 + (mb_w % 32 ? 1 : 0));
for (mb_y = 0; mb_y < (mb_h >> 1); mb_y++)
{
int step = 2;
int offset = 0;
int x = 0;
int rmb_x = 0;
int ry[2];
ry[0] = mb_y * 2 * 32;
ry[1] = ry[0] + 32;
for (mb_x = 0; mb_x < mb_w * 2; mb_x++, rmb_x += 64 * 32)
{
_tex_sub_2d(tex->gc, x, ry[offset], 32, 32,
tex->ptuv->format, tex->ptuv->dataformat,
rows[mb_y + base_h] + rmb_x);
step++;
if ((step & 0x3) == 0)
{
offset = 1 - offset;
x -= 32;
}
else
{
x += 32;
}
}
}
if (mb_h & 0x1)
{
int rmb_x = 0;
int x = 0;
int ry;
ry = mb_y * 2 * 32;
for (mb_x = 0; mb_x < mb_w; mb_x++, x += 32, rmb_x += 64 * 32)
_tex_sub_2d(tex->gc, x, ry, 64, 32, tex->ptuv->format, tex->ptuv->dataformat, rows[mb_y + base_h] + rmb_x);
}
}