Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "mupdf/fitz.h"
2
#include "draw-imp.h"
3
4
#define MAX_GLYPH_SIZE 256
5
#define MAX_CACHE_SIZE (1024*1024)
6
7
#define GLYPH_HASH_LEN 509
8
9
typedef struct fz_glyph_cache_entry_s fz_glyph_cache_entry;
10
typedef struct fz_glyph_key_s fz_glyph_key;
11
12
struct fz_glyph_key_s
13
{
14
fz_font *font;
15
int a, b;
16
int c, d;
17
unsigned short gid;
18
unsigned char e, f;
19
int aa;
20
};
21
22
struct fz_glyph_cache_entry_s
23
{
24
fz_glyph_key key;
25
unsigned hash;
26
fz_glyph_cache_entry *lru_prev;
27
fz_glyph_cache_entry *lru_next;
28
fz_glyph_cache_entry *bucket_next;
29
fz_glyph_cache_entry *bucket_prev;
30
fz_glyph *val;
31
};
32
33
struct fz_glyph_cache_s
34
{
35
int refs;
36
int total;
37
#ifndef NDEBUG
38
int num_evictions;
39
int evicted;
40
#endif
41
fz_glyph_cache_entry *entry[GLYPH_HASH_LEN];
42
fz_glyph_cache_entry *lru_head;
43
fz_glyph_cache_entry *lru_tail;
44
};
45
46
void
47
fz_new_glyph_cache_context(fz_context *ctx)
48
{
49
fz_glyph_cache *cache;
50
51
cache = fz_malloc_struct(ctx, fz_glyph_cache);
52
cache->total = 0;
53
cache->refs = 1;
54
55
ctx->glyph_cache = cache;
56
}
57
58
static void
59
drop_glyph_cache_entry(fz_context *ctx, fz_glyph_cache_entry *entry)
60
{
61
fz_glyph_cache *cache = ctx->glyph_cache;
62
63
if (entry->lru_next)
64
entry->lru_next->lru_prev = entry->lru_prev;
65
else
66
cache->lru_tail = entry->lru_prev;
67
if (entry->lru_prev)
68
entry->lru_prev->lru_next = entry->lru_next;
69
else
70
cache->lru_head = entry->lru_next;
71
cache->total -= fz_glyph_size(ctx, entry->val);
72
if (entry->bucket_next)
73
entry->bucket_next->bucket_prev = entry->bucket_prev;
74
if (entry->bucket_prev)
75
entry->bucket_prev->bucket_next = entry->bucket_next;
76
else
77
cache->entry[entry->hash] = entry->bucket_next;
78
fz_drop_font(ctx, entry->key.font);
79
fz_drop_glyph(ctx, entry->val);
80
fz_free(ctx, entry);
81
}
82
83
/* The glyph cache lock is always held when this function is called. */
84
static void
85
do_purge(fz_context *ctx)
86
{
87
fz_glyph_cache *cache = ctx->glyph_cache;
88
int i;
89
90
for (i = 0; i < GLYPH_HASH_LEN; i++)
91
{
92
while (cache->entry[i])
93
drop_glyph_cache_entry(ctx, cache->entry[i]);
94
}
95
96
cache->total = 0;
97
}
98
99
void
100
fz_purge_glyph_cache(fz_context *ctx)
101
{
102
fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
103
do_purge(ctx);
104
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
105
}
106
107
void
108
fz_drop_glyph_cache_context(fz_context *ctx)
109
{
110
if (!ctx->glyph_cache)
111
return;
112
113
fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
114
ctx->glyph_cache->refs--;
115
if (ctx->glyph_cache->refs == 0)
116
{
117
do_purge(ctx);
118
fz_free(ctx, ctx->glyph_cache);
119
ctx->glyph_cache = NULL;
120
}
121
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
122
}
123
124
fz_glyph_cache *
125
fz_keep_glyph_cache(fz_context *ctx)
126
{
127
fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
128
ctx->glyph_cache->refs++;
129
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
130
return ctx->glyph_cache;
131
}
132
133
float
134
fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf)
135
{
136
float size = fz_matrix_expansion(ctm);
137
int q;
138
float pix_e, pix_f, r;
139
140
/* Quantise the subpixel positions. */
141
/* We never need more than 4 subpixel positions for glyphs - arguably
142
* even that is too much. */
143
if (size >= 48)
144
q = 0, r = 0.5f;
145
else if (size >= 24)
146
q = 128, r = 0.25f;
147
else
148
q = 192, r = 0.125f;
149
150
/* Split translation into pixel and subpixel parts */
151
subpix_ctm->a = ctm->a;
152
subpix_ctm->b = ctm->b;
153
subpix_ctm->c = ctm->c;
154
subpix_ctm->d = ctm->d;
155
subpix_ctm->e = ctm->e + r;
156
pix_e = floorf(subpix_ctm->e);
157
subpix_ctm->e -= pix_e;
158
subpix_ctm->f = ctm->f + r;
159
pix_f = floorf(subpix_ctm->f);
160
subpix_ctm->f -= pix_f;
161
162
/* Quantise the subpixel part */
163
*qe = (int)(subpix_ctm->e * 256) & q;
164
subpix_ctm->e = *qe / 256.0f;
165
*qf = (int)(subpix_ctm->f * 256) & q;
166
subpix_ctm->f = *qf / 256.0f;
167
168
/* Reassemble the complete translation */
169
ctm->e = subpix_ctm->e + pix_e;
170
ctm->f = subpix_ctm->f + pix_f;
171
172
return size;
173
}
174
175
fz_glyph *
176
fz_render_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *stroke, const fz_irect *scissor)
177
{
178
if (font->ft_face)
179
{
180
fz_matrix subpix_trm;
181
unsigned char qe, qf;
182
183
if (stroke->dash_len > 0)
184
return NULL;
185
(void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf);
186
return fz_render_ft_stroked_glyph(ctx, font, gid, &subpix_trm, ctm, stroke);
187
}
188
return fz_render_glyph(ctx, font, gid, trm, NULL, scissor);
189
}
190
191
fz_pixmap *
192
fz_render_stroked_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm, const fz_matrix *ctm, fz_stroke_state *stroke, const fz_irect *scissor)
193
{
194
if (font->ft_face)
195
{
196
fz_matrix subpix_trm;
197
unsigned char qe, qf;
198
199
if (stroke->dash_len > 0)
200
return NULL;
201
(void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf);
202
return fz_render_ft_stroked_glyph_pixmap(ctx, font, gid, &subpix_trm, ctm, stroke);
203
}
204
return fz_render_glyph_pixmap(ctx, font, gid, trm, NULL, scissor);
205
}
206
207
static unsigned do_hash(unsigned char *s, int len)
208
{
209
unsigned val = 0;
210
int i;
211
for (i = 0; i < len; i++)
212
{
213
val += s[i];
214
val += (val << 10);
215
val ^= (val >> 6);
216
}
217
val += (val << 3);
218
val ^= (val >> 11);
219
val += (val << 15);
220
return val;
221
}
222
223
static inline void
224
move_to_front(fz_glyph_cache *cache, fz_glyph_cache_entry *entry)
225
{
226
if (entry->lru_prev == NULL)
227
return; /* At front already */
228
229
/* Unlink */
230
entry->lru_prev->lru_next = entry->lru_next;
231
if (entry->lru_next)
232
entry->lru_next->lru_prev = entry->lru_prev;
233
else
234
cache->lru_tail = entry->lru_prev;
235
/* Relink */
236
entry->lru_next = cache->lru_head;
237
if (entry->lru_next)
238
entry->lru_next->lru_prev = entry;
239
cache->lru_head = entry;
240
entry->lru_prev = NULL;
241
}
242
243
fz_glyph *
244
fz_render_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, fz_colorspace *model, const fz_irect *scissor)
245
{
246
fz_glyph_cache *cache;
247
fz_glyph_key key;
248
fz_matrix subpix_ctm;
249
fz_irect subpix_scissor;
250
float size;
251
fz_glyph *val;
252
int do_cache, locked, caching;
253
fz_glyph_cache_entry *entry;
254
unsigned hash;
255
256
fz_var(locked);
257
fz_var(caching);
258
fz_var(val);
259
260
memset(&key, 0, sizeof key);
261
size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f);
262
if (size <= MAX_GLYPH_SIZE)
263
{
264
scissor = &fz_infinite_irect;
265
do_cache = 1;
266
}
267
else
268
{
269
if (font->ft_face)
270
return NULL;
271
subpix_scissor.x0 = scissor->x0 - floorf(ctm->e);
272
subpix_scissor.y0 = scissor->y0 - floorf(ctm->f);
273
subpix_scissor.x1 = scissor->x1 - floorf(ctm->e);
274
subpix_scissor.y1 = scissor->y1 - floorf(ctm->f);
275
scissor = &subpix_scissor;
276
do_cache = 0;
277
}
278
279
cache = ctx->glyph_cache;
280
281
key.font = font;
282
key.gid = gid;
283
key.a = subpix_ctm.a * 65536;
284
key.b = subpix_ctm.b * 65536;
285
key.c = subpix_ctm.c * 65536;
286
key.d = subpix_ctm.d * 65536;
287
key.aa = fz_aa_level(ctx);
288
289
fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
290
hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN;
291
entry = cache->entry[hash];
292
while (entry)
293
{
294
if (memcmp(&entry->key, &key, sizeof(key)) == 0)
295
{
296
move_to_front(cache, entry);
297
val = fz_keep_glyph(ctx, entry->val);
298
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
299
return val;
300
}
301
entry = entry->bucket_next;
302
}
303
304
locked = 1;
305
caching = 0;
306
val = NULL;
307
308
fz_try(ctx)
309
{
310
if (font->ft_face)
311
{
312
val = fz_render_ft_glyph(ctx, font, gid, &subpix_ctm, key.aa);
313
}
314
else if (font->t3procs)
315
{
316
/* We drop the glyphcache here, and execute the t3
317
* glyph code. The danger here is that some other
318
* thread will come along, and want the same glyph
319
* too. If it does, we may both end up rendering
320
* pixmaps. We cope with this later on, by ensuring
321
* that only one gets inserted into the cache. If
322
* we insert ours to find one already there, we
323
* abandon ours, and use the one there already.
324
*/
325
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
326
locked = 0;
327
val = fz_render_t3_glyph(ctx, font, gid, &subpix_ctm, model, scissor);
328
fz_lock(ctx, FZ_LOCK_GLYPHCACHE);
329
locked = 1;
330
}
331
else
332
{
333
fz_warn(ctx, "assert: uninitialized font structure");
334
}
335
if (val && do_cache)
336
{
337
if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE)
338
{
339
/* If we throw an exception whilst caching,
340
* just ignore the exception and carry on. */
341
caching = 1;
342
if (!font->ft_face)
343
{
344
/* We had to unlock. Someone else might
345
* have rendered in the meantime */
346
entry = cache->entry[hash];
347
while (entry)
348
{
349
if (memcmp(&entry->key, &key, sizeof(key)) == 0)
350
{
351
fz_drop_glyph(ctx, val);
352
move_to_front(cache, entry);
353
val = fz_keep_glyph(ctx, entry->val);
354
goto unlock_and_return_val;
355
}
356
entry = entry->bucket_next;
357
}
358
}
359
360
entry = fz_malloc_struct(ctx, fz_glyph_cache_entry);
361
entry->key = key;
362
entry->hash = hash;
363
entry->bucket_next = cache->entry[hash];
364
if (entry->bucket_next)
365
entry->bucket_next->bucket_prev = entry;
366
cache->entry[hash] = entry;
367
entry->val = fz_keep_glyph(ctx, val);
368
fz_keep_font(ctx, key.font);
369
370
entry->lru_next = cache->lru_head;
371
if (entry->lru_next)
372
entry->lru_next->lru_prev = entry;
373
else
374
cache->lru_tail = entry;
375
cache->lru_head = entry;
376
377
cache->total += fz_glyph_size(ctx, val);
378
while (cache->total > MAX_CACHE_SIZE)
379
{
380
#ifndef NDEBUG
381
cache->num_evictions++;
382
cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val);
383
#endif
384
drop_glyph_cache_entry(ctx, cache->lru_tail);
385
}
386
387
}
388
}
389
unlock_and_return_val:
390
{
391
}
392
}
393
fz_always(ctx)
394
{
395
if (locked)
396
fz_unlock(ctx, FZ_LOCK_GLYPHCACHE);
397
}
398
fz_catch(ctx)
399
{
400
if (caching)
401
fz_warn(ctx, "cannot encache glyph; continuing");
402
else
403
fz_rethrow(ctx);
404
}
405
406
return val;
407
}
408
409
fz_pixmap *
410
fz_render_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, fz_colorspace *model, const fz_irect *scissor)
411
{
412
fz_pixmap *val;
413
unsigned char qe, qf;
414
fz_matrix subpix_ctm;
415
float size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &qe, &qf);
416
417
if (size <= MAX_GLYPH_SIZE)
418
{
419
scissor = &fz_infinite_irect;
420
}
421
else
422
{
423
if (font->ft_face)
424
return NULL;
425
}
426
427
fz_try(ctx)
428
{
429
if (font->ft_face)
430
{
431
val = fz_render_ft_glyph_pixmap(ctx, font, gid, &subpix_ctm, fz_aa_level(ctx));
432
}
433
else if (font->t3procs)
434
{
435
val = fz_render_t3_glyph_pixmap(ctx, font, gid, &subpix_ctm, model, scissor);
436
}
437
else
438
{
439
fz_warn(ctx, "assert: uninitialized font structure");
440
val = NULL;
441
}
442
}
443
fz_catch(ctx)
444
{
445
fz_rethrow(ctx);
446
}
447
448
return val;
449
}
450
451
void
452
fz_dump_glyph_cache_stats(fz_context *ctx)
453
{
454
fz_glyph_cache *cache = ctx->glyph_cache;
455
456
printf("Glyph Cache Size: %d\n", cache->total);
457
#ifndef NDEBUG
458
printf("Glyph Cache Evictions: %d (%d bytes)\n", cache->num_evictions, cache->evicted);
459
#endif
460
}
461
462