efl/src/lib/evas/common/evas_font_draw.c

510 lines
15 KiB
C

#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_blend_private.h"
#include "language/evas_bidi_utils.h" /*defines BIDI_SUPPORT if possible */
#include "evas_font_private.h" /* for Frame-Queuing support */
#include "evas_font_ot.h"
#ifdef EVAS_CSERVE2
#include "../cserve2/evas_cs2_private.h"
#endif
struct _Evas_Glyph
{
RGBA_Font_Glyph *fg;
int x, y;
FT_UInt idx;
};
EAPI void
evas_common_font_draw_init(void)
{
}
static void *
_evas_font_image_new(RGBA_Font_Glyph *fg, int alpha, Evas_Colorspace cspace)
{
DATA32 *image_data;
int src_w, src_h;
if (!fg) return NULL;
image_data = (DATA32 *)fg->glyph_out->bitmap.buffer;
src_w = fg->glyph_out->bitmap.width;
src_h = fg->glyph_out->bitmap.rows;
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
{
Evas_Cache2 *cache = evas_common_image_cache2_get();
return evas_cache2_image_data(cache, src_w, src_h, image_data, alpha, cspace);
}
#endif
return evas_cache_image_data(evas_common_image_cache_get(), src_w, src_h, image_data, alpha, cspace);
}
static void
_evas_font_image_free(void *image)
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get() && evas_cache2_image_cached(image))
{
evas_cache2_image_close(image);
return;
}
#endif
evas_cache_image_drop(image);
}
static void
_evas_font_image_draw(void *context, void *surface, void *image, RGBA_Font_Glyph *fg, int x, int y, int w, int h, int smooth)
{
RGBA_Image *im;
int src_w, src_h;
if (!image || !fg) return;
im = image;
src_w = fg->glyph_out->bitmap.width;
src_h = fg->glyph_out->bitmap.rows;
#ifdef BUILD_PIPE_RENDER
if ((eina_cpu_count() > 1))
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_load_data(&im->cache_entry);
#endif
evas_common_rgba_image_scalecache_prepare((Image_Entry *)(im),
surface, context, smooth,
0, 0, src_w, src_h,
x, y, w, h);
evas_common_pipe_image_draw(im, surface, context, smooth,
0, 0, src_w, src_h,
x, y, w, h);
}
else
#endif
{
evas_common_rgba_image_scalecache_prepare
(&im->cache_entry, surface, context, smooth,
0, 0, src_w, src_h,
x, y, w, h);
evas_common_rgba_image_scalecache_do
(&im->cache_entry, surface, context, smooth,
0, 0, src_w, src_h,
x, y, w, h);
evas_common_cpu_end_opt();
}
}
/*
* BiDi handling: We receive the shaped string + other props from text_props,
* we need to reorder it so we'll have the visual string (the way we draw)
* and then for kerning we have to switch the order of the kerning query (as the prev
* is on the right, and not on the left).
*/
EAPI Eina_Bool
evas_common_font_rgba_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y,
Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func EINA_UNUSED, int ext_x, int ext_y, int ext_w,
int ext_h, int im_w, int im_h EINA_UNUSED)
{
Evas_Glyph *glyph;
if (!glyphs) return EINA_FALSE;
if (!glyphs->array) return EINA_FALSE;
EINA_INARRAY_FOREACH(glyphs->array, glyph)
{
RGBA_Font_Glyph *fg;
int chr_x, chr_y, w, h;
fg = glyph->fg;
w = fg->glyph_out->bitmap.width;
h = fg->glyph_out->bitmap.rows;
if (FT_HAS_FIXED_SIZES(fg->fi->src->ft.face))
{
if ((fg->fi->bitmap_scalable & EFL_TEXT_FONT_BITMAP_SCALABLE_COLOR) &&
FT_HAS_COLOR(fg->fi->src->ft.face))
{
w *= fg->fi->scale_factor;
h *= fg->fi->scale_factor;
}
}
if ((!fg->ext_dat) && (dc->font_ext.func.gl_new))
{
/* extension calls */
fg->ext_dat = dc->font_ext.func.gl_new(dc->font_ext.data, fg);
fg->ext_dat_free = dc->font_ext.func.gl_free;
}
if ((!fg->ext_dat) && FT_HAS_COLOR(fg->fi->src->ft.face))
{
if (dc->font_ext.func.gl_image_new)
{
/* extension calls */
fg->ext_dat = dc->font_ext.func.gl_image_new
(dc->font_ext.data, fg, EINA_TRUE, EVAS_COLORSPACE_ARGB8888);
fg->ext_dat_free = dc->font_ext.func.gl_image_free;
}
else
{
fg->ext_dat = _evas_font_image_new(fg, EINA_TRUE, EVAS_COLORSPACE_ARGB8888);
fg->ext_dat_free = _evas_font_image_free;
}
}
chr_x = x + glyph->x;
chr_y = y + glyph->y;
if (chr_x < (ext_x + ext_w))
{
if ((w > 0) && ((chr_x + w) > ext_x))
{
if (fg->glyph_out->rle)
{
if ((fg->ext_dat) && (dc->font_ext.func.gl_draw))
dc->font_ext.func.gl_draw(dc->font_ext.data, dst,
dc, fg,
chr_x, y - (chr_y - y), w, h);
else
// TODO: scale with evas_font_compress_draw.c...
evas_common_font_glyph_draw(fg, dc, dst, im_w,
chr_x, y - (chr_y - y), w, h,
ext_x, ext_y,
ext_w, ext_h);
}
else if ((fg->ext_dat) && FT_HAS_COLOR(fg->fi->src->ft.face))
{
if (dc->font_ext.func.gl_image_draw)
dc->font_ext.func.gl_image_draw
(dc->font_ext.data, fg->ext_dat,
chr_x, y - (chr_y - y), w, h, EINA_TRUE);
else
_evas_font_image_draw
(dc, dst, fg->ext_dat, fg,
chr_x, y - (chr_y - y), w, h, EINA_TRUE);
}
}
}
else
break;
}
return EINA_TRUE;
}
void
evas_common_font_glyphs_ref(Evas_Glyph_Array *array)
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get() && !array->refcount)
{
Eina_Iterator *iter;
Evas_Glyph *glyph;
iter = eina_inarray_iterator_new(array->array);
EINA_ITERATOR_FOREACH(iter, glyph)
evas_cserve2_font_glyph_ref(glyph->fg->glyph_out, EINA_TRUE);
eina_iterator_free(iter);
}
#endif
array->refcount++;
}
void
evas_common_font_glyphs_unref(Evas_Glyph_Array *array)
{
if (--array->refcount) return;
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
{
Eina_Iterator *iter;
Evas_Glyph *glyph;
iter = eina_inarray_iterator_new(array->array);
EINA_ITERATOR_FOREACH(iter, glyph)
evas_cserve2_font_glyph_ref(glyph->fg->glyph_out, EINA_FALSE);
eina_iterator_free(iter);
}
#endif
eina_inarray_free(array->array);
evas_common_font_int_unref(array->fi);
free(array);
}
void
evas_common_font_fonts_ref(Evas_Font_Array *array)
{
array->refcount++;
}
void
evas_common_font_fonts_unref(Evas_Font_Array *array)
{
if (--array->refcount) return;
eina_inarray_free(array->array);
free(array);
}
EAPI void
evas_common_font_draw_prepare(Evas_Text_Props *text_props)
{
RGBA_Font_Int *fi;
RGBA_Font_Glyph *fg = NULL;
Eina_Inarray *glyphs;
size_t unit = 32;
Eina_Bool reused_glyphs;
EVAS_FONT_WALK_TEXT_INIT();
fi = text_props->font_instance;
if (!fi) return;
if ((!text_props->changed) &&
(text_props->generation == fi->generation) &&
text_props->glyphs)
return;
if (text_props->len < unit) unit = text_props->len;
if (text_props->glyphs && text_props->glyphs->refcount == 1)
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
{
Eina_Iterator *iter;
Evas_Glyph *glyph;
iter = eina_inarray_iterator_new(text_props->glyphs->array);
EINA_ITERATOR_FOREACH(iter, glyph)
evas_cserve2_font_glyph_ref(glyph->fg->glyph_out, EINA_FALSE);
eina_iterator_free(iter);
}
#endif
glyphs = text_props->glyphs->array;
glyphs->len = 0;
reused_glyphs = EINA_TRUE;
}
else
{
glyphs = eina_inarray_new(sizeof(Evas_Glyph), unit);
reused_glyphs = EINA_FALSE;
}
evas_common_font_int_reload(fi);
if (fi->src->current_size != fi->size)
{
evas_common_font_source_reload(fi->src);
FTLOCK();
FT_Activate_Size(fi->ft.size);
FTUNLOCK();
fi->src->current_size = fi->size;
}
EVAS_FONT_WALK_TEXT_START()
{
Evas_Glyph *glyph;
FT_UInt idx;
if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
idx = EVAS_FONT_WALK_INDEX;
fg = evas_common_font_int_cache_glyph_get(fi, idx);
if (!fg) continue;
if (!evas_common_font_int_cache_glyph_render(fg))
{
fg = NULL;
goto error;
}
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cserve2_font_glyph_ref(fg->glyph_out, EINA_TRUE);
#endif
glyph = eina_inarray_grow(glyphs, 1);
if (!glyph) goto error;
glyph->fg = fg;
glyph->idx = idx;
glyph->x = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR;
glyph->y = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR;
}
EVAS_FONT_WALK_TEXT_END();
if (!reused_glyphs)
{
if (text_props->glyphs) evas_common_font_glyphs_unref(text_props->glyphs);
text_props->glyphs = malloc(sizeof(*text_props->glyphs));
if (!text_props->glyphs) goto error;
text_props->glyphs->refcount = 1;
text_props->glyphs->array = glyphs;
text_props->glyphs->fi = fi;
fi->references++;
}
/* check if there's a request queue in fi, if so ask cserve2 to render
* those glyphs
*/
text_props->generation = fi->generation;
text_props->changed = EINA_FALSE;
return;
error:
eina_inarray_free(glyphs);
}
EAPI Eina_Bool
evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, Evas_Common_Font_Draw_Cb cb)
{
int ext_x, ext_y, ext_w, ext_h;
int im_w, im_h;
RGBA_Gfx_Func func;
Cutout_Rect *r;
int c, cx, cy, cw, ch;
int i;
if (!glyphs) return EINA_FALSE;
im_w = dst->cache_entry.w;
im_h = dst->cache_entry.h;
// evas_common_font_size_use(fn);
func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst->cache_entry.flags.alpha, 1, dc->render_op);
if (!dc->cutout.rects)
{
ext_x = 0; ext_y = 0; ext_w = im_w; ext_h = im_h;
if (dc->clip.use)
{
ext_x = dc->clip.x;
ext_y = dc->clip.y;
ext_w = dc->clip.w;
ext_h = dc->clip.h;
if (ext_x < 0)
{
ext_w += ext_x;
ext_x = 0;
}
if (ext_y < 0)
{
ext_h += ext_y;
ext_y = 0;
}
if ((ext_x + ext_w) > im_w)
ext_w = im_w - ext_x;
if ((ext_y + ext_h) > im_h)
ext_h = im_h - ext_y;
}
if (ext_w <= 0) return EINA_FALSE;
if (ext_h <= 0) return EINA_FALSE;
return cb(dst, dc, x, y, glyphs,
func, ext_x, ext_y, ext_w, ext_h,
im_w, im_h);
}
else
{
Eina_Bool ret = EINA_FALSE;
c = dc->clip.use; cx = dc->clip.x; cy = dc->clip.y; cw = dc->clip.w; ch = dc->clip.h;
evas_common_draw_context_clip_clip(dc, 0, 0, dst->cache_entry.w, dst->cache_entry.h);
/* our clip is 0 size.. abort */
if ((dc->clip.w > 0) && (dc->clip.h > 0))
{
dc->cache.rects = evas_common_draw_context_apply_cutouts(dc, dc->cache.rects);
for (i = 0; i < dc->cache.rects->active; ++i)
{
r = dc->cache.rects->rects + i;
evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
ret |= cb(dst, dc, x, y, glyphs,
func, r->x, r->y, r->w, r->h,
im_w, im_h);
}
evas_common_draw_context_cache_update(dc);
}
dc->clip.use = c; dc->clip.x = cx; dc->clip.y = cy; dc->clip.w = cw; dc->clip.h = ch;
return ret;
}
}
EAPI void
evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs)
{
evas_common_font_draw_cb(dst, dc, x, y, glyphs,
evas_common_font_rgba_draw);
}
EAPI void
evas_common_font_draw_do(const Cutout_Rects *reuse, const Eina_Rectangle *clip, RGBA_Gfx_Func func,
RGBA_Image *dst, RGBA_Draw_Context *dc,
int x, int y, const Evas_Text_Props *text_props)
{
Eina_Rectangle area;
Cutout_Rect *r;
int i;
int im_w, im_h;
im_w = dst->cache_entry.w;
im_h = dst->cache_entry.h;
if (!reuse)
{
evas_common_draw_context_clip_clip(dc,
clip->x, clip->y,
clip->w, clip->h);
evas_common_font_rgba_draw(dst, dc, x, y, text_props->glyphs,
func,
dc->clip.x, dc->clip.y,
dc->clip.w, dc->clip.h,
im_w, im_h);
return;
}
for (i = 0; i < reuse->active; ++i)
{
r = reuse->rects + i;
EINA_RECTANGLE_SET(&area, r->x, r->y, r->w - 1, r->h - 1);
if (!eina_rectangle_intersection(&area, clip)) continue ;
evas_common_draw_context_set_clip(dc, area.x, area.y, area.w, area.h);
evas_common_font_rgba_draw(dst, dc, x, y, text_props->glyphs,
func, area.x, area.y, area.w, area.h,
im_w, im_h);
}
}
EAPI Eina_Bool
evas_common_font_draw_prepare_cutout(Cutout_Rects **reuse, RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Gfx_Func *func)
{
int im_w, im_h;
im_w = dst->cache_entry.w;
im_h = dst->cache_entry.h;
*func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst->cache_entry.flags.alpha, 1, dc->render_op);
evas_common_draw_context_clip_clip(dc, 0, 0, im_w, im_h);
if (dc->clip.w <= 0) return EINA_FALSE;
if (dc->clip.h <= 0) return EINA_FALSE;
if (dc->cutout.rects)
{
*reuse = evas_common_draw_context_apply_cutouts(dc, *reuse);
}
return EINA_TRUE;
}