From dd7595a3475407a7fa96a97393bae8c5220e8762 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Wed, 4 Jan 2012 18:41:13 +1000 Subject: Add the base Enlightenment Foundation Libraries - eina, eet, evas, ecore, embryo, and edje. Note that embryo wont be used, but I'm not sure yet if you can build edje without it. --- .../evas/src/lib/engines/common/evas_font_draw.c | 615 +++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 libraries/evas/src/lib/engines/common/evas_font_draw.c (limited to 'libraries/evas/src/lib/engines/common/evas_font_draw.c') diff --git a/libraries/evas/src/lib/engines/common/evas_font_draw.c b/libraries/evas/src/lib/engines/common/evas_font_draw.c new file mode 100644 index 0000000..94aa085 --- /dev/null +++ b/libraries/evas/src/lib/engines/common/evas_font_draw.c @@ -0,0 +1,615 @@ +#include "evas_common.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" + +/* Forcibly disable the broken word/metric caching. */ +#ifdef METRIC_CACHE +# undef METRIC_CACHE +#endif + +#ifdef WORD_CACHE +# undef WORD_CACHE +#endif + +#define WORD_CACHE_MAXLEN 50 +/* How many to cache */ +#define WORD_CACHE_NWORDS 40 + +static int max_cached_words = WORD_CACHE_NWORDS; + +struct prword +{ + EINA_INLIST; + struct cinfo *cinfo; + Evas_Text_Props text_props; + DATA8 *im; + int roww; + int width; + int height; + int baseline; +}; + +struct cinfo +{ + FT_UInt index; + struct + { + int x, y; + } pos; + int posx; + RGBA_Font_Glyph *fg; + struct + { + int w,h; + int rows; + unsigned char *data; + } bm; +}; + + +#if defined(METRIC_CACHE) || defined(WORD_CACHE) +LK(lock_words); // for word cache call +static Eina_Inlist *words = NULL; +static struct prword *evas_font_word_prerender(RGBA_Draw_Context *dc, const Evas_Text_Props *text_props); +#endif + +EAPI void +evas_common_font_draw_init(void) +{ + char *p; + int tmp; + + if ((p = getenv("EVAS_WORD_CACHE_MAX_WORDS"))) + { + tmp = strtol(p,NULL,10); + /* 0 to disable of course */ + if (tmp > -1 && tmp < 500){ + max_cached_words = tmp; + } + } +} + +#ifdef EVAS_FRAME_QUEUING +EAPI void +evas_common_font_draw_finish(void) +{ +} +#endif + +/* + * 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). + */ +static void +evas_common_font_draw_internal(RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Font *fn __UNUSED__, int x, int y, + const Evas_Text_Props *text_props, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, + int ext_h, int im_w, int im_h __UNUSED__) +{ + DATA32 *im; + RGBA_Font_Int *fi; + EVAS_FONT_WALK_TEXT_INIT(); + + fi = text_props->font_instance; + if (!fi) return; + + evas_common_font_int_reload(fi); + + if (fi->src->current_size != fi->size) + { + FTLOCK(); + FT_Activate_Size(fi->ft.size); + FTUNLOCK(); + fi->src->current_size = fi->size; + } + + +#if defined(METRIC_CACHE) || defined(WORD_CACHE) + unsigned int len; + + len = text_props->len; + + if (len > 2 && (len < WORD_CACHE_MAXLEN)) + { + struct prword *word; + + word = + evas_font_word_prerender(dc, text_props); + if (word) + { + int j, rowstart, rowend, xstart, xrun; + + im = dst->image.data; + xrun = word->width; + y -= word->baseline; + xstart = 0; + rowstart = 0; + rowend = word->height; + /* Clip to extent */ + if (x + xrun > ext_x + ext_w) + { + xrun -= x + xrun - ext_x - ext_w; + } + if (x < ext_x) + { + int excess = ext_x - x; + xstart = excess - 1; + xrun -= excess; + x = ext_x; + } + if (y + rowend > ext_y + ext_h) + { + rowend -= (y - ext_y + rowend - ext_h); + } + if (y < ext_y) + { + int excess = ext_y - y; + rowstart += excess; + //rowend -= excess; + // y = ext_y; + } + + if (xrun < 1) return; +/* For some reason, metric and word chache are exactly the same except for + * this piece of code that makes metric go nuts. ATM, we'll just go the + * WORD_CACHE path. */ +#if defined(METRIC_CACHE) || defined(WORD_CACHE) + if (word->im) + { + for (j = rowstart ; j < rowend ; j ++) + { + func(NULL, word->im + (word->roww * j) + xstart, dc->col.col, + im + ((y + j) * im_w) + x, xrun); + } + return; + } +# elif defined(METRIC_CACHE) + unsigned int ind; + + y += word->baseline; + for (ind = 0 ; ind < len ; ind ++) + { + // FIXME Do we need to draw? + struct cinfo *ci = word->cinfo + ind; + for (j = rowstart ; j < rowend ; j ++) + { + if ((ci->fg->ext_dat) && (dc->font_ext.func.gl_draw)) + { + /* ext glyph draw */ + dc->font_ext.func.gl_draw(dc->font_ext.data, + (void *)dst, + dc, ci->fg, + x + ci->pos.x, + y - ci->bm.h + j); + } + else + { + func(NULL, word->im + (word->roww * j) + xstart, + dc->col.col, im + ((y + j) * im_w) + x, xrun); + } + } + } + return; +# endif + } + } +#endif + + im = dst->image.data; + + EVAS_FONT_WALK_TEXT_START() + { + FT_UInt idx; + RGBA_Font_Glyph *fg; + int chr_x, chr_y; + + if (!EVAS_FONT_WALK_IS_VISIBLE) continue; + + idx = EVAS_FONT_WALK_INDEX; + + LKL(fi->ft_mutex); + fg = evas_common_font_int_cache_glyph_get(fi, idx); + if (!fg) + { + LKU(fi->ft_mutex); + continue; + } + + LKU(fi->ft_mutex); + + if (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; + } + + chr_x = x + EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR; + chr_y = y + EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR; + + if (chr_x < (ext_x + ext_w)) + { + DATA8 *data; + int i, j, w, h; + + data = fg->glyph_out->bitmap.buffer; + j = fg->glyph_out->bitmap.pitch; + w = fg->glyph_out->bitmap.width; + if (j < w) j = w; + h = fg->glyph_out->bitmap.rows; + /* + if ((fg->glyph_out->bitmap.pixel_mode == ft_pixel_mode_grays) + && (fg->glyph_out->bitmap.num_grays == 256) + ) + */ + { + if ((j > 0) && (chr_x + w > ext_x)) + { + if ((fg->ext_dat) && (dc->font_ext.func.gl_draw)) + { + /* ext glyph draw */ + dc->font_ext.func.gl_draw(dc->font_ext.data, + (void *)dst, + dc, fg, chr_x, + y - (chr_y - y)); + } + else + { + if ((fg->glyph_out->bitmap.num_grays == 256) && + (fg->glyph_out->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)) + { + for (i = 0; i < h; i++) + { + int dx, dy; + int in_x, in_w; + + in_x = 0; + in_w = 0; + dx = chr_x; + dy = y - (chr_y - i - y); +#ifdef EVAS_SLI + if (((dy) % dc->sli.h) == dc->sli.y) +#endif + { + if ((dx < (ext_x + ext_w)) && + (dy >= (ext_y)) && + (dy < (ext_y + ext_h))) + { + if (dx + w > (ext_x + ext_w)) + in_w += (dx + w) - (ext_x + ext_w); + if (dx < ext_x) + { + in_w += ext_x - dx; + in_x = ext_x - dx; + dx = ext_x; + } + if (in_w < w) + { + func(NULL, data + (i * j) + in_x, dc->col.col, + im + (dy * im_w) + dx, w - in_w); + } + } + } + } + } + else + { + DATA8 *tmpbuf = NULL, *dp, *tp, bits; + int bi, bj; + const DATA8 bitrepl[2] = {0x0, 0xff}; + + tmpbuf = alloca(w); + for (i = 0; i < h; i++) + { + int dx, dy; + int in_x, in_w, end; + + in_x = 0; + in_w = 0; + dx = chr_x; + dy = y - (chr_y - i - y); +#ifdef EVAS_SLI + if (((dy) % dc->sli.h) == dc->sli.y) +#endif + { + tp = tmpbuf; + dp = data + (i * fg->glyph_out->bitmap.pitch); + for (bi = 0; bi < w; bi += 8) + { + bits = *dp; + if ((w - bi) < 8) end = w - bi; + else end = 8; + for (bj = 0; bj < end; bj++) + { + *tp = bitrepl[(bits >> (7 - bj)) & 0x1]; + tp++; + } + dp++; + } + if ((dx < (ext_x + ext_w)) && + (dy >= (ext_y)) && + (dy < (ext_y + ext_h))) + { + if (dx + w > (ext_x + ext_w)) + in_w += (dx + w) - (ext_x + ext_w); + if (dx < ext_x) + { + in_w += ext_x - dx; + in_x = ext_x - dx; + dx = ext_x; + } + if (in_w < w) + { + func(NULL, tmpbuf + in_x, dc->col.col, + im + (dy * im_w) + dx, w - in_w); + } + } + } + } + } + } + } + } + } + else + break; + } + EVAS_FONT_WALK_TEXT_END(); + evas_common_font_int_use_trim(); +} + +EAPI void +evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Font *fn, int x, int y, const Evas_Text_Props *text_props) +{ + int ext_x, ext_y, ext_w, ext_h; + int im_w, im_h; + RGBA_Gfx_Func func; + Cutout_Rects *rects; + Cutout_Rect *r; + int c, cx, cy, cw, ch; + int i; + + im_w = dst->cache_entry.w; + im_h = dst->cache_entry.h; + + 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; + if (ext_h <= 0) return; + +#ifdef EVAS_FRAME_QUEUING + LKL(fn->lock); +#endif +// evas_common_font_size_use(fn); + func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst, 1, dc->render_op); + + if (!dc->cutout.rects) + { + evas_common_font_draw_internal(dst, dc, fn, x, y, text_props, + func, ext_x, ext_y, ext_w, ext_h, + im_w, im_h); + } + else + { + 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)) + { + rects = evas_common_draw_context_apply_cutouts(dc); + for (i = 0; i < rects->active; ++i) + { + r = rects->rects + i; + evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h); + evas_common_font_draw_internal(dst, dc, fn, x, y, text_props, + func, r->x, r->y, r->w, r->h, + im_w, im_h); + } + evas_common_draw_context_apply_clear_cutouts(rects); + } + dc->clip.use = c; dc->clip.x = cx; dc->clip.y = cy; dc->clip.w = cw; dc->clip.h = ch; + } +#ifdef EVAS_FRAME_QUEUING + LKU(fn->lock); +#endif +} + +/* Only used if cache is on */ +#if defined(METRIC_CACHE) || defined(WORD_CACHE) + +static Eina_Bool +_evas_font_word_prerender_text_props_equal(const Evas_Text_Props *_a, const Evas_Text_Props *_b) +{ + Evas_Font_Glyph_Info *gl1, *gl2; + size_t i; + + if ((_a->len != _b->len) || + (_a->font_instance != _b->font_instance)) + return EINA_FALSE; + + gl1 = _a->info->glyph + _a->start; + gl2 = _b->info->glyph + _b->start; + i = _a->len; + for ( ; (i > 0) && (gl1->index == gl2->index) ; i--, gl1++, gl2++) + ; + + return (i == 0); +} + +static struct prword * +evas_font_word_prerender(RGBA_Draw_Context *dc, const Evas_Text_Props *text_props) +{ + struct cinfo *metrics; + unsigned char *im; + int width; + int height, above, below, baseline, descent; + unsigned int i,j; + struct prword *w; + int last_delta = 0; + Eina_Unicode gl; + struct cinfo *ci; + unsigned int len = text_props->len; + RGBA_Font_Int *fi = (RGBA_Font_Int *) text_props->font_instance; + EVAS_FONT_WALK_TEXT_INIT(); + +# ifndef METRIC_CACHE + gl = dc->font_ext.func.gl_new ? 1: 0; + if (gl) return NULL; +# endif + + LKL(lock_words); + EINA_INLIST_FOREACH(words,w) + { + if (_evas_font_word_prerender_text_props_equal(&w->text_props, + text_props)) + { + words = eina_inlist_promote(words, EINA_INLIST_GET(w)); + LKU(lock_words); + return w; + } + } + LKU(lock_words); + + gl = dc->font_ext.func.gl_new ? 1: 0; + + above = 0; below = 0; baseline = 0; height = 0; descent = 0; + + /* First pass: Work out how big and populate */ + metrics = malloc(sizeof(struct cinfo) * len); + ci = metrics; + EVAS_FONT_WALK_TEXT_START() + { + FT_UInt index; + RGBA_Font_Glyph *fg; + index = EVAS_FONT_WALK_INDEX; + LKL(fi->ft_mutex); + fg = evas_common_font_int_cache_glyph_get(fi, index); + if (!fg) + { + LKU(fi->ft_mutex); + continue; + } + + LKU(fi->ft_mutex); + EVAS_FONT_WALK_TEXT_WORK(); + /* Currently broken with invisible chars if (!EVAS_FONT_WALK_IS_VISIBLE) continue; */ + ci->index = index; + ci->fg = fg; + + if (gl) + { + ci->fg->ext_dat =dc->font_ext.func.gl_new(dc->font_ext.data,ci->fg); + ci->fg->ext_dat_free = dc->font_ext.func.gl_free; + } + ci->bm.data = ci->fg->glyph_out->bitmap.buffer; + ci->bm.w = MAX(ci->fg->glyph_out->bitmap.pitch, + ci->fg->glyph_out->bitmap.width); + ci->bm.rows = ci->fg->glyph_out->bitmap.rows; + ci->bm.h = ci->fg->glyph_out->top; + above = ci->bm.rows - (ci->bm.rows - ci->bm.h); + below = ci->bm.rows - ci->bm.h; + if (below > descent) descent = below; + if (above > baseline) baseline = above; + ci->pos.x = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR; + ci->pos.y = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR; + last_delta = EVAS_FONT_WALK_X_ADV - + (ci->bm.w + ci->fg->glyph_out->left); + ci++; + } + EVAS_FONT_WALK_TEXT_END(); + + /* First loop done */ + width = EVAS_FONT_WALK_PEN_X; + if (last_delta < 0) + width -= last_delta; + width = (width & 0x7) ? width + (8 - (width & 0x7)) : width; + + height = baseline + descent; + if (!gl) + { + im = calloc(height, width); + for (i = 0 ; i < len ; i ++) + { + struct cinfo *cin = metrics + i; + + for (j = 0 ; j < cin->bm.rows ; j ++) + { + int correction; /* Used to remove negative inset and such */ + if (cin->pos.x < 0) + correction = -cin->pos.x; + else + correction = 0; + + memcpy(im + cin->pos.x + (j + baseline - cin->bm.h) * width + + correction, + cin->bm.data + j * cin->bm.w + correction, + cin->bm.w - correction); + } + } + } + else + { + im = NULL; + } + + /* Save it */ + struct prword *save; + + save = malloc(sizeof(struct prword)); + save->cinfo = metrics; + evas_common_text_props_content_copy_and_ref(&save->text_props, text_props); + save->im = im; + save->width = EVAS_FONT_WALK_PEN_X; + if (last_delta < 0) + save->width += last_delta; + save->roww = width; + save->height = height; + save->baseline = baseline; + LKL(lock_words); + words = eina_inlist_prepend(words, EINA_INLIST_GET(save)); + + /* Clean up if too long */ + if (eina_inlist_count(words) > max_cached_words) + { + struct prword *last = (struct prword *)(words->last); + + if (last) + { + if (last->im) free(last->im); + if (last->cinfo) free(last->cinfo); + evas_common_text_props_content_unref(&last->text_props); + words = eina_inlist_remove(words, EINA_INLIST_GET(last)); + free(last); + } + } + LKU(lock_words); + + return save; +} +#endif -- cgit v1.1