/src/mupdf/source/fitz/draw-glyph.c
Line | Count | Source (jump to first uncovered line) |
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 | 6.09M | #define MAX_GLYPH_SIZE 256 |
32 | 564k | #define MAX_CACHE_SIZE (1024*1024) |
33 | | |
34 | 18.3M | #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 | 1.12M | { |
73 | 1.12M | if (glyph == NULL) |
74 | 0 | return 0; |
75 | 1.12M | return sizeof(fz_glyph) + glyph->size + fz_pixmap_size(ctx, glyph->pixmap); |
76 | 1.12M | } |
77 | | |
78 | | void |
79 | | fz_new_glyph_cache_context(fz_context *ctx) |
80 | 15.6k | { |
81 | 15.6k | fz_glyph_cache *cache; |
82 | | |
83 | 15.6k | cache = fz_malloc_struct(ctx, fz_glyph_cache); |
84 | 15.6k | cache->total = 0; |
85 | 15.6k | cache->refs = 1; |
86 | | |
87 | 15.6k | ctx->glyph_cache = cache; |
88 | 15.6k | } |
89 | | |
90 | | static void |
91 | | drop_glyph_cache_entry(fz_context *ctx, fz_glyph_cache_entry *entry) |
92 | 557k | { |
93 | 557k | fz_glyph_cache *cache = ctx->glyph_cache; |
94 | | |
95 | 557k | if (entry->lru_next) |
96 | 529k | entry->lru_next->lru_prev = entry->lru_prev; |
97 | 27.7k | else |
98 | 27.7k | cache->lru_tail = entry->lru_prev; |
99 | 557k | if (entry->lru_prev) |
100 | 536k | entry->lru_prev->lru_next = entry->lru_next; |
101 | 21.3k | else |
102 | 21.3k | cache->lru_head = entry->lru_next; |
103 | 557k | cache->total -= fz_glyph_size(ctx, entry->val); |
104 | 557k | if (entry->bucket_next) |
105 | 210k | entry->bucket_next->bucket_prev = entry->bucket_prev; |
106 | 557k | if (entry->bucket_prev) |
107 | 6.80k | entry->bucket_prev->bucket_next = entry->bucket_next; |
108 | 550k | else |
109 | 550k | cache->entry[entry->hash] = entry->bucket_next; |
110 | 557k | fz_drop_font(ctx, entry->key.font); |
111 | 557k | fz_drop_glyph(ctx, entry->val); |
112 | 557k | fz_free(ctx, entry); |
113 | 557k | } |
114 | | |
115 | | /* The glyph cache lock is always held when this function is called. */ |
116 | | static void |
117 | | do_purge(fz_context *ctx) |
118 | 27.4k | { |
119 | 27.4k | fz_glyph_cache *cache = ctx->glyph_cache; |
120 | 27.4k | int i; |
121 | | |
122 | 14.0M | for (i = 0; i < GLYPH_HASH_LEN; i++) |
123 | 13.9M | { |
124 | 14.5M | while (cache->entry[i]) |
125 | 550k | drop_glyph_cache_entry(ctx, cache->entry[i]); |
126 | 13.9M | } |
127 | | |
128 | 27.4k | cache->total = 0; |
129 | 27.4k | } |
130 | | |
131 | | void |
132 | | fz_purge_glyph_cache(fz_context *ctx) |
133 | 11.8k | { |
134 | 11.8k | fz_lock(ctx, FZ_LOCK_GLYPHCACHE); |
135 | 11.8k | do_purge(ctx); |
136 | 11.8k | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
137 | 11.8k | } |
138 | | |
139 | | void |
140 | | fz_drop_glyph_cache_context(fz_context *ctx) |
141 | 15.6k | { |
142 | 15.6k | if (!ctx || !ctx->glyph_cache) |
143 | 0 | return; |
144 | | |
145 | 15.6k | fz_lock(ctx, FZ_LOCK_GLYPHCACHE); |
146 | 15.6k | ctx->glyph_cache->refs--; |
147 | 15.6k | if (ctx->glyph_cache->refs == 0) |
148 | 15.6k | { |
149 | 15.6k | do_purge(ctx); |
150 | 15.6k | fz_free(ctx, ctx->glyph_cache); |
151 | 15.6k | ctx->glyph_cache = NULL; |
152 | 15.6k | } |
153 | 15.6k | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
154 | 15.6k | } |
155 | | |
156 | | fz_glyph_cache * |
157 | | fz_keep_glyph_cache(fz_context *ctx) |
158 | 0 | { |
159 | 0 | fz_lock(ctx, FZ_LOCK_GLYPHCACHE); |
160 | 0 | ctx->glyph_cache->refs++; |
161 | 0 | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
162 | 0 | return ctx->glyph_cache; |
163 | 0 | } |
164 | | |
165 | | float |
166 | | fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf) |
167 | 4.63M | { |
168 | 4.63M | float size = fz_matrix_expansion(*ctm); |
169 | 4.63M | int q, hq, vq, qmin; |
170 | 4.63M | 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 | 4.63M | if (size >= 48) |
177 | 176k | q = 0, r = 0.5f; |
178 | 4.46M | else if (size >= 24) |
179 | 86.7k | q = 128, r = 0.25f; |
180 | 4.37M | else |
181 | 4.37M | q = 192, r = 0.125f; |
182 | | |
183 | | /* Then in the 'downward' direction (normally Y). */ |
184 | 4.63M | if (size >= 8) |
185 | 3.96M | qmin = 0, rmin = 0.5f; |
186 | 672k | else if (size >= 4) |
187 | 377k | qmin = 128, rmin = 0.25f; |
188 | 295k | else |
189 | 295k | 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 | 4.63M | hq = vq = q; |
195 | 4.63M | hr = vr = r; |
196 | 4.63M | if (ctm->a == 0 && ctm->d == 0) |
197 | 54.1k | hq = qmin, hr = rmin; |
198 | 4.63M | if (ctm->b == 0 && ctm->c == 0) |
199 | 4.32M | vq = qmin, vr = rmin; |
200 | | |
201 | | /* Split translation into pixel and subpixel parts */ |
202 | 4.63M | subpix_ctm->a = ctm->a; |
203 | 4.63M | subpix_ctm->b = ctm->b; |
204 | 4.63M | subpix_ctm->c = ctm->c; |
205 | 4.63M | subpix_ctm->d = ctm->d; |
206 | 4.63M | subpix_ctm->e = ctm->e + hr; |
207 | 4.63M | pix_e = floorf(subpix_ctm->e); |
208 | 4.63M | subpix_ctm->e -= pix_e; |
209 | 4.63M | subpix_ctm->f = ctm->f + vr; |
210 | 4.63M | pix_f = floorf(subpix_ctm->f); |
211 | 4.63M | subpix_ctm->f -= pix_f; |
212 | | |
213 | | /* Quantise the subpixel part */ |
214 | 4.63M | *qe = (int)(subpix_ctm->e * 256) & hq; |
215 | 4.63M | subpix_ctm->e = *qe / 256.0f; |
216 | 4.63M | *qf = (int)(subpix_ctm->f * 256) & vq; |
217 | 4.63M | subpix_ctm->f = *qf / 256.0f; |
218 | | |
219 | | /* Reassemble the complete translation */ |
220 | 4.63M | ctm->e = subpix_ctm->e + pix_e; |
221 | 4.63M | ctm->f = subpix_ctm->f + pix_f; |
222 | | |
223 | 4.63M | return size; |
224 | 4.63M | } |
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 | 256k | { |
229 | 256k | if (fz_font_ft_face(ctx, font)) |
230 | 256k | { |
231 | 256k | fz_matrix subpix_trm; |
232 | 256k | unsigned char qe, qf; |
233 | | |
234 | 256k | if (stroke->dash_len > 0) |
235 | 40.8k | return NULL; |
236 | 215k | (void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf); |
237 | 215k | return fz_render_ft_stroked_glyph(ctx, font, gid, subpix_trm, ctm, stroke, aa); |
238 | 256k | } |
239 | 288 | return fz_render_glyph(ctx, font, gid, trm, model, scissor, 1, aa); |
240 | 256k | } |
241 | | |
242 | | static unsigned do_hash(unsigned char *s, int len) |
243 | 4.36M | { |
244 | 4.36M | unsigned val = 0; |
245 | 4.36M | int i; |
246 | 144M | for (i = 0; i < len; i++) |
247 | 139M | { |
248 | 139M | val += s[i]; |
249 | 139M | val += (val << 10); |
250 | 139M | val ^= (val >> 6); |
251 | 139M | } |
252 | 4.36M | val += (val << 3); |
253 | 4.36M | val ^= (val >> 11); |
254 | 4.36M | val += (val << 15); |
255 | 4.36M | return val; |
256 | 4.36M | } |
257 | | |
258 | | static inline void |
259 | | move_to_front(fz_glyph_cache *cache, fz_glyph_cache_entry *entry) |
260 | 3.50M | { |
261 | 3.50M | if (entry->lru_prev == NULL) |
262 | 354k | return; /* At front already */ |
263 | | |
264 | | /* Unlink */ |
265 | 3.14M | entry->lru_prev->lru_next = entry->lru_next; |
266 | 3.14M | if (entry->lru_next) |
267 | 3.10M | entry->lru_next->lru_prev = entry->lru_prev; |
268 | 46.1k | else |
269 | 46.1k | cache->lru_tail = entry->lru_prev; |
270 | | /* Relink */ |
271 | 3.14M | entry->lru_next = cache->lru_head; |
272 | 3.14M | if (entry->lru_next) |
273 | 3.14M | entry->lru_next->lru_prev = entry; |
274 | 3.14M | cache->lru_head = entry; |
275 | 3.14M | entry->lru_prev = NULL; |
276 | 3.14M | } |
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 | 4.42M | { |
281 | 4.42M | fz_glyph_cache *cache; |
282 | 4.42M | fz_glyph_key key; |
283 | 4.42M | fz_matrix subpix_ctm; |
284 | 4.42M | fz_irect subpix_scissor; |
285 | 4.42M | float size; |
286 | 4.42M | fz_glyph *val; |
287 | 4.42M | int do_cache, locked, caching; |
288 | 4.42M | fz_glyph_cache_entry *entry; |
289 | 4.42M | unsigned hash; |
290 | 4.42M | int is_ft_font = !!fz_font_ft_face(ctx, font); |
291 | | |
292 | 4.42M | fz_var(locked); |
293 | 4.42M | fz_var(caching); |
294 | 4.42M | fz_var(val); |
295 | | |
296 | 4.42M | memset(&key, 0, sizeof key); |
297 | 4.42M | size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f); |
298 | 4.42M | if (size <= MAX_GLYPH_SIZE) |
299 | 4.36M | { |
300 | 4.36M | scissor = &fz_infinite_irect; |
301 | 4.36M | do_cache = 1; |
302 | 4.36M | } |
303 | 53.0k | else |
304 | 53.0k | { |
305 | 53.0k | if (is_ft_font) |
306 | 52.9k | return NULL; |
307 | 61 | subpix_scissor.x0 = scissor->x0 - floorf(ctm->e); |
308 | 61 | subpix_scissor.y0 = scissor->y0 - floorf(ctm->f); |
309 | 61 | subpix_scissor.x1 = scissor->x1 - floorf(ctm->e); |
310 | 61 | subpix_scissor.y1 = scissor->y1 - floorf(ctm->f); |
311 | 61 | scissor = &subpix_scissor; |
312 | 61 | do_cache = 0; |
313 | 61 | } |
314 | | |
315 | 4.36M | cache = ctx->glyph_cache; |
316 | | |
317 | 4.36M | key.font = font; |
318 | 4.36M | key.gid = gid; |
319 | 4.36M | key.a = subpix_ctm.a * 65536; |
320 | 4.36M | key.b = subpix_ctm.b * 65536; |
321 | 4.36M | key.c = subpix_ctm.c * 65536; |
322 | 4.36M | key.d = subpix_ctm.d * 65536; |
323 | 4.36M | key.aa = aa; |
324 | | |
325 | 4.36M | hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN; |
326 | 4.36M | fz_lock(ctx, FZ_LOCK_GLYPHCACHE); |
327 | 4.36M | entry = cache->entry[hash]; |
328 | 7.93M | while (entry) |
329 | 7.07M | { |
330 | 7.07M | if (memcmp(&entry->key, &key, sizeof(key)) == 0) |
331 | 3.50M | { |
332 | 3.50M | move_to_front(cache, entry); |
333 | 3.50M | val = fz_keep_glyph(ctx, entry->val); |
334 | 3.50M | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
335 | 3.50M | return val; |
336 | 3.50M | } |
337 | 3.56M | entry = entry->bucket_next; |
338 | 3.56M | } |
339 | | |
340 | 865k | locked = 1; |
341 | 865k | caching = 0; |
342 | 865k | val = NULL; |
343 | | |
344 | 1.73M | fz_try(ctx) |
345 | 1.73M | { |
346 | 865k | if (is_ft_font) |
347 | 854k | { |
348 | 854k | val = fz_render_ft_glyph(ctx, font, gid, subpix_ctm, aa); |
349 | 854k | } |
350 | 10.5k | else if (fz_font_t3_procs(ctx, font)) |
351 | 10.5k | { |
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 | 10.5k | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
362 | 10.5k | locked = 0; |
363 | 10.5k | val = fz_render_t3_glyph(ctx, font, gid, subpix_ctm, model, scissor, aa); |
364 | 10.5k | fz_lock(ctx, FZ_LOCK_GLYPHCACHE); |
365 | 10.5k | locked = 1; |
366 | 10.5k | } |
367 | 0 | else |
368 | 0 | { |
369 | 0 | fz_warn(ctx, "assert: uninitialized font structure"); |
370 | 0 | } |
371 | 865k | if (val && do_cache) |
372 | 559k | { |
373 | 559k | if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) |
374 | 557k | { |
375 | | /* If we throw an exception whilst caching, |
376 | | * just ignore the exception and carry on. */ |
377 | 557k | caching = 1; |
378 | 557k | if (!is_ft_font) |
379 | 3.03k | { |
380 | | /* We had to unlock. Someone else might |
381 | | * have rendered in the meantime */ |
382 | 3.03k | entry = cache->entry[hash]; |
383 | 3.28k | while (entry) |
384 | 255 | { |
385 | 255 | if (memcmp(&entry->key, &key, sizeof(key)) == 0) |
386 | 0 | { |
387 | 0 | fz_drop_glyph(ctx, val); |
388 | 0 | move_to_front(cache, entry); |
389 | 0 | val = fz_keep_glyph(ctx, entry->val); |
390 | 0 | goto unlock_and_return_val; |
391 | 0 | } |
392 | 255 | entry = entry->bucket_next; |
393 | 255 | } |
394 | 3.03k | } |
395 | | |
396 | 557k | entry = fz_malloc_struct(ctx, fz_glyph_cache_entry); |
397 | 557k | entry->key = key; |
398 | 557k | entry->hash = hash; |
399 | 557k | entry->bucket_next = cache->entry[hash]; |
400 | 557k | if (entry->bucket_next) |
401 | 216k | entry->bucket_next->bucket_prev = entry; |
402 | 557k | cache->entry[hash] = entry; |
403 | 557k | entry->val = fz_keep_glyph(ctx, val); |
404 | 557k | fz_keep_font(ctx, key.font); |
405 | | |
406 | 557k | entry->lru_next = cache->lru_head; |
407 | 557k | if (entry->lru_next) |
408 | 551k | entry->lru_next->lru_prev = entry; |
409 | 5.44k | else |
410 | 5.44k | cache->lru_tail = entry; |
411 | 557k | cache->lru_head = entry; |
412 | | |
413 | 557k | cache->total += fz_glyph_size(ctx, val); |
414 | 564k | while (cache->total > MAX_CACHE_SIZE) |
415 | 6.88k | { |
416 | 6.88k | #ifndef NDEBUG |
417 | 6.88k | cache->num_evictions++; |
418 | 6.88k | cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val); |
419 | 6.88k | #endif |
420 | 6.88k | drop_glyph_cache_entry(ctx, cache->lru_tail); |
421 | 6.88k | } |
422 | 557k | } |
423 | 559k | } |
424 | 865k | unlock_and_return_val: |
425 | 865k | { |
426 | 865k | } |
427 | 865k | } |
428 | 1.73M | fz_always(ctx) |
429 | 865k | { |
430 | 865k | if (locked) |
431 | 865k | fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); |
432 | 865k | } |
433 | 865k | fz_catch(ctx) |
434 | 15 | { |
435 | 15 | if (caching) |
436 | 0 | fz_warn(ctx, "cannot encache glyph; continuing"); |
437 | 15 | else |
438 | 15 | fz_rethrow(ctx); |
439 | 15 | } |
440 | | |
441 | 865k | return val; |
442 | 865k | } |
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 | 0 | { |
447 | 0 | fz_pixmap *val = NULL; |
448 | 0 | unsigned char qe, qf; |
449 | 0 | fz_matrix subpix_ctm; |
450 | 0 | float size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &qe, &qf); |
451 | 0 | int is_ft_font = !!fz_font_ft_face(ctx, font); |
452 | |
|
453 | 0 | if (size <= MAX_GLYPH_SIZE) |
454 | 0 | { |
455 | 0 | scissor = &fz_infinite_irect; |
456 | 0 | } |
457 | 0 | else |
458 | 0 | { |
459 | 0 | if (is_ft_font) |
460 | 0 | return NULL; |
461 | 0 | } |
462 | | |
463 | 0 | if (is_ft_font) |
464 | 0 | { |
465 | 0 | val = fz_render_ft_glyph_pixmap(ctx, font, gid, subpix_ctm, aa); |
466 | 0 | } |
467 | 0 | else if (fz_font_t3_procs(ctx, font)) |
468 | 0 | { |
469 | 0 | val = fz_render_t3_glyph_pixmap(ctx, font, gid, subpix_ctm, NULL, scissor, aa); |
470 | 0 | } |
471 | 0 | else |
472 | 0 | { |
473 | 0 | fz_warn(ctx, "assert: uninitialized font structure"); |
474 | 0 | val = NULL; |
475 | 0 | } |
476 | |
|
477 | 0 | return val; |
478 | 0 | } |
479 | | |
480 | | void |
481 | | fz_dump_glyph_cache_stats(fz_context *ctx, fz_output *out) |
482 | 0 | { |
483 | 0 | fz_glyph_cache *cache = ctx->glyph_cache; |
484 | 0 | fz_write_printf(ctx, out, "Glyph Cache Size: %zu\n", cache->total); |
485 | 0 | #ifndef NDEBUG |
486 | 0 | fz_write_printf(ctx, out, "Glyph Cache Evictions: %d (%zu bytes)\n", cache->num_evictions, cache->evicted); |
487 | 0 | #endif |
488 | 0 | } |