diff options
Diffstat (limited to 'source/fitz/draw-glyph.c')
-rw-r--r-- | source/fitz/draw-glyph.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/source/fitz/draw-glyph.c b/source/fitz/draw-glyph.c new file mode 100644 index 00000000..b0a52949 --- /dev/null +++ b/source/fitz/draw-glyph.c @@ -0,0 +1,241 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#define MAX_GLYPH_SIZE 256 +#define MAX_CACHE_SIZE (1024*1024) + +typedef struct fz_glyph_key_s fz_glyph_key; + +struct fz_glyph_cache_s +{ + int refs; + fz_hash_table *hash; + int total; +}; + +struct fz_glyph_key_s +{ + fz_font *font; + int a, b; + int c, d; + unsigned short gid; + unsigned char e, f; + int aa; +}; + +void +fz_new_glyph_cache_context(fz_context *ctx) +{ + fz_glyph_cache *cache; + + cache = fz_malloc_struct(ctx, fz_glyph_cache); + fz_try(ctx) + { + cache->hash = fz_new_hash_table(ctx, 509, sizeof(fz_glyph_key), FZ_LOCK_GLYPHCACHE); + } + fz_catch(ctx) + { + fz_free(ctx, cache); + fz_rethrow(ctx); + } + cache->total = 0; + cache->refs = 1; + + ctx->glyph_cache = cache; +} + +/* The glyph cache lock is always held when this function is called. */ +static void +fz_evict_glyph_cache(fz_context *ctx) +{ + fz_glyph_cache *cache = ctx->glyph_cache; + fz_glyph_key *key; + fz_pixmap *pixmap; + int i; + + for (i = 0; i < fz_hash_len(ctx, cache->hash); i++) + { + key = fz_hash_get_key(ctx, cache->hash, i); + if (key->font) + fz_drop_font(ctx, key->font); + pixmap = fz_hash_get_val(ctx, cache->hash, i); + if (pixmap) + fz_drop_pixmap(ctx, pixmap); + } + + cache->total = 0; + + fz_empty_hash(ctx, cache->hash); +} + +void +fz_drop_glyph_cache_context(fz_context *ctx) +{ + if (!ctx->glyph_cache) + return; + + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + ctx->glyph_cache->refs--; + if (ctx->glyph_cache->refs == 0) + { + fz_evict_glyph_cache(ctx); + fz_free_hash(ctx, ctx->glyph_cache->hash); + fz_free(ctx, ctx->glyph_cache); + ctx->glyph_cache = NULL; + } + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); +} + +fz_glyph_cache * +fz_keep_glyph_cache(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + ctx->glyph_cache->refs++; + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + return ctx->glyph_cache; +} + +fz_pixmap * +fz_render_stroked_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *stroke, fz_irect scissor) +{ + if (font->ft_face) + { + if (stroke->dash_len > 0) + return NULL; + return fz_render_ft_stroked_glyph(ctx, font, gid, trm, ctm, stroke); + } + return fz_render_glyph(ctx, font, gid, trm, NULL, scissor); +} + +/* + Render a glyph and return a bitmap. + If the glyph is too large to fit the cache we have two choices: + 1) Return NULL so the caller can draw the glyph using an outline. + Only supported for freetype fonts. + 2) Render a clipped glyph by using the scissor rectangle. + Only supported for type 3 fonts. + This must not be inserted into the cache. + */ +fz_pixmap * +fz_render_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm, fz_colorspace *model, fz_irect scissor) +{ + fz_glyph_cache *cache; + fz_glyph_key key; + fz_pixmap *val; + float size = fz_matrix_expansion(ctm); + int do_cache, locked, caching; + fz_matrix local_ctm = *ctm; + + fz_var(locked); + fz_var(caching); + + if (size <= MAX_GLYPH_SIZE) + { + scissor = fz_infinite_irect; + do_cache = 1; + } + else + { + if (font->ft_face) + return NULL; + do_cache = 0; + } + + cache = ctx->glyph_cache; + + memset(&key, 0, sizeof key); + key.font = font; + key.gid = gid; + key.a = local_ctm.a * 65536; + key.b = local_ctm.b * 65536; + key.c = local_ctm.c * 65536; + key.d = local_ctm.d * 65536; + key.e = (local_ctm.e - floorf(local_ctm.e)) * 256; + key.f = (local_ctm.f - floorf(local_ctm.f)) * 256; + key.aa = fz_aa_level(ctx); + + local_ctm.e = floorf(local_ctm.e) + key.e / 256.0f; + local_ctm.f = floorf(local_ctm.f) + key.f / 256.0f; + + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + val = fz_hash_find(ctx, cache->hash, &key); + if (val) + { + fz_keep_pixmap(ctx, val); + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + return val; + } + + locked = 1; + caching = 0; + + fz_try(ctx) + { + if (font->ft_face) + { + val = fz_render_ft_glyph(ctx, font, gid, &local_ctm, key.aa); + } + else if (font->t3procs) + { + /* We drop the glyphcache here, and execute the t3 + * glyph code. The danger here is that some other + * thread will come along, and want the same glyph + * too. If it does, we may both end up rendering + * pixmaps. We cope with this later on, by ensuring + * that only one gets inserted into the cache. If + * we insert ours to find one already there, we + * abandon ours, and use the one there already. + */ + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + locked = 0; + val = fz_render_t3_glyph(ctx, font, gid, &local_ctm, model, scissor); + fz_lock(ctx, FZ_LOCK_GLYPHCACHE); + locked = 1; + } + else + { + fz_warn(ctx, "assert: uninitialized font structure"); + val = NULL; + } + if (val && do_cache) + { + if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) + { + fz_pixmap *pix; + + /* If we throw an exception whilst caching, + * just ignore the exception and carry on. */ + caching = 1; + if (cache->total + val->w * val->h > MAX_CACHE_SIZE) + fz_evict_glyph_cache(ctx); + + pix = fz_hash_insert(ctx, cache->hash, &key, val); + if (pix) + { + fz_drop_pixmap(ctx, val); + val = pix; + } + else + { + fz_keep_font(ctx, key.font); + cache->total += val->w * val->h; + } + val = fz_keep_pixmap(ctx, val); + } + } + } + fz_always(ctx) + { + if (locked) + fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); + } + fz_catch(ctx) + { + if (caching) + fz_warn(ctx, "cannot encache glyph; continuing"); + else + fz_rethrow(ctx); + } + + return val; +} |