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

800 lines
21 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_font_private.h"
#ifdef EVAS_CSERVE2
# include "../cserve2/evas_cs2_private.h"
#endif
#include FT_OUTLINE_H
#include FT_SYNTHESIS_H
FT_Library evas_ft_lib = 0;
static int initialised = 0;
LK(lock_font_draw); // for freetype2 API calls
LK(lock_bidi); // for evas bidi internal usage.
LK(lock_ot); // for evas bidi internal usage.
EAPI void
evas_common_font_init(void)
{
int error;
const char *s;
initialised++;
if (initialised != 1) return;
error = FT_Init_FreeType(&evas_ft_lib);
if (error) return;
evas_common_font_load_init();
evas_common_font_draw_init();
s = getenv("EVAS_FONT_DPI");
if (s)
{
int dpi = atoi(s);
if (dpi > 0) evas_common_font_dpi_set(dpi);
}
LKI(lock_font_draw);
LKI(lock_bidi);
LKI(lock_ot);
}
EAPI void
evas_common_font_shutdown(void)
{
if (initialised < 1) return;
initialised--;
if (initialised != 0) return;
evas_common_font_load_shutdown();
evas_common_font_cache_set(0);
evas_common_font_flush();
FT_Done_FreeType(evas_ft_lib);
evas_ft_lib = 0;
LKD(lock_font_draw);
LKD(lock_bidi);
LKD(lock_ot);
}
EAPI void
evas_common_font_font_all_unload(void)
{
evas_common_font_all_clear();
}
EAPI int
evas_common_font_instance_ascent_get(RGBA_Font_Int *fi)
{
int val;
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 (!FT_IS_SCALABLE(fi->src->ft.face))
{
WRN("NOT SCALABLE!");
}
val = (int)fi->src->ft.face->size->metrics.ascender;
return FONT_METRIC_ROUNDUP(val);
// printf("%i | %i\n", val, val >> 6);
// if (fi->src->ft.face->units_per_EM == 0)
// return val;
// dv = (fi->src->ft.orig_upem * 2048) / fi->src->ft.face->units_per_EM;
// ret = (val * fi->src->ft.face->size->metrics.y_scale) / (dv * dv);
// return ret;
}
EAPI int
evas_common_font_instance_descent_get(RGBA_Font_Int *fi)
{
int val;
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;
}
val = -(int)fi->src->ft.face->size->metrics.descender;
return FONT_METRIC_ROUNDUP(val);
// if (fi->src->ft.face->units_per_EM == 0)
// return val;
// dv = (fi->src->ft.orig_upem * 2048) / fi->src->ft.face->units_per_EM;
// ret = (val * fi->src->ft.face->size->metrics.y_scale) / (dv * dv);
// return ret;
}
EAPI int
evas_common_font_instance_max_ascent_get(RGBA_Font_Int *fi)
{
int val, dv;
int ret;
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 ((fi->src->ft.face->bbox.yMax == 0) &&
(fi->src->ft.face->bbox.yMin == 0) &&
(fi->src->ft.face->units_per_EM == 0))
val = FONT_METRIC_ROUNDUP((int)fi->src->ft.face->size->metrics.ascender);
else
val = (int)fi->src->ft.face->bbox.yMax;
if (fi->src->ft.face->units_per_EM == 0)
return val;
dv = (fi->src->ft.orig_upem * 2048) / fi->src->ft.face->units_per_EM;
ret = FONT_METRIC_CONV(val, dv, fi->src->ft.face->size->metrics.y_scale);
return ret;
}
EAPI int
evas_common_font_instance_max_descent_get(RGBA_Font_Int *fi)
{
int val, dv;
int ret;
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 ((fi->src->ft.face->bbox.yMax == 0) &&
(fi->src->ft.face->bbox.yMin == 0) &&
(fi->src->ft.face->units_per_EM == 0))
val = FONT_METRIC_ROUNDUP(-(int)fi->src->ft.face->size->metrics.descender);
else
val = -(int)fi->src->ft.face->bbox.yMin;
if (fi->src->ft.face->units_per_EM == 0)
return val;
dv = (fi->src->ft.orig_upem * 2048) / fi->src->ft.face->units_per_EM;
ret = FONT_METRIC_CONV(val, dv, fi->src->ft.face->size->metrics.y_scale);
return ret;
}
EAPI int
evas_common_font_ascent_get(RGBA_Font *fn)
{
// evas_common_font_size_use(fn);
return evas_common_font_instance_ascent_get(fn->fonts->data);
}
EAPI int
evas_common_font_descent_get(RGBA_Font *fn)
{
// evas_common_font_size_use(fn);
return evas_common_font_instance_descent_get(fn->fonts->data);
}
EAPI int
evas_common_font_max_ascent_get(RGBA_Font *fn)
{
// evas_common_font_size_use(fn);
return evas_common_font_instance_max_ascent_get(fn->fonts->data);
}
EAPI int
evas_common_font_max_descent_get(RGBA_Font *fn)
{
// evas_common_font_size_use(fn);
return evas_common_font_instance_max_descent_get(fn->fonts->data);
}
EAPI int
evas_common_font_get_line_advance(RGBA_Font *fn)
{
int val;
RGBA_Font_Int *fi;
// evas_common_font_size_use(fn);
fi = fn->fonts->data;
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;
}
val = (int)fi->src->ft.face->size->metrics.height;
if ((fi->src->ft.face->bbox.yMax == 0) &&
(fi->src->ft.face->bbox.yMin == 0) &&
(fi->src->ft.face->units_per_EM == 0))
return FONT_METRIC_ROUNDUP(val);
else if (fi->src->ft.face->units_per_EM == 0)
return val;
return FONT_METRIC_ROUNDUP(val);
// dv = (fi->src->ft.orig_upem * 2048) / fi->src->ft.face->units_per_EM;
// ret = (val * fi->src->ft.face->size->metrics.y_scale) / (dv * dv);
// return ret;
}
EAPI int
evas_common_font_instance_underline_position_get(RGBA_Font_Int *fi)
{
int position = 0;
if (!fi) goto end;
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;
}
position = FT_MulFix(fi->src->ft.face->underline_position,
fi->src->ft.face->size->metrics.x_scale);
position = FONT_METRIC_ROUNDUP(abs(position));
end:
/* This almost surely means a broken font, offset at least by one pixel. */
if (position == 0)
position = 1;
return position;
}
EAPI int
evas_common_font_instance_underline_thickness_get(RGBA_Font_Int *fi)
{
int thickness = 0;
if (!fi) goto end;
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;
}
thickness = FT_MulFix(fi->src->ft.face->underline_thickness,
fi->src->ft.face->size->metrics.x_scale);
thickness = FONT_METRIC_ROUNDUP(thickness);
end:
/* This almost surely means a broken font, make it at least one pixel. */
if (thickness == 0)
thickness = 1;
return thickness;
}
/* Set of common functions that are used in a couple of places. */
static void
_fash_int2_free(Fash_Int_Map2 *fash)
{
int i;
for (i = 0; i < 256; i++) if (fash->bucket[i]) free(fash->bucket[i]);
free(fash);
}
static void
_fash_int_free(Fash_Int *fash)
{
int i;
for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_int2_free(fash->bucket[i]);
free(fash);
}
static Fash_Int *
_fash_int_new(void)
{
Fash_Int *fash = calloc(1, sizeof(Fash_Int));
fash->freeme = _fash_int_free;
return fash;
}
static Fash_Item_Index_Map *
_fash_int_find(Fash_Int *fash, int item)
{
int grp, maj, min;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
grp = (item >> 16) & 0xff;
maj = (item >> 8) & 0xff;
min = item & 0xff;
if (!fash->bucket[grp]) return NULL;
if (!fash->bucket[grp]->bucket[maj]) return NULL;
return &(fash->bucket[grp]->bucket[maj]->item[min]);
}
static void
_fash_int_add(Fash_Int *fash, int item, RGBA_Font_Int *fint, int idx)
{
int grp, maj, min;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
grp = (item >> 16) & 0xff;
maj = (item >> 8) & 0xff;
min = item & 0xff;
if (!fash->bucket[grp])
fash->bucket[grp] = calloc(1, sizeof(Fash_Int_Map2));
EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]);
if (!fash->bucket[grp]->bucket[maj])
fash->bucket[grp]->bucket[maj] = calloc(1, sizeof(Fash_Int_Map));
EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]);
fash->bucket[grp]->bucket[maj]->item[min].fint = fint;
fash->bucket[grp]->bucket[maj]->item[min].index = idx;
}
static void
_glyph_free(RGBA_Font_Glyph *fg)
{
if ((!fg) || (fg == (void *)(-1))) return;
FT_Done_Glyph(fg->glyph);
/* extension calls */
if (fg->ext_dat_free) fg->ext_dat_free(fg->ext_dat);
if (fg->glyph_out_free) fg->glyph_out_free(fg->glyph_out);
free(fg);
}
static void
_fash_glyph_free(Fash_Glyph_Map *fmap)
{
int i;
for (i = 0; i <= 0xff; i++)
{
RGBA_Font_Glyph *fg = fmap->item[i];
if ((fg) && (fg != (void *)(-1)))
{
_glyph_free(fg);
fmap->item[i] = NULL;
}
}
free(fmap);
}
static void
_fash_gl2_free(Fash_Glyph_Map2 *fash)
{
int i;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_glyph_free(fash->bucket[i]);
free(fash);
}
static void
_fash_gl_free(Fash_Glyph *fash)
{
int i;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
for (i = 0; i < 256; i++) if (fash->bucket[i]) _fash_gl2_free(fash->bucket[i]);
free(fash);
}
static Fash_Glyph *
_fash_gl_new(void)
{
Fash_Glyph *fash = calloc(1, sizeof(Fash_Glyph));
fash->freeme = _fash_gl_free;
return fash;
}
static RGBA_Font_Glyph *
_fash_gl_find(Fash_Glyph *fash, int item)
{
int grp, maj, min;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
grp = (item >> 16) & 0xff;
maj = (item >> 8) & 0xff;
min = item & 0xff;
if (!fash->bucket[grp]) return NULL;
if (!fash->bucket[grp]->bucket[maj]) return NULL;
return fash->bucket[grp]->bucket[maj]->item[min];
}
static void
_fash_gl_add(Fash_Glyph *fash, int item, RGBA_Font_Glyph *glyph)
{
int grp, maj, min;
// 24bits for unicode - v6 up to E01EF (chrs) & 10FFFD for private use (plane 16)
grp = (item >> 16) & 0xff;
maj = (item >> 8) & 0xff;
min = item & 0xff;
if (!fash->bucket[grp])
fash->bucket[grp] = calloc(1, sizeof(Fash_Glyph_Map2));
EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]);
if (!fash->bucket[grp]->bucket[maj])
fash->bucket[grp]->bucket[maj] = calloc(1, sizeof(Fash_Glyph_Map));
EINA_SAFETY_ON_NULL_RETURN(fash->bucket[grp]->bucket[maj]);
fash->bucket[grp]->bucket[maj]->item[min] = glyph;
}
EAPI RGBA_Font_Glyph *
evas_common_font_int_cache_glyph_get(RGBA_Font_Int *fi, FT_UInt idx)
{
RGBA_Font_Glyph *fg;
FT_Error error;
const FT_Int32 hintflags[3] =
{ FT_LOAD_NO_HINTING, FT_LOAD_FORCE_AUTOHINT, FT_LOAD_NO_AUTOHINT };
static FT_Matrix transform = {0x10000, _EVAS_FONT_SLANT_TAN * 0x10000,
0x00000, 0x10000};
evas_common_font_int_promote(fi);
if (fi->fash)
{
fg = _fash_gl_find(fi->fash, idx);
if (fg == (void *)(-1)) return NULL;
else if (fg)
{
#ifdef EVAS_CSERVE2
if (fi->cs2_handler)
{
if (evas_cserve2_font_glyph_used(fi->cs2_handler, idx,
fi->hinting))
return fg;
else
{
_glyph_free(fg);
_fash_gl_add(fi->fash, idx, NULL);
}
}
else return fg;
#else
return fg;
#endif
}
}
// fg = eina_hash_find(fi->glyphs, &hindex);
// if (fg) return fg;
evas_common_font_int_reload(fi);
FTLOCK();
error = FT_Load_Glyph(fi->src->ft.face, idx,
FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP |
hintflags[fi->hinting]);
FTUNLOCK();
if (error)
{
if (!fi->fash) fi->fash = _fash_gl_new();
if (fi->fash) _fash_gl_add(fi->fash, idx, (void *)(-1));
return NULL;
}
/* Transform the outline of Glyph according to runtime_rend. */
if (fi->runtime_rend & FONT_REND_SLANT)
FT_Outline_Transform(&fi->src->ft.face->glyph->outline, &transform);
/* Embolden the outline of Glyph according to rundtime_rend. */
if (fi->runtime_rend & FONT_REND_WEIGHT)
FT_GlyphSlot_Embolden(fi->src->ft.face->glyph);
fg = calloc(1, sizeof(RGBA_Font_Glyph));
if (!fg) return NULL;
FTLOCK();
error = FT_Get_Glyph(fi->src->ft.face->glyph, &(fg->glyph));
FTUNLOCK();
if (error)
{
free(fg);
if (!fi->fash) fi->fash = _fash_gl_new();
if (fi->fash) _fash_gl_add(fi->fash, idx, (void *)(-1));
return NULL;
}
{
FT_BBox outbox;
FT_Glyph_Get_CBox(fg->glyph,
((fi->hinting == 0) ? FT_GLYPH_BBOX_UNSCALED :
FT_GLYPH_BBOX_GRIDFIT),
&outbox);
fg->width = EVAS_FONT_ROUND_26_6_TO_INT(outbox.xMax - outbox.xMin);
fg->x_bear = EVAS_FONT_ROUND_26_6_TO_INT(outbox.xMin);
fg->y_bear = EVAS_FONT_ROUND_26_6_TO_INT(outbox.yMax);
}
fg->index = idx;
fg->fi = fi;
if (!fi->fash) fi->fash = _fash_gl_new();
if (fi->fash) _fash_gl_add(fi->fash, idx, fg);
#ifdef EVAS_CSERVE2
if (fi->cs2_handler)
evas_cserve2_font_glyph_request(fi->cs2_handler, idx, fi->hinting);
#endif
// eina_hash_direct_add(fi->glyphs, &fg->index, fg);
return fg;
}
EAPI Eina_Bool
evas_common_font_int_cache_glyph_render(RGBA_Font_Glyph *fg)
{
int size;
FT_Error error;
RGBA_Font_Int *fi = fg->fi;
FT_BitmapGlyph fbg;
#ifdef EVAS_CSERVE2
if (fi->cs2_handler)
{
fg->glyph_out = evas_cserve2_font_glyph_bitmap_get(fi->cs2_handler,
fg->index,
fg->fi->hinting);
if (!fg->glyph_out)
{
if (!fi->fash) fi->fash = _fash_gl_new();
if (fi->fash) _fash_gl_add(fi->fash, fg->index, (void *)(-1));
free(fg);
return EINA_FALSE;
}
return EINA_TRUE;
}
#endif
/* no cserve2 case */
if (fg->glyph_out)
return EINA_TRUE;
FTLOCK();
error = FT_Glyph_To_Bitmap(&(fg->glyph), FT_RENDER_MODE_NORMAL, 0, 1);
if (error)
{
FT_Done_Glyph(fg->glyph);
FTUNLOCK();
if (!fi->fash) fi->fash = _fash_gl_new();
if (fi->fash) _fash_gl_add(fi->fash, fg->index, (void *)(-1));
free(fg);
return EINA_FALSE;
}
FTUNLOCK();
fbg = (FT_BitmapGlyph)fg->glyph;
fg->glyph_out = malloc(sizeof(RGBA_Font_Glyph_Out));
fg->glyph_out->bitmap.rows = fbg->bitmap.rows;
fg->glyph_out->bitmap.width = fbg->bitmap.width;
fg->glyph_out->bitmap.pitch = fbg->bitmap.pitch;
fg->glyph_out->bitmap.buffer = fbg->bitmap.buffer;
fg->glyph_out->bitmap.num_grays = fbg->bitmap.num_grays;
fg->glyph_out->bitmap.pixel_mode = fbg->bitmap.pixel_mode;
fg->glyph_out_free = free;
/* This '+ 200' is just an estimation of how much memory freetype will use
* on it's size. This value is not really used anywhere in code - it's
* only for statistics. */
size = sizeof(RGBA_Font_Glyph) + sizeof(Eina_List) +
(fg->glyph_out->bitmap.width * fg->glyph_out->bitmap.rows) + 200;
fi->usage += size;
if (fi->inuse) evas_common_font_int_use_increase(size);
return EINA_TRUE;
}
typedef struct _Font_Char_Index Font_Char_Index;
struct _Font_Char_Index
{
FT_UInt index;
Eina_Unicode gl;
};
EAPI FT_UInt
evas_common_get_char_index(RGBA_Font_Int* fi, Eina_Unicode gl)
{
static const unsigned short mapfix[] =
{
0x00b0, 0x7,
0x00b1, 0x8,
0x00b7, 0x1f,
0x03c0, 0x1c,
0x20a4, 0xa3,
0x2260, 0x1d,
0x2264, 0x1a,
0x2265, 0x1b,
0x23ba, 0x10,
0x23bb, 0x11,
0x23bc, 0x13,
0x23bd, 0x14,
0x2409, 0x3,
0x240a, 0x6,
0x240b, 0xa,
0x240c, 0x4,
0x240d, 0x5,
0x2424, 0x9,
0x2500, 0x12,
0x2502, 0x19,
0x250c, 0xd,
0x2510, 0xc,
0x2514, 0xe,
0x2518, 0xb,
0x251c, 0x15,
0x2524, 0x16,
0x252c, 0x18,
0x2534, 0x17,
0x253c, 0xf,
0x2592, 0x2,
0x25c6, 0x1,
};
Font_Char_Index result;
//FT_UInt ret;
#ifdef HAVE_PTHREAD
/// pthread_mutex_lock(&fi->ft_mutex);
#endif
// result = eina_hash_find(fi->indexes, &gl);
// if (result) goto on_correct;
//
// result = malloc(sizeof (Font_Char_Index));
// if (!result)
// {
//#ifdef HAVE_PTHREAD
// pthread_mutex_unlock(&fi->ft_mutex);
//#endif
// return FT_Get_Char_Index(fi->src->ft.face, gl);
// }
evas_common_font_int_reload(fi);
/*
* There is no point in locking FreeType at this point as all caller
* are running in the main loop at a time where there is zero chance
* that something else try to use it.
*/
/* FTLOCK(); */
result.index = FT_Get_Char_Index(fi->src->ft.face, gl);
/* FTUNLOCK(); */
result.gl = gl;
// eina_hash_direct_add(fi->indexes, &result->gl, result);
//
// on_correct:
#ifdef HAVE_PTHREAD
// pthread_mutex_unlock(&fi->ft_mutex);
#endif
// this is a workaround freetype bugs where for a bitmap old style font
// even if it has unicode information and mappings, they are not used
// to find terminal line/drawing chars, so do this by hand with a table
if ((result.index <= 0) && (fi->src->ft.face->num_fixed_sizes == 1) &&
(fi->src->ft.face->num_glyphs < 512))
{
int i, min = 0, max;
// binary search through sorted table of codepoints to new
// codepoints with a guess that bitmap font is playing the old
// game of putting line drawing chars in specific ranges
max = sizeof(mapfix) / (sizeof(mapfix[0]) * 2);
i = (min + max) / 2;
for (;;)
{
unsigned short v;
v = mapfix[i << 1];
if (gl == v)
{
gl = mapfix[(i << 1) + 1];
FTLOCK();
result.index = FT_Get_Char_Index(fi->src->ft.face, gl);
FTUNLOCK();
break;
}
// failure to find at all
if ((max - min) <= 2) break;
// if glyph above out position...
if (gl > v)
{
min = i;
if ((max - min) == 1) i = max;
else i = (min + max) / 2;
}
// if glyph below out position
else if (gl < v)
{
max = i;
if ((max - min) == 1) i = min;
else i = (min + max) / 2;
}
}
}
return result.index;
}
EAPI int
evas_common_font_glyph_search(RGBA_Font *fn, RGBA_Font_Int **fi_ret, Eina_Unicode gl)
{
Eina_List *l;
if (fn->fash)
{
Fash_Item_Index_Map *fm = _fash_int_find(fn->fash, gl);
if (fm)
{
if (fm->fint)
{
*fi_ret = fm->fint;
return fm->index;
}
else if (fm->index == -1) return 0;
}
}
for (l = fn->fonts; l; l = l->next)
{
RGBA_Font_Int *fi;
int idx;
fi = l->data;
#if 0 /* FIXME: charmap user is disabled and use a deprecated data type. */
/*
if (fi->src->charmap) // Charmap loaded, FI/FS blank
{
idx = evas_array_hash_search(fi->src->charmap, gl);
if (idx != 0)
{
evas_common_font_source_load_complete(fi->src);
evas_common_font_int_load_complete(fi);
evas_array_hash_free(fi->src->charmap);
fi->src->charmap = NULL;
*fi_ret = fi;
return idx;
}
}
else
*/
#endif
if (!fi->src->ft.face) /* Charmap not loaded, FI/FS blank */
{
evas_common_font_int_reload(fi);
}
if (fi->src->ft.face)
{
idx = evas_common_get_char_index(fi, gl);
if (idx != 0)
{
if (!fi->ft.size)
evas_common_font_int_load_complete(fi);
if (!fn->fash) fn->fash = _fash_int_new();
if (fn->fash) _fash_int_add(fn->fash, gl, fi, idx);
*fi_ret = fi;
return idx;
}
else
{
if (!fn->fash) fn->fash = _fash_int_new();
if (fn->fash) _fash_int_add(fn->fash, gl, NULL, -1);
}
}
}
*fi_ret = NULL;
return 0;
}