diff options
author | Ethan Yonker <dees_troy@teamw.in> | 2015-12-28 21:54:50 +0100 |
---|---|---|
committer | Ethan Yonker <dees_troy@teamw.in> | 2016-01-27 17:53:13 +0100 |
commit | fbb4353a247157d32208f8f133cd1ee42f4fbc49 (patch) | |
tree | 6f819fbdd21f2adef4f7ba4a5b4d02054cc02f29 /minuitwrp/truetype.c | |
parent | Adopted Storage support (diff) | |
download | android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar.gz android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar.bz2 android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar.lz android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar.xz android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.tar.zst android_bootable_recovery-fbb4353a247157d32208f8f133cd1ee42f4fbc49.zip |
Diffstat (limited to 'minuitwrp/truetype.c')
-rw-r--r-- | minuitwrp/truetype.c | 816 |
1 files changed, 0 insertions, 816 deletions
diff --git a/minuitwrp/truetype.c b/minuitwrp/truetype.c deleted file mode 100644 index d9ed0198b..000000000 --- a/minuitwrp/truetype.c +++ /dev/null @@ -1,816 +0,0 @@ -#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_bytes; // number of bytes from C string rendered, not number of UTF8 characters! - 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; -} - -int utf8_to_unicode(unsigned char* pIn, unsigned int *pOut) -{ - int utf_bytes = 1; - unsigned int unicode = 0; - unsigned char tmp; - tmp = *pIn++; - if (tmp < 0x80) - { - *pOut = tmp; - } - else - { - unsigned int high_bit_mask = 0x3F; - unsigned int high_bit_shift = 0; - int total_bits = 0; - while((tmp & 0xC0) == 0xC0) - { - utf_bytes ++; - if(utf_bytes > 6) - { - *pOut = tmp; - return 1; - } - tmp = 0xFF & (tmp << 1); - total_bits += 6; - high_bit_mask >>= 1; - high_bit_shift++; - unicode <<= 6; - unicode |= (*pIn++) & 0x3F; - } - unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits; - *pOut = unicode; - } - - return utf_bytes; -} - -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; -} - -void *gr_ttf_scaleFont(void *font, int max_width, int measured_width) -{ - if (!font) - return NULL; - - TrueTypeFont *f = font; - float scale_value = (float)(max_width) / (float)(measured_width); - int new_size = ((int)((float)f->size * scale_value)) - 1; - if (new_size < 1) - new_size = 1; - const char* file = f->key->path; - int dpi = f->dpi; - return gr_ttf_loadFont(file, new_size, dpi); -} - -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); - - // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular), - // the result might end up being before the buffer - I'm not sure how to properly handle this. - if(dest_itr < dest->data) - dest_itr = dest->data; - - 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 void gr_ttf_calcMaxFontHeight(TrueTypeFont *f) -{ - 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; -} - -// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters! -static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width) -{ - TrueTypeFont *f = font; - TrueTypeCacheEntry *ent; - int bytes_rendered = 0, total_w = 0; - int utf_bytes = 0; - unsigned int unicode = 0; - int i, x, diff, char_idx, prev_idx = 0; - int height, base; - FT_Vector delta; - uint8_t *data = NULL; - const char *text_itr = text; - int *char_idxs; - int char_idxs_len = 0; - - char_idxs = malloc(strlen(text) * sizeof(int)); - - while(*text_itr) - { - utf_bytes = utf8_to_unicode(text_itr, &unicode); - text_itr += utf_bytes; - bytes_rendered += utf_bytes; - - char_idx = FT_Get_Char_Index(f->face, unicode); - char_idxs[char_idxs_len] = char_idx; - - 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; - ++char_idxs_len; - } - - if(font->max_height == -1) - gr_ttf_calcMaxFontHeight(font); - - if(font->max_height == -1) - { - free(char_idxs); - 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 < char_idxs_len; ++i) - { - char_idx = char_idxs[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; - } - - free(char_idxs); - return bytes_rendered; -} - -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_bytes = gr_ttf_render_text(font, &res->surface, text, max_width); - if(res->rendered_bytes < 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_bytes = 0, total_w = 0; - int utf_bytes, prev_utf_bytes = 0; - unsigned int unicode = 0; - 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_bytes = e->rendered_bytes; - pthread_mutex_unlock(&f->mutex); - return max_bytes; - } - - while(*s) - { - utf_bytes = utf8_to_unicode(s, &unicode); - s += utf_bytes; - - char_idx = FT_Get_Char_Index(f->face, unicode); - 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) - { - max_bytes -= prev_utf_bytes; - break; - } - prev_utf_bytes = utf_bytes; - - ent = gr_ttf_glyph_cache_get(f, char_idx); - if(!ent) - continue; - - total_w += ent->glyph->root.advance.x >> 16; - max_bytes += utf_bytes; - } - pthread_mutex_unlock(&f->mutex); - return max_bytes; -} - -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_bytes; - - 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); - gl->disable(gl, GGL_TEXTURE_2D); - - 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) - gr_ttf_calcMaxFontHeight(f); - 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); -} |