comparison mupdf-source/source/fitz/draw-glyph.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "draw-imp.h"
25 #include "glyph-imp.h"
26 #include "pixmap-imp.h"
27
28 #include <string.h>
29 #include <math.h>
30
31 #define MAX_GLYPH_SIZE 256
32 #define MAX_CACHE_SIZE (1024*1024)
33
34 #define GLYPH_HASH_LEN 509
35
36 typedef struct
37 {
38 fz_font *font;
39 int a, b;
40 int c, d;
41 unsigned short gid;
42 unsigned char e, f;
43 int aa;
44 } fz_glyph_key;
45
46 typedef struct fz_glyph_cache_entry
47 {
48 fz_glyph_key key;
49 unsigned hash;
50 struct fz_glyph_cache_entry *lru_prev;
51 struct fz_glyph_cache_entry *lru_next;
52 struct fz_glyph_cache_entry *bucket_next;
53 struct fz_glyph_cache_entry *bucket_prev;
54 fz_glyph *val;
55 } fz_glyph_cache_entry;
56
57 struct fz_glyph_cache
58 {
59 int refs;
60 size_t total;
61 #ifndef NDEBUG
62 int num_evictions;
63 ptrdiff_t evicted;
64 #endif
65 fz_glyph_cache_entry *entry[GLYPH_HASH_LEN];
66 fz_glyph_cache_entry *lru_head;
67 fz_glyph_cache_entry *lru_tail;
68 };
69
70 static size_t
71 fz_glyph_size(fz_context *ctx, fz_glyph *glyph)
72 {
73 if (glyph == NULL)
74 return 0;
75 return sizeof(fz_glyph) + glyph->size + fz_pixmap_size(ctx, glyph->pixmap);
76 }
77
78 void
79 fz_new_glyph_cache_context(fz_context *ctx)
80 {
81 fz_glyph_cache *cache;
82
83 cache = fz_malloc_struct(ctx, fz_glyph_cache);
84 cache->total = 0;
85 cache->refs = 1;
86
87 ctx->glyph_cache = cache;
88 }
89
90 static void
91 drop_glyph_cache_entry(fz_context *ctx, fz_glyph_cache_entry *entry)
92 {
93 fz_glyph_cache *cache = ctx->glyph_cache;
94
95 if (entry->lru_next)
96 entry->lru_next->lru_prev = entry->lru_prev;
97 else
98 cache->lru_tail = entry->lru_prev;
99 if (entry->lru_prev)
100 entry->lru_prev->lru_next = entry->lru_next;
101 else
102 cache->lru_head = entry->lru_next;
103 cache->total -= fz_glyph_size(ctx, entry->val);
104 if (entry->bucket_next)
105 entry->bucket_next->bucket_prev = entry->bucket_prev;
106 if (entry->bucket_prev)
107 entry->bucket_prev->bucket_next = entry->bucket_next;
108 else
109 cache->entry[entry->hash] = entry->bucket_next;
110 fz_drop_font(ctx, entry->key.font);
111 fz_drop_glyph(ctx, entry->val);
112 fz_free(ctx, entry);
113 }
114
115 /* The glyph cache lock is always held when this function is called. */
116 static void
117 do_purge(fz_context *ctx)
118 {
119 fz_glyph_cache *cache = ctx->glyph_cache;
120 int i;
121
122 for (i = 0; i < GLYPH_HASH_LEN; i++)
123 {
124 while (cache->entry[i])
125 drop_glyph_cache_entry(ctx, cache->entry[i]);
126 }
127
128 cache->total = 0;
129 }
130
131 void
132 fz_purge_glyph_cache(fz_context *ctx)
133 {
134 fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
135 do_purge(ctx);
136 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
137 }
138
139 void
140 fz_drop_glyph_cache_context(fz_context *ctx)
141 {
142 if (!ctx || !ctx->glyph_cache)
143 return;
144
145 fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
146 ctx->glyph_cache->refs--;
147 if (ctx->glyph_cache->refs == 0)
148 {
149 do_purge(ctx);
150 fz_free(ctx, ctx->glyph_cache);
151 ctx->glyph_cache = NULL;
152 }
153 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
154 }
155
156 fz_glyph_cache *
157 fz_keep_glyph_cache(fz_context *ctx)
158 {
159 fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
160 ctx->glyph_cache->refs++;
161 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
162 return ctx->glyph_cache;
163 }
164
165 float
166 fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf)
167 {
168 float size = fz_matrix_expansion(*ctm);
169 int q, hq, vq, qmin;
170 float pix_e, pix_f, r, hr, vr, rmin;
171
172 /* Quantise the subpixel positions. First, in the direction of
173 * movement (i.e. normally X). We never need more than 4 subpixel
174 * positions for glyphs - arguably even that is too much.
175 * Suppress this as we get larger, because it makes less impact. */
176 if (size >= 48)
177 q = 0, r = 0.5f;
178 else if (size >= 24)
179 q = 128, r = 0.25f;
180 else
181 q = 192, r = 0.125f;
182
183 /* Then in the 'downward' direction (normally Y). */
184 if (size >= 8)
185 qmin = 0, rmin = 0.5f;
186 else if (size >= 4)
187 qmin = 128, rmin = 0.25f;
188 else
189 qmin = 192, rmin = 0.125f;
190
191 /* Suppress subpixel antialiasing in y axis if we have a horizontal
192 * matrix, and in x axis if we have a vertical matrix, unless we're
193 * really small. */
194 hq = vq = q;
195 hr = vr = r;
196 if (ctm->a == 0 && ctm->d == 0)
197 hq = qmin, hr = rmin;
198 if (ctm->b == 0 && ctm->c == 0)
199 vq = qmin, vr = rmin;
200
201 /* Split translation into pixel and subpixel parts */
202 subpix_ctm->a = ctm->a;
203 subpix_ctm->b = ctm->b;
204 subpix_ctm->c = ctm->c;
205 subpix_ctm->d = ctm->d;
206 subpix_ctm->e = ctm->e + hr;
207 pix_e = floorf(subpix_ctm->e);
208 subpix_ctm->e -= pix_e;
209 subpix_ctm->f = ctm->f + vr;
210 pix_f = floorf(subpix_ctm->f);
211 subpix_ctm->f -= pix_f;
212
213 /* Quantise the subpixel part */
214 *qe = (int)(subpix_ctm->e * 256) & hq;
215 subpix_ctm->e = *qe / 256.0f;
216 *qf = (int)(subpix_ctm->f * 256) & vq;
217 subpix_ctm->f = *qf / 256.0f;
218
219 /* Reassemble the complete translation */
220 ctm->e = subpix_ctm->e + pix_e;
221 ctm->f = subpix_ctm->f + pix_f;
222
223 return size;
224 }
225
226 fz_glyph *
227 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)
228 {
229 if (fz_font_ft_face(ctx, font))
230 {
231 fz_matrix subpix_trm;
232 unsigned char qe, qf;
233
234 if (stroke->dash_len > 0)
235 return NULL;
236 (void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf);
237 return fz_render_ft_stroked_glyph(ctx, font, gid, subpix_trm, ctm, stroke, aa);
238 }
239 return fz_render_glyph(ctx, font, gid, trm, model, scissor, 1, aa);
240 }
241
242 static unsigned do_hash(unsigned char *s, int len)
243 {
244 unsigned val = 0;
245 int i;
246 for (i = 0; i < len; i++)
247 {
248 val += s[i];
249 val += (val << 10);
250 val ^= (val >> 6);
251 }
252 val += (val << 3);
253 val ^= (val >> 11);
254 val += (val << 15);
255 return val;
256 }
257
258 static inline void
259 move_to_front(fz_glyph_cache *cache, fz_glyph_cache_entry *entry)
260 {
261 if (entry->lru_prev == NULL)
262 return; /* At front already */
263
264 /* Unlink */
265 entry->lru_prev->lru_next = entry->lru_next;
266 if (entry->lru_next)
267 entry->lru_next->lru_prev = entry->lru_prev;
268 else
269 cache->lru_tail = entry->lru_prev;
270 /* Relink */
271 entry->lru_next = cache->lru_head;
272 if (entry->lru_next)
273 entry->lru_next->lru_prev = entry;
274 cache->lru_head = entry;
275 entry->lru_prev = NULL;
276 }
277
278 fz_glyph *
279 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)
280 {
281 fz_glyph_cache *cache;
282 fz_glyph_key key;
283 fz_matrix subpix_ctm;
284 fz_irect subpix_scissor;
285 float size;
286 fz_glyph *val;
287 int do_cache, locked, caching;
288 fz_glyph_cache_entry *entry;
289 unsigned hash;
290 int is_ft_font = !!fz_font_ft_face(ctx, font);
291
292 fz_var(locked);
293 fz_var(caching);
294 fz_var(val);
295
296 memset(&key, 0, sizeof key);
297 size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f);
298 if (size <= MAX_GLYPH_SIZE)
299 {
300 scissor = &fz_infinite_irect;
301 do_cache = 1;
302 }
303 else
304 {
305 if (is_ft_font)
306 return NULL;
307 subpix_scissor.x0 = scissor->x0 - floorf(ctm->e);
308 subpix_scissor.y0 = scissor->y0 - floorf(ctm->f);
309 subpix_scissor.x1 = scissor->x1 - floorf(ctm->e);
310 subpix_scissor.y1 = scissor->y1 - floorf(ctm->f);
311 scissor = &subpix_scissor;
312 do_cache = 0;
313 }
314
315 cache = ctx->glyph_cache;
316
317 key.font = font;
318 key.gid = gid;
319 key.a = subpix_ctm.a * 65536;
320 key.b = subpix_ctm.b * 65536;
321 key.c = subpix_ctm.c * 65536;
322 key.d = subpix_ctm.d * 65536;
323 key.aa = aa;
324
325 hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN;
326 fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
327 entry = cache->entry[hash];
328 while (entry)
329 {
330 if (memcmp(&entry->key, &key, sizeof(key)) == 0)
331 {
332 move_to_front(cache, entry);
333 val = fz_keep_glyph(ctx, entry->val);
334 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
335 return val;
336 }
337 entry = entry->bucket_next;
338 }
339
340 locked = 1;
341 caching = 0;
342 val = NULL;
343
344 fz_try(ctx)
345 {
346 if (is_ft_font)
347 {
348 val = fz_render_ft_glyph(ctx, font, gid, subpix_ctm, aa);
349 }
350 else if (fz_font_t3_procs(ctx, font))
351 {
352 /* We drop the glyphcache here, and execute the t3
353 * glyph code. The danger here is that some other
354 * thread will come along, and want the same glyph
355 * too. If it does, we may both end up rendering
356 * pixmaps. We cope with this later on, by ensuring
357 * that only one gets inserted into the cache. If
358 * we insert ours to find one already there, we
359 * abandon ours, and use the one there already.
360 */
361 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
362 locked = 0;
363 val = fz_render_t3_glyph(ctx, font, gid, subpix_ctm, model, scissor, aa);
364 fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
365 locked = 1;
366 }
367 else
368 {
369 fz_warn(ctx, "assert: uninitialized font structure");
370 }
371 if (val && do_cache)
372 {
373 if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE)
374 {
375 /* If we throw an exception whilst caching,
376 * just ignore the exception and carry on. */
377 caching = 1;
378 if (!is_ft_font)
379 {
380 /* We had to unlock. Someone else might
381 * have rendered in the meantime */
382 entry = cache->entry[hash];
383 while (entry)
384 {
385 if (memcmp(&entry->key, &key, sizeof(key)) == 0)
386 {
387 fz_drop_glyph(ctx, val);
388 move_to_front(cache, entry);
389 val = fz_keep_glyph(ctx, entry->val);
390 goto unlock_and_return_val;
391 }
392 entry = entry->bucket_next;
393 }
394 }
395
396 entry = fz_malloc_struct(ctx, fz_glyph_cache_entry);
397 entry->key = key;
398 entry->hash = hash;
399 entry->bucket_next = cache->entry[hash];
400 if (entry->bucket_next)
401 entry->bucket_next->bucket_prev = entry;
402 cache->entry[hash] = entry;
403 entry->val = fz_keep_glyph(ctx, val);
404 fz_keep_font(ctx, key.font);
405
406 entry->lru_next = cache->lru_head;
407 if (entry->lru_next)
408 entry->lru_next->lru_prev = entry;
409 else
410 cache->lru_tail = entry;
411 cache->lru_head = entry;
412
413 cache->total += fz_glyph_size(ctx, val);
414 while (cache->total > MAX_CACHE_SIZE)
415 {
416 #ifndef NDEBUG
417 cache->num_evictions++;
418 cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val);
419 #endif
420 drop_glyph_cache_entry(ctx, cache->lru_tail);
421 }
422 }
423 }
424 unlock_and_return_val:
425 {
426 }
427 }
428 fz_always(ctx)
429 {
430 if (locked)
431 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
432 }
433 fz_catch(ctx)
434 {
435 if (caching)
436 fz_warn(ctx, "cannot encache glyph; continuing");
437 else
438 fz_rethrow(ctx);
439 }
440
441 return val;
442 }
443
444 fz_pixmap *
445 fz_render_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, const fz_irect *scissor, int aa)
446 {
447 fz_pixmap *val = NULL;
448 unsigned char qe, qf;
449 fz_matrix subpix_ctm;
450 float size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &qe, &qf);
451 int is_ft_font = !!fz_font_ft_face(ctx, font);
452
453 if (size <= MAX_GLYPH_SIZE)
454 {
455 scissor = &fz_infinite_irect;
456 }
457 else
458 {
459 if (is_ft_font)
460 return NULL;
461 }
462
463 if (is_ft_font)
464 {
465 val = fz_render_ft_glyph_pixmap(ctx, font, gid, subpix_ctm, aa);
466 }
467 else if (fz_font_t3_procs(ctx, font))
468 {
469 val = fz_render_t3_glyph_pixmap(ctx, font, gid, subpix_ctm, NULL, scissor, aa);
470 }
471 else
472 {
473 fz_warn(ctx, "assert: uninitialized font structure");
474 val = NULL;
475 }
476
477 return val;
478 }
479
480 void
481 fz_dump_glyph_cache_stats(fz_context *ctx, fz_output *out)
482 {
483 fz_glyph_cache *cache = ctx->glyph_cache;
484 fz_write_printf(ctx, out, "Glyph Cache Size: %zu\n", cache->total);
485 #ifndef NDEBUG
486 fz_write_printf(ctx, out, "Glyph Cache Evictions: %d (%zu bytes)\n", cache->num_evictions, cache->evicted);
487 #endif
488 }