diff options
Diffstat (limited to '')
-rw-r--r-- | minuitwrp/Android.mk | 12 | ||||
-rw-r--r-- | minuitwrp/graphics.c | 101 | ||||
-rw-r--r-- | minuitwrp/minui.h | 21 | ||||
-rw-r--r-- | minuitwrp/truetype.c | 731 |
4 files changed, 801 insertions, 64 deletions
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk index ba81f2723..bc4e054b3 100644 --- a/minuitwrp/Android.mk +++ b/minuitwrp/Android.mk @@ -107,8 +107,16 @@ ifneq ($(TW_WHITELIST_INPUT),) LOCAL_CFLAGS += -DWHITELIST_INPUT=$(TW_WHITELIST_INPUT) endif -LOCAL_SHARED_LIBRARIES += libz libc libcutils libjpeg -LOCAL_STATIC_LIBRARIES += libpng libpixelflinger_static +ifeq ($(TW_DISABLE_TTF), true) + LOCAL_CFLAGS += -DTW_DISABLE_TTF +else + LOCAL_SHARED_LIBRARIES += libft2 + LOCAL_C_INCLUDES += external/freetype/include + LOCAL_SRC_FILES += truetype.c +endif + +LOCAL_SHARED_LIBRARIES += libz libc libcutils libjpeg libpng +LOCAL_STATIC_LIBRARIES += libpixelflinger_static LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libminuitwrp diff --git a/minuitwrp/graphics.c b/minuitwrp/graphics.c index 79b1e9aa5..9926904ef 100644 --- a/minuitwrp/graphics.c +++ b/minuitwrp/graphics.c @@ -58,6 +58,7 @@ // #define PRINT_SCREENINFO 1 // Enables printing of screen info to log typedef struct { + int type; GGLSurface texture; unsigned offset[97]; unsigned cheight; @@ -392,6 +393,11 @@ int gr_measureEx(const char *s, void* font) if (!fnt) fnt = gr_font; +#ifndef TW_DISABLE_TTF + if(fnt->type == FONT_TYPE_TTF) + return gr_ttf_measureEx(s, font); +#endif + while ((off = *s++)) { off -= 32; @@ -410,6 +416,11 @@ int gr_maxExW(const char *s, void* font, int max_width) if (!fnt) fnt = gr_font; +#ifndef TW_DISABLE_TTF + if(fnt->type == FONT_TYPE_TTF) + return gr_ttf_maxExW(s, font, max_width); +#endif + while ((off = *s++)) { off -= 32; @@ -425,21 +436,6 @@ int gr_maxExW(const char *s, void* font, int max_width) return total; } -unsigned character_width(const char *s, void* pFont) -{ - GRFont *font = (GRFont*) pFont; - unsigned off; - - /* Handle default font */ - if (!font) font = gr_font; - - off = *s - 32; - if (off == 0) - return 0; - - return font->offset[off+1] - font->offset[off]; -} - int gr_textEx(int x, int y, const char *s, void* pFont) { GGLContext *gl = gr_context; @@ -450,6 +446,11 @@ int gr_textEx(int x, int y, const char *s, void* pFont) /* Handle default font */ if (!font) font = gr_font; +#ifndef TW_DISABLE_TTF + if(font->type == FONT_TYPE_TTF) + return gr_ttf_textExWH(gl, x, y, s, pFont, -1, -1); +#endif + gl->bindTexture(gl, &font->texture); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); @@ -480,6 +481,11 @@ int gr_textExW(int x, int y, const char *s, void* pFont, int max_width) /* Handle default font */ if (!font) font = gr_font; +#ifndef TW_DISABLE_TTF + if(font->type == FONT_TYPE_TTF) + return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, -1); +#endif + gl->bindTexture(gl, &font->texture); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); @@ -518,6 +524,11 @@ int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max /* Handle default font */ if (!font) font = gr_font; +#ifndef TW_DISABLE_TTF + if(font->type == FONT_TYPE_TTF) + return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, max_height); +#endif + gl->bindTexture(gl, &font->texture); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); @@ -549,34 +560,6 @@ int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max return x; } -int twgr_text(int x, int y, const char *s) -{ - GGLContext *gl = gr_context; - GRFont *font = gr_font; - unsigned off; - unsigned cwidth = 0; - - y -= font->ascent; - - gl->bindTexture(gl, &font->texture); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); - - while((off = *s++)) { - off -= 32; - if (off < 96) { - cwidth = font->offset[off+1] - font->offset[off]; - gl->texCoord2i(gl, (off * cwidth) - x, 0 - y); - gl->recti(gl, x, y, x + cwidth, y + font->cheight); - } - x += cwidth; - } - - return x; -} - void gr_fill(int x, int y, int w, int h) { GGLContext *gl = gr_context; @@ -682,33 +665,32 @@ void* gr_loadFont(const char* fontName) ftex->stride = width; ftex->data = (void*) bits; ftex->format = GGL_PIXEL_FORMAT_A_8; + font->type = FONT_TYPE_TWRP; font->cheight = height; font->ascent = height - 2; return (void*) font; } -int gr_getFontDetails(void* font, unsigned* cheight, unsigned* maxwidth) +void gr_freeFont(void *font) +{ + GRFont *f = font; + free(f->texture.data); + free(f); +} + +int gr_getMaxFontHeight(void *font) { GRFont *fnt = (GRFont*) font; if (!fnt) fnt = gr_font; if (!fnt) return -1; - if (cheight) *cheight = fnt->cheight; - if (maxwidth) - { - int pos; - *maxwidth = 0; - for (pos = 0; pos < 96; pos++) - { - unsigned int width = fnt->offset[pos+1] - fnt->offset[pos]; - if (width > *maxwidth) - { - *maxwidth = width; - } - } - } - return 0; +#ifndef TW_DISABLE_TTF + if(fnt->type == FONT_TYPE_TTF) + return gr_ttf_getMaxFontHeight(font); +#endif + + return fnt->cheight; } static void gr_init_font(void) @@ -746,6 +728,7 @@ static void gr_init_font(void) ftex->stride = width; ftex->data = (void*) bits; ftex->format = GGL_PIXEL_FORMAT_A_8; + gr_font->type = FONT_TYPE_TWRP; gr_font->cheight = height; gr_font->ascent = height - 2; return; diff --git a/minuitwrp/minui.h b/minuitwrp/minui.h index f04f518b5..cb9f8a385 100644 --- a/minuitwrp/minui.h +++ b/minuitwrp/minui.h @@ -20,6 +20,12 @@ typedef void* gr_surface; typedef unsigned short gr_pixel; +#define FONT_TYPE_TWRP 0 + +#ifndef TW_DISABLE_TTF +#define FONT_TYPE_TTF 1 +#endif + int gr_init(void); void gr_exit(void); @@ -35,16 +41,25 @@ void gr_fill(int x, int y, int w, int h); int gr_textEx(int x, int y, const char *s, void* font); int gr_textExW(int x, int y, const char *s, void* font, int max_width); int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max_height); -int twgr_text(int x, int y, const char *s); static inline int gr_text(int x, int y, const char *s) { return gr_textEx(x, y, s, NULL); } int gr_measureEx(const char *s, void* font); static inline int gr_measure(const char *s) { return gr_measureEx(s, NULL); } int gr_maxExW(const char *s, void* font, int max_width); -int gr_getFontDetails(void* font, unsigned* cheight, unsigned* maxwidth); -static inline void gr_font_size(int *x, int *y) { gr_getFontDetails(NULL, (unsigned*) y, (unsigned*) x); } +int gr_getMaxFontHeight(void *font); void* gr_loadFont(const char* fontName); +void gr_freeFont(void *font); + +#ifndef TW_DISABLE_TTF +void *gr_ttf_loadFont(const char *filename, int size, int dpi); +void gr_ttf_freeFont(void *font); +int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height); +int gr_ttf_measureEx(const char *s, void *font); +int gr_ttf_maxExW(const char *s, void *font, int max_width); +int gr_ttf_getMaxFontHeight(void *font); +void gr_ttf_dump_stats(void); +#endif void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy); unsigned int gr_get_width(gr_surface surface); diff --git a/minuitwrp/truetype.c b/minuitwrp/truetype.c new file mode 100644 index 000000000..8e0df42ea --- /dev/null +++ b/minuitwrp/truetype.c @@ -0,0 +1,731 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <stdio.h> + +#include "minui.h" + +#include <cutils/hashmap.h> +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include <pixelflinger/pixelflinger.h> +#include <pthread.h> + +#define STRING_CACHE_MAX_ENTRIES 400 +#define STRING_CACHE_TRUNCATE_ENTRIES 150 + +typedef struct +{ + int size; + int dpi; + char *path; +} TrueTypeFontKey; + +typedef struct +{ + int type; + int refcount; + int size; + int dpi; + int max_height; + int base; + FT_Face face; + Hashmap *glyph_cache; + Hashmap *string_cache; + struct StringCacheEntry *string_cache_head; + struct StringCacheEntry *string_cache_tail; + pthread_mutex_t mutex; + TrueTypeFontKey *key; +} TrueTypeFont; + +typedef struct +{ + FT_BBox bbox; + FT_BitmapGlyph glyph; +} TrueTypeCacheEntry; + +typedef struct +{ + char *text; + int max_width; +} StringCacheKey; + +struct StringCacheEntry +{ + GGLSurface surface; + int rendered_len; + StringCacheKey *key; + struct StringCacheEntry *prev; + struct StringCacheEntry *next; +}; + +typedef struct StringCacheEntry StringCacheEntry; + +typedef struct +{ + FT_Library ft_library; + Hashmap *fonts; + pthread_mutex_t mutex; +} FontData; + +static FontData font_data = { + .ft_library = NULL, + .fonts = NULL, + .mutex = PTHREAD_MUTEX_INITIALIZER, +}; + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +// 32bit FNV-1a hash algorithm +// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a +static const uint32_t FNV_prime = 16777619U; +static const uint32_t offset_basis = 2166136261U; + +static uint32_t fnv_hash(void *data, uint32_t len) +{ + uint8_t *d8 = data; + uint32_t *d32 = data; + uint32_t i, max; + uint32_t hash = offset_basis; + + max = len/4; + + // 32 bit data + for(i = 0; i < max; ++i) + { + hash ^= *d32++; + hash *= FNV_prime; + } + + // last bits + for(i *= 4; i < len; ++i) + { + hash ^= (uint32_t) d8[i]; + hash *= FNV_prime; + } + return hash; +} + +static inline uint32_t fnv_hash_add(uint32_t cur_hash, uint32_t word) +{ + cur_hash ^= word; + cur_hash *= FNV_prime; + return cur_hash; +} + +static bool gr_ttf_string_cache_equals(void *keyA, void *keyB) +{ + StringCacheKey *a = keyA; + StringCacheKey *b = keyB; + return a->max_width == b->max_width && strcmp(a->text, b->text) == 0; +} + +static int gr_ttf_string_cache_hash(void *key) +{ + StringCacheKey *k = key; + return fnv_hash(k->text, strlen(k->text)); +} + +static bool gr_ttf_font_cache_equals(void *keyA, void *keyB) +{ + TrueTypeFontKey *a = keyA; + TrueTypeFontKey *b = keyB; + return (a->size == b->size) && (a->dpi == b->dpi) && !strcmp(a->path, b->path); +} + +static int gr_ttf_font_cache_hash(void *key) +{ + TrueTypeFontKey *k = key; + + uint32_t hash = fnv_hash(k->path, strlen(k->path)); + hash = fnv_hash_add(hash, k->size); + hash = fnv_hash_add(hash, k->dpi); + return hash; +} + +void *gr_ttf_loadFont(const char *filename, int size, int dpi) +{ + int error; + TrueTypeFont *res = NULL; + + pthread_mutex_lock(&font_data.mutex); + + if(font_data.fonts) + { + TrueTypeFontKey k = { + .size = size, + .dpi = dpi, + .path = (char*)filename + }; + + res = hashmapGet(font_data.fonts, &k); + if(res) + { + ++res->refcount; + goto exit; + } + } + + if(!font_data.ft_library) + { + error = FT_Init_FreeType(&font_data.ft_library); + if(error) + { + fprintf(stderr, "Failed to init libfreetype! %d\n", error); + goto exit; + } + } + + FT_Face face; + error = FT_New_Face(font_data.ft_library, filename, 0, &face); + if(error) + { + fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error); + goto exit; + } + + error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi); + if(error) + { + fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error); + FT_Done_Face(face); + goto exit; + } + + res = malloc(sizeof(TrueTypeFont)); + memset(res, 0, sizeof(TrueTypeFont)); + res->type = FONT_TYPE_TTF; + res->size = size; + res->dpi = dpi; + res->face = face; + res->max_height = -1; + res->base = -1; + res->refcount = 1; + res->glyph_cache = hashmapCreate(32, hashmapIntHash, hashmapIntEquals); + res->string_cache = hashmapCreate(128, gr_ttf_string_cache_hash, gr_ttf_string_cache_equals); + pthread_mutex_init(&res->mutex, 0); + + if(!font_data.fonts) + font_data.fonts = hashmapCreate(4, gr_ttf_font_cache_hash, gr_ttf_font_cache_equals); + + TrueTypeFontKey *key = malloc(sizeof(TrueTypeFontKey)); + memset(key, 0, sizeof(TrueTypeFontKey)); + key->path = strdup(filename); + key->size = size; + key->dpi = dpi; + + res->key = key; + + hashmapPut(font_data.fonts, key, res); + +exit: + pthread_mutex_unlock(&font_data.mutex); + return res; +} + +static bool gr_ttf_freeFontCache(void *key, void *value, void *context) +{ + TrueTypeCacheEntry *e = value; + FT_Done_Glyph((FT_Glyph)e->glyph); + free(e); + free(key); + return true; +} + +static bool gr_ttf_freeStringCache(void *key, void *value, void *context) +{ + StringCacheKey *k = key; + free(k->text); + free(k); + + StringCacheEntry *e = value; + free(e->surface.data); + free(e); + return true; +} + +void gr_ttf_freeFont(void *font) +{ + pthread_mutex_lock(&font_data.mutex); + + TrueTypeFont *d = font; + + if(--d->refcount == 0) + { + hashmapRemove(font_data.fonts, d->key); + + if(hashmapSize(font_data.fonts) == 0) + { + hashmapFree(font_data.fonts); + font_data.fonts = NULL; + } + + free(d->key->path); + free(d->key); + + FT_Done_Face(d->face); + hashmapForEach(d->string_cache, gr_ttf_freeStringCache, NULL); + hashmapFree(d->string_cache); + hashmapForEach(d->glyph_cache, gr_ttf_freeFontCache, NULL); + hashmapFree(d->glyph_cache); + pthread_mutex_destroy(&d->mutex); + free(d); + } + + pthread_mutex_unlock(&font_data.mutex); +} + +static TrueTypeCacheEntry *gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index) +{ + return hashmapGet(font->glyph_cache, &char_index); +} + +static TrueTypeCacheEntry *gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index) +{ + TrueTypeCacheEntry *res = hashmapGet(font->glyph_cache, &char_index); + if(!res) + { + int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER); + if(error) + { + fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error); + return NULL; + } + + FT_BitmapGlyph glyph; + error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph); + if(error) + { + fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error); + return NULL; + } + + res = malloc(sizeof(TrueTypeCacheEntry)); + memset(res, 0, sizeof(TrueTypeCacheEntry)); + res->glyph = glyph; + FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox); + + int *key = malloc(sizeof(int)); + *key = char_index; + + hashmapPut(font->glyph_cache, key, res); + } + + return res; +} + +static int gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base) +{ + int y; + uint8_t *src_itr = glyph->bitmap.buffer; + uint8_t *dest_itr = dest->data; + + if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) + { + fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode); + return -1; + } + + dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left); + + for(y = 0; y < glyph->bitmap.rows; ++y) + { + memcpy(dest_itr, src_itr, glyph->bitmap.width); + src_itr += glyph->bitmap.pitch; + dest_itr += dest->stride; + } + return 0; +} + +static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width) +{ + TrueTypeFont *f = font; + TrueTypeCacheEntry *ent; + int max_len = 0, total_w = 0; + char c; + int i, x, diff, char_idx, prev_idx = 0; + int height, base; + FT_Vector delta; + uint8_t *data = NULL; + const char *text_itr = text; + + while((c = *text_itr++)) + { + char_idx = FT_Get_Char_Index(f->face, c); + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(ent) + { + diff = ent->glyph->root.advance.x >> 16; + + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + diff += delta.x >> 6; + } + + if(max_width != -1 && total_w + diff > max_width) + break; + + total_w += diff; + } + prev_idx = char_idx; + ++max_len; + } + + if(font->max_height == -1) + gr_ttf_getMaxFontHeight(font); + + if(font->max_height == -1) + return -1; + + height = font->max_height; + + data = malloc(total_w*height); + memset(data, 0, total_w*height); + x = 0; + prev_idx = 0; + + surface->version = sizeof(*surface); + surface->width = total_w; + surface->height = height; + surface->stride = total_w; + surface->data = (void*)data; + surface->format = GGL_PIXEL_FORMAT_A_8; + + for(i = 0; i < max_len; ++i) + { + char_idx = FT_Get_Char_Index(f->face, text[i]); + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + x += delta.x >> 6; + } + + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(ent) + { + gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base); + x += ent->glyph->root.advance.x >> 16; + } + + prev_idx = char_idx; + } + + return max_len; +} + +static StringCacheEntry *gr_ttf_string_cache_peek(TrueTypeFont *font, const char *text, int max_width) +{ + StringCacheEntry *res; + StringCacheKey k = { + .text = (char*)text, + .max_width = max_width + }; + + return hashmapGet(font->string_cache, &k); +} + +static StringCacheEntry *gr_ttf_string_cache_get(TrueTypeFont *font, const char *text, int max_width) +{ + StringCacheEntry *res; + StringCacheKey k = { + .text = (char*)text, + .max_width = max_width + }; + + res = hashmapGet(font->string_cache, &k); + if(!res) + { + res = malloc(sizeof(StringCacheEntry)); + memset(res, 0, sizeof(StringCacheEntry)); + res->rendered_len = gr_ttf_render_text(font, &res->surface, text, max_width); + if(res->rendered_len < 0) + { + free(res); + return NULL; + } + + StringCacheKey *new_key = malloc(sizeof(StringCacheKey)); + memset(new_key, 0, sizeof(StringCacheKey)); + new_key->max_width = max_width; + new_key->text = strdup(text); + + res->key = new_key; + + if(font->string_cache_tail) + { + res->prev = font->string_cache_tail; + res->prev->next = res; + } + else + font->string_cache_head = res; + font->string_cache_tail = res; + + hashmapPut(font->string_cache, new_key, res); + } + else if(res->next) + { + // move this entry to the tail of the linked list + // if it isn't already there + if(res->prev) + res->prev->next = res->next; + + res->next->prev = res->prev; + + if(!res->prev) + font->string_cache_head = res->next; + + res->next = NULL; + res->prev = font->string_cache_tail; + res->prev->next = res; + font->string_cache_tail = res; + + // truncate old entries + if(hashmapSize(font->string_cache) >= STRING_CACHE_MAX_ENTRIES) + { + printf("Truncating string cache entries.\n"); + int i; + StringCacheEntry *ent; + for(i = 0; i < STRING_CACHE_TRUNCATE_ENTRIES; ++i) + { + ent = font->string_cache_head; + font->string_cache_head = ent->next; + font->string_cache_head->prev = NULL; + + hashmapRemove(font->string_cache, ent->key); + + gr_ttf_freeStringCache(ent->key, ent, NULL); + } + } + } + return res; +} + +int gr_ttf_measureEx(const char *s, void *font) +{ + TrueTypeFont *f = font; + int res = -1; + + pthread_mutex_lock(&f->mutex); + StringCacheEntry *e = gr_ttf_string_cache_get(font, s, -1); + if(e) + res = e->surface.width; + pthread_mutex_unlock(&f->mutex); + + return res; +} + +int gr_ttf_maxExW(const char *s, void *font, int max_width) +{ + TrueTypeFont *f = font; + TrueTypeCacheEntry *ent; + int max_len = 0, total_w = 0; + char c; + int char_idx, prev_idx = 0; + FT_Vector delta; + StringCacheEntry *e; + + pthread_mutex_lock(&f->mutex); + + e = gr_ttf_string_cache_peek(font, s, max_width); + if(e) + { + max_len = e->rendered_len; + pthread_mutex_unlock(&f->mutex); + return max_len; + } + + for(; (c = *s++); ++max_len) + { + char_idx = FT_Get_Char_Index(f->face, c); + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + total_w += delta.x >> 6; + } + prev_idx = char_idx; + + if(total_w > max_width) + break; + + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(!ent) + continue; + + total_w += ent->glyph->root.advance.x >> 16; + } + pthread_mutex_unlock(&f->mutex); + return max_len > 0 ? max_len - 1 : 0; +} + +int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height) +{ + GGLContext *gl = context; + TrueTypeFont *font = pFont; + + // not actualy max width, but max_width + x + if(max_width != -1) + { + max_width -= x; + if(max_width <= 0) + return 0; + } + + pthread_mutex_lock(&font->mutex); + + StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width); + if(!e) + { + pthread_mutex_unlock(&font->mutex); + return -1; + } + + int y_bottom = y + e->surface.height; + int res = e->rendered_len; + + if(max_height != -1 && max_height < y_bottom) + { + y_bottom = max_height; + if(y_bottom <= y) + { + pthread_mutex_unlock(&font->mutex); + return 0; + } + } + + gl->bindTexture(gl, &e->surface); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + + gl->texCoord2i(gl, -x, -y); + gl->recti(gl, x, y, x + e->surface.width, y_bottom); + + pthread_mutex_unlock(&font->mutex); + return res; +} + +int gr_ttf_getMaxFontHeight(void *font) +{ + int res; + TrueTypeFont *f = font; + + pthread_mutex_lock(&f->mutex); + + if(f->max_height == -1) + { + char c; + int char_idx; + int error; + FT_Glyph glyph; + FT_BBox bbox; + FT_BBox bbox_glyph; + TrueTypeCacheEntry *ent; + + bbox.yMin = bbox_glyph.yMin = LONG_MAX; + bbox.yMax = bbox_glyph.yMax = LONG_MIN; + + for(c = '!'; c <= '~'; ++c) + { + char_idx = FT_Get_Char_Index(f->face, c); + ent = gr_ttf_glyph_cache_peek(f, char_idx); + if(ent) + { + bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin); + bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax); + } + else + { + error = FT_Load_Glyph(f->face, char_idx, 0); + if(error) + continue; + + error = FT_Get_Glyph(f->face->glyph, &glyph); + if(error) + continue; + + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph); + bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin); + bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax); + + FT_Done_Glyph(glyph); + } + } + + if(bbox.yMin > bbox.yMax) + bbox.yMin = bbox.yMax = 0; + + f->max_height = bbox.yMax - bbox.yMin; + f->base = bbox.yMax; + + // FIXME: twrp fonts have some padding on top, I'll add it here + // Should be fixed in the themes + f->max_height += f->size / 4; + f->base += f->size / 4; + } + + res = f->max_height; + + pthread_mutex_unlock(&f->mutex); + return res; +} + +static bool gr_ttf_dump_stats_count_string_cache(void *key, void *value, void *context) +{ + int *string_cache_size = context; + StringCacheEntry *e = value; + *string_cache_size += e->surface.height*e->surface.width + sizeof(StringCacheEntry); + return true; +} + +static bool gr_ttf_dump_stats_font(void *key, void *value, void *context) +{ + TrueTypeFontKey *k = key; + TrueTypeFont *f = value; + int *total_string_cache_size = context; + int string_cache_size = 0; + + pthread_mutex_lock(&f->mutex); + + hashmapForEach(f->string_cache, gr_ttf_dump_stats_count_string_cache, &string_cache_size); + + printf(" Font %s (size %d, dpi %d):\n" + " refcount: %d\n" + " max_height: %d\n" + " base: %d\n" + " glyph_cache: %d entries\n" + " string_cache: %d entries (%.2f kB)\n", + k->path, k->size, k->dpi, + f->refcount, f->max_height, f->base, + hashmapSize(f->glyph_cache), + hashmapSize(f->string_cache), ((double)string_cache_size)/1024); + + pthread_mutex_unlock(&f->mutex); + + *total_string_cache_size += string_cache_size; + return true; +} + +void gr_ttf_dump_stats(void) +{ + pthread_mutex_lock(&font_data.mutex); + + printf("TrueType fonts system stats: "); + if(!font_data.fonts) + printf("no truetype fonts loaded.\n"); + else + { + int total_string_cache_size = 0; + printf("%d fonts loaded.\n", hashmapSize(font_data.fonts)); + hashmapForEach(font_data.fonts, gr_ttf_dump_stats_font, &total_string_cache_size); + printf(" Total string cache size: %.2f kB\n", ((double)total_string_cache_size)/1024); + } + + pthread_mutex_unlock(&font_data.mutex); +} |