| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- // Copyright (C) 2004-2021 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "draw-imp.h"
- #include "glyph-imp.h"
- #include "pixmap-imp.h"
- #include <string.h>
- #include <math.h>
- #define MAX_GLYPH_SIZE 256
- #define MAX_CACHE_SIZE (1024*1024)
- #define GLYPH_HASH_LEN 509
- typedef struct
- {
- fz_font *font;
- int a, b;
- int c, d;
- unsigned short gid;
- unsigned char e, f;
- int aa;
- } fz_glyph_key;
- typedef struct fz_glyph_cache_entry
- {
- fz_glyph_key key;
- unsigned hash;
- struct fz_glyph_cache_entry *lru_prev;
- struct fz_glyph_cache_entry *lru_next;
- struct fz_glyph_cache_entry *bucket_next;
- struct fz_glyph_cache_entry *bucket_prev;
- fz_glyph *val;
- } fz_glyph_cache_entry;
- struct fz_glyph_cache
- {
- int refs;
- size_t total;
- #ifndef NDEBUG
- int num_evictions;
- ptrdiff_t evicted;
- #endif
- fz_glyph_cache_entry *entry[GLYPH_HASH_LEN];
- fz_glyph_cache_entry *lru_head;
- fz_glyph_cache_entry *lru_tail;
- };
- static size_t
- fz_glyph_size(fz_context *ctx, fz_glyph *glyph)
- {
- if (glyph == NULL)
- return 0;
- return sizeof(fz_glyph) + glyph->size + fz_pixmap_size(ctx, glyph->pixmap);
- }
- void
- fz_new_glyph_cache_context(fz_context *ctx)
- {
- fz_glyph_cache *cache;
- cache = fz_malloc_struct(ctx, fz_glyph_cache);
- cache->total = 0;
- cache->refs = 1;
- ctx->glyph_cache = cache;
- }
- static void
- drop_glyph_cache_entry(fz_context *ctx, fz_glyph_cache_entry *entry)
- {
- fz_glyph_cache *cache = ctx->glyph_cache;
- if (entry->lru_next)
- entry->lru_next->lru_prev = entry->lru_prev;
- else
- cache->lru_tail = entry->lru_prev;
- if (entry->lru_prev)
- entry->lru_prev->lru_next = entry->lru_next;
- else
- cache->lru_head = entry->lru_next;
- cache->total -= fz_glyph_size(ctx, entry->val);
- if (entry->bucket_next)
- entry->bucket_next->bucket_prev = entry->bucket_prev;
- if (entry->bucket_prev)
- entry->bucket_prev->bucket_next = entry->bucket_next;
- else
- cache->entry[entry->hash] = entry->bucket_next;
- fz_drop_font(ctx, entry->key.font);
- fz_drop_glyph(ctx, entry->val);
- fz_free(ctx, entry);
- }
- /* The glyph cache lock is always held when this function is called. */
- static void
- do_purge(fz_context *ctx)
- {
- fz_glyph_cache *cache = ctx->glyph_cache;
- int i;
- for (i = 0; i < GLYPH_HASH_LEN; i++)
- {
- while (cache->entry[i])
- drop_glyph_cache_entry(ctx, cache->entry[i]);
- }
- cache->total = 0;
- }
- void
- fz_purge_glyph_cache(fz_context *ctx)
- {
- fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
- do_purge(ctx);
- fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
- }
- void
- fz_drop_glyph_cache_context(fz_context *ctx)
- {
- if (!ctx || !ctx->glyph_cache)
- return;
- fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
- ctx->glyph_cache->refs--;
- if (ctx->glyph_cache->refs == 0)
- {
- do_purge(ctx);
- 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;
- }
- float
- fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf)
- {
- float size = fz_matrix_expansion(*ctm);
- int q, hq, vq, qmin;
- float pix_e, pix_f, r, hr, vr, rmin;
- /* Quantise the subpixel positions. First, in the direction of
- * movement (i.e. normally X). We never need more than 4 subpixel
- * positions for glyphs - arguably even that is too much.
- * Suppress this as we get larger, because it makes less impact. */
- if (size >= 48)
- q = 0, r = 0.5f;
- else if (size >= 24)
- q = 128, r = 0.25f;
- else
- q = 192, r = 0.125f;
- /* Then in the 'downward' direction (normally Y). */
- if (size >= 8)
- qmin = 0, rmin = 0.5f;
- else if (size >= 4)
- qmin = 128, rmin = 0.25f;
- else
- qmin = 192, rmin = 0.125f;
- /* Suppress subpixel antialiasing in y axis if we have a horizontal
- * matrix, and in x axis if we have a vertical matrix, unless we're
- * really small. */
- hq = vq = q;
- hr = vr = r;
- if (ctm->a == 0 && ctm->d == 0)
- hq = qmin, hr = rmin;
- if (ctm->b == 0 && ctm->c == 0)
- vq = qmin, vr = rmin;
- /* Split translation into pixel and subpixel parts */
- subpix_ctm->a = ctm->a;
- subpix_ctm->b = ctm->b;
- subpix_ctm->c = ctm->c;
- subpix_ctm->d = ctm->d;
- subpix_ctm->e = ctm->e + hr;
- pix_e = floorf(subpix_ctm->e);
- subpix_ctm->e -= pix_e;
- subpix_ctm->f = ctm->f + vr;
- pix_f = floorf(subpix_ctm->f);
- subpix_ctm->f -= pix_f;
- /* Quantise the subpixel part */
- *qe = (int)(subpix_ctm->e * 256) & hq;
- subpix_ctm->e = *qe / 256.0f;
- *qf = (int)(subpix_ctm->f * 256) & vq;
- subpix_ctm->f = *qf / 256.0f;
- /* Reassemble the complete translation */
- ctm->e = subpix_ctm->e + pix_e;
- ctm->f = subpix_ctm->f + pix_f;
- return size;
- }
- fz_glyph *
- fz_render_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm, fz_matrix ctm, fz_colorspace *model, const fz_stroke_state *stroke, const fz_irect *scissor, int aa)
- {
- if (fz_font_ft_face(ctx, font))
- {
- fz_matrix subpix_trm;
- unsigned char qe, qf;
- if (stroke->dash_len > 0)
- return NULL;
- (void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf);
- return fz_render_ft_stroked_glyph(ctx, font, gid, subpix_trm, ctm, stroke, aa);
- }
- return fz_render_glyph(ctx, font, gid, trm, model, scissor, 1, aa);
- }
- static unsigned do_hash(unsigned char *s, int len)
- {
- unsigned val = 0;
- int i;
- for (i = 0; i < len; i++)
- {
- val += s[i];
- val += (val << 10);
- val ^= (val >> 6);
- }
- val += (val << 3);
- val ^= (val >> 11);
- val += (val << 15);
- return val;
- }
- static inline void
- move_to_front(fz_glyph_cache *cache, fz_glyph_cache_entry *entry)
- {
- if (entry->lru_prev == NULL)
- return; /* At front already */
- /* Unlink */
- entry->lru_prev->lru_next = entry->lru_next;
- if (entry->lru_next)
- entry->lru_next->lru_prev = entry->lru_prev;
- else
- cache->lru_tail = entry->lru_prev;
- /* Relink */
- entry->lru_next = cache->lru_head;
- if (entry->lru_next)
- entry->lru_next->lru_prev = entry;
- cache->lru_head = entry;
- entry->lru_prev = NULL;
- }
- fz_glyph *
- fz_render_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, fz_colorspace *model, const fz_irect *scissor, int alpha, int aa)
- {
- fz_glyph_cache *cache;
- fz_glyph_key key;
- fz_matrix subpix_ctm;
- fz_irect subpix_scissor;
- float size;
- fz_glyph *val;
- int do_cache, locked, caching;
- fz_glyph_cache_entry *entry;
- unsigned hash;
- int is_ft_font = !!fz_font_ft_face(ctx, font);
- fz_var(locked);
- fz_var(caching);
- fz_var(val);
- memset(&key, 0, sizeof key);
- size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f);
- if (size <= MAX_GLYPH_SIZE)
- {
- scissor = &fz_infinite_irect;
- do_cache = 1;
- }
- else
- {
- if (is_ft_font)
- return NULL;
- subpix_scissor.x0 = scissor->x0 - floorf(ctm->e);
- subpix_scissor.y0 = scissor->y0 - floorf(ctm->f);
- subpix_scissor.x1 = scissor->x1 - floorf(ctm->e);
- subpix_scissor.y1 = scissor->y1 - floorf(ctm->f);
- scissor = &subpix_scissor;
- do_cache = 0;
- }
- cache = ctx->glyph_cache;
- key.font = font;
- key.gid = gid;
- key.a = subpix_ctm.a * 65536;
- key.b = subpix_ctm.b * 65536;
- key.c = subpix_ctm.c * 65536;
- key.d = subpix_ctm.d * 65536;
- key.aa = aa;
- hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN;
- fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
- entry = cache->entry[hash];
- while (entry)
- {
- if (memcmp(&entry->key, &key, sizeof(key)) == 0)
- {
- move_to_front(cache, entry);
- val = fz_keep_glyph(ctx, entry->val);
- fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
- return val;
- }
- entry = entry->bucket_next;
- }
- locked = 1;
- caching = 0;
- val = NULL;
- fz_try(ctx)
- {
- if (is_ft_font)
- {
- val = fz_render_ft_glyph(ctx, font, gid, subpix_ctm, aa);
- }
- else if (fz_font_t3_procs(ctx, font))
- {
- /* 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, subpix_ctm, model, scissor, aa);
- fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
- locked = 1;
- }
- else
- {
- fz_warn(ctx, "assert: uninitialized font structure");
- }
- if (val && do_cache)
- {
- if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE)
- {
- /* If we throw an exception whilst caching,
- * just ignore the exception and carry on. */
- caching = 1;
- if (!is_ft_font)
- {
- /* We had to unlock. Someone else might
- * have rendered in the meantime */
- entry = cache->entry[hash];
- while (entry)
- {
- if (memcmp(&entry->key, &key, sizeof(key)) == 0)
- {
- fz_drop_glyph(ctx, val);
- move_to_front(cache, entry);
- val = fz_keep_glyph(ctx, entry->val);
- goto unlock_and_return_val;
- }
- entry = entry->bucket_next;
- }
- }
- entry = fz_malloc_struct(ctx, fz_glyph_cache_entry);
- entry->key = key;
- entry->hash = hash;
- entry->bucket_next = cache->entry[hash];
- if (entry->bucket_next)
- entry->bucket_next->bucket_prev = entry;
- cache->entry[hash] = entry;
- entry->val = fz_keep_glyph(ctx, val);
- fz_keep_font(ctx, key.font);
- entry->lru_next = cache->lru_head;
- if (entry->lru_next)
- entry->lru_next->lru_prev = entry;
- else
- cache->lru_tail = entry;
- cache->lru_head = entry;
- cache->total += fz_glyph_size(ctx, val);
- while (cache->total > MAX_CACHE_SIZE)
- {
- #ifndef NDEBUG
- cache->num_evictions++;
- cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val);
- #endif
- drop_glyph_cache_entry(ctx, cache->lru_tail);
- }
- }
- }
- unlock_and_return_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;
- }
- fz_pixmap *
- fz_render_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, const fz_irect *scissor, int aa)
- {
- fz_pixmap *val = NULL;
- unsigned char qe, qf;
- fz_matrix subpix_ctm;
- float size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &qe, &qf);
- int is_ft_font = !!fz_font_ft_face(ctx, font);
- if (size <= MAX_GLYPH_SIZE)
- {
- scissor = &fz_infinite_irect;
- }
- else
- {
- if (is_ft_font)
- return NULL;
- }
- if (is_ft_font)
- {
- val = fz_render_ft_glyph_pixmap(ctx, font, gid, subpix_ctm, aa);
- }
- else if (fz_font_t3_procs(ctx, font))
- {
- val = fz_render_t3_glyph_pixmap(ctx, font, gid, subpix_ctm, NULL, scissor, aa);
- }
- else
- {
- fz_warn(ctx, "assert: uninitialized font structure");
- val = NULL;
- }
- return val;
- }
- void
- fz_dump_glyph_cache_stats(fz_context *ctx, fz_output *out)
- {
- fz_glyph_cache *cache = ctx->glyph_cache;
- fz_write_printf(ctx, out, "Glyph Cache Size: %zu\n", cache->total);
- #ifndef NDEBUG
- fz_write_printf(ctx, out, "Glyph Cache Evictions: %d (%zu bytes)\n", cache->num_evictions, cache->evicted);
- #endif
- }
|