/* * fbfrender - FBF font cache and renderer * * Copyright (c) 2001, 2002 Fabrice Bellard. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qe.h" #include "fbfrender.h" #include "libfbf.h" //#define CONFIG_FILE_FONTS static UniFontData *first_font; static UniFontData *fallback_font; /**********************************************/ /* glyph cache handling */ #define HASH_SIZE 263 #define MAX_CACHE_SIZE (256 * 1024) static GlyphCache *hash_table[HASH_SIZE]; static int cache_size = 0; static GlyphCache first_cache_entry; static void glyph_cache_init(void) { first_cache_entry.next = &first_cache_entry; first_cache_entry.prev = &first_cache_entry; } static inline int glyph_hash(int index, int size, int style) { unsigned int h; h = index + (style << 24) + (size << 16); return h % HASH_SIZE; } static GlyphCache *get_cached_glyph(QEFont *font, int index) { GlyphCache *p; int h; h = glyph_hash(index, font->size, font->style); p = hash_table[h]; while (p != NULL) { if (p->index == index && p->size == font->size && p->style == font->style) goto found; p = p->hash_next; } return NULL; found: /* suppress in linked list */ p->next->prev = p->prev; p->prev->next = p->next; /* move to front */ first_cache_entry.next->prev = p; p->next = first_cache_entry.next; first_cache_entry.next = p; p->prev = &first_cache_entry; return p; } static GlyphCache *add_cached_glyph(QEFont *font, int index, int data_size) { GlyphCache **pp, *p; int h; cache_size += sizeof(GlyphCache) + data_size + 4; while (cache_size > MAX_CACHE_SIZE) { /* suppress oldest entry */ p = first_cache_entry.prev; cache_size -= sizeof(GlyphCache) + p->data_size + 4; p->next->prev = p->prev; p->prev->next = p->next; /* suppress from hash table (should use double linked list ?) */ h = glyph_hash(p->index, p->size, p->style); pp = &hash_table[h]; while (*pp != p) pp = &(*pp)->hash_next; *pp = p->hash_next; qe_free(&p); } p = qe_malloc_hack(GlyphCache, data_size); if (!p) return NULL; h = glyph_hash(index, font->size, font->style); pp = &hash_table[h]; p->hash_next = *pp; *pp = p; p->index = index; p->size = font->size; p->style = font->style; p->data_size = data_size; p->private = NULL; first_cache_entry.next->prev = p; p->next = first_cache_entry.next; first_cache_entry.next = p; p->prev = &first_cache_entry; return p; } static GlyphCache *fbf_decode_glyph1(QEFont *font, int code) { UniFontData *uf = font->priv_data; int glyph_index, size, src_width, src_height; GlyphCache *glyph_cache; GlyphEntry *fbf_glyph_entry; glyph_index = fbf_unicode_to_glyph(uf, code); if (glyph_index < 0) return NULL; /* decompress a glyph if needed */ if (fbf_decode_glyph(uf, &fbf_glyph_entry, glyph_index) < 0) return NULL; src_width = fbf_glyph_entry->w; src_height = fbf_glyph_entry->h; size = src_width * src_height; glyph_cache = add_cached_glyph(font, code, size); if (!glyph_cache) return NULL; /* convert to bitmap */ { int x, y, bit, pitch; unsigned char *bitmap; bitmap = fbf_glyph_entry->bitmap; pitch = (src_width + 7) >> 3; for (y = 0; y < src_height; y++) { for (x = 0; x < src_width; x++) { bit = (bitmap[pitch * y + (x >> 3)] >> (7 - (x & 7))) & 1; glyph_cache->data[src_width * y + x] = -bit; } } } glyph_cache->w = src_width; glyph_cache->h = src_height; glyph_cache->x = fbf_glyph_entry->x; glyph_cache->y = fbf_glyph_entry->y; glyph_cache->xincr = fbf_glyph_entry->xincr; return glyph_cache; } /* * main function : get one glyph. Return -1 if no glyph found, 0 if OK. */ GlyphCache *decode_cached_glyph(QEditScreen *s, QEFont *font, int code) { GlyphCache *g; QEFont *font1; g = get_cached_glyph(font, code); if (!g) { g = fbf_decode_glyph1(font, code); if (!g) { /* try with fallback font */ font1 = select_font(s, font->style | (1 << QE_FONT_FAMILY_FALLBACK_SHIFT), font->size); g = fbf_decode_glyph1(font1, code); release_font(s, font1); if (!g) return NULL; /* indicates that it is a fallback glyph so that the correct font height can be computed */ g->is_fallback = 1; } else { g->is_fallback = 0; } } return g; } void fbf_text_metrics(QEditScreen *s, QEFont *font, QECharMetrics *metrics, const char32_t *str, int len) { GlyphCache *g; int i, x; char32_t cc; metrics->font_ascent = font->ascent; metrics->font_descent = font->descent; x = 0; for (i = 0;i < len; i++) { cc = str[i]; g = decode_cached_glyph(s, font, cc); if (g) { x += g->xincr; if (g->is_fallback) { /* if alternate font used, modify metrics */ metrics->font_ascent = max_int(metrics->font_ascent, fallback_font->ascent); metrics->font_descent = max_int(metrics->font_descent, fallback_font->descent); } } } metrics->width = x; } #define MAX_MATCHES 32 QEFont *fbf_open_font(qe__unused__ QEditScreen *s, int style, int size) { QEFont *font; UniFontData *uf, *uf_found; int d, dmin; UniFontData *fonts[MAX_MATCHES]; int nb_fonts, i; font = qe_mallocz(QEFont); if (!font) return NULL; if ((style & QE_FONT_FAMILY_FALLBACK_MASK) != 0) { uf_found = fallback_font; } else { /* convert to unifont family types */ style = ((style & QE_FONT_FAMILY_MASK) >> QE_FONT_FAMILY_SHIFT) - 1; /* first match style */ nb_fonts = 0; for (uf = first_font; uf != NULL; uf = uf->next_font) { if (uf->family_type == style) { fonts[nb_fonts++] = uf; } } if (!nb_fonts) { uf_found = first_font; } else { /* select closest size */ uf_found = NULL; dmin = INT_MAX; for (i = 0;i < nb_fonts;i++) { uf = fonts[i]; d = abs(uf->pt_size - size); if (d < dmin) { uf_found = uf; dmin = d; } } } } font->priv_data = uf_found; font->ascent = uf_found->ascent; font->descent = uf_found->descent; return font; } void fbf_close_font(qe__unused__ QEditScreen *s, QEFont **fontp) { qe_free(fontp); } static void *my_malloc(qe__unused__ void *opaque, int size) { return qe_malloc_bytes(size); } static void my_free(qe__unused__ void *opaque, void *ptr) { qe_free(&ptr); } #ifdef CONFIG_FILE_FONTS /* disk font opening */ int my_fbf_seek(void *infile, long pos) { return fseek((FILE *)infile, pos, SEEK_SET); } int my_fbf_read(void *infile, unsigned char *buf, int len) { return fread(buf, 1, len, (FILE *)infile); } int my_fbf_getc(void *infile) { return fgetc((FILE *)infile); } static int fbf_load_font_file(const char *filename) { UniFontData *uf; FILE *f; printf("loading %s\n", filename); f = fopen(filename, "r"); if (!f) return -1; uf = qe_mallocz(UniFontData); if (!uf) { fclose(f); return -1; } /* init memory */ uf->mem_opaque = NULL; uf->fbf_malloc = my_malloc; uf->fbf_free = my_free; /* init file I/O */ uf->infile = f; uf->fbf_seek = my_fbf_seek; uf->fbf_read = my_fbf_read; uf->fbf_getc = my_fbf_getc; if (fbf_load_font(uf) < 0) { qe_free(&uf); fclose(f); return -1; } /* add font */ uf->next_font = first_font; first_font = uf; return 0; } int fbf_render_init(const char *font_path) { FindFileState *ffs; char filename[MAX_FILENAME_SIZE]; glyph_cache_init(); first_font = NULL; ffs = find_file_open(font_path, "*.fbf", FF_PATH | FF_NODIR); if (!ffs) return -1; for (;;) { if (find_file_next(ffs, filename, sizeof(filename))) break; if (fbf_load_font_file(filename) < 0) { fprintf(stderr, "Could not load font '%s'\n", filename); } } return 0; } void fbf_render_cleanup(void) { UniFontData *uf, *uf1; for (uf = first_font; uf != NULL; uf = uf1) { uf1 = uf->next_font; /* close font data structures */ fbf_free_font(uf); /* close font file */ fclose(uf->infile); } first_font = NULL; } #else typedef struct MemoryFile { const unsigned char *base; int offset; int size; } MemoryFile; static int my_fbf_seek(void *infile, long pos) { MemoryFile *f = infile; f->offset = pos; return pos; } static int my_fbf_read(void *infile, unsigned char *buf, int len) { MemoryFile *f = infile; int len1; len1 = f->size - f->offset; if (len > len1) len = len1; memcpy(buf, f->base + f->offset, len); f->offset += len; return len; } static int my_fbf_getc(void *infile) { MemoryFile *f = infile; if (f->offset < f->size) return f->base[f->offset++]; else return EOF; } static int fbf_load_font_memory(const unsigned char *data, int data_size) { UniFontData *uf; MemoryFile *f; f = qe_mallocz(MemoryFile); if (!f) return -1; f->base = data; f->size = data_size; uf = qe_mallocz(UniFontData); if (!uf) { qe_free(&f); return -1; } /* init memory */ uf->mem_opaque = NULL; uf->fbf_malloc = my_malloc; uf->fbf_free = my_free; /* init file I/O */ uf->infile = f; uf->fbf_seek = my_fbf_seek; uf->fbf_read = my_fbf_read; uf->fbf_getc = my_fbf_getc; if (fbf_load_font(uf) < 0) { qe_free(&uf); qe_free(&f); return -1; } /* add font */ uf->next_font = first_font; first_font = uf; /* we consider that unifont is the fallback font */ /* XXX: use generic system as in x11.c */ if (!strcasecmp(uf->family_name, "unifont")) fallback_font = uf; return 0; } int fbf_render_init(qe__unused__ const char *font_path) { const struct fbf_font *pp; glyph_cache_init(); first_font = NULL; for (pp = fbf_fonts; pp->data != NULL; pp++) { fbf_load_font_memory(pp->data, pp->size); } if (!fallback_font) fallback_font = first_font; return 0; } void fbf_render_cleanup(void) { while (first_font) { UniFontData *uf = first_font; first_font = uf->next_font; /* close font data structures */ fbf_free_font(uf); /* close font file */ qe_free(&uf->infile); qe_free(&uf); } } #endif