Coverage Report

Created: 2024-05-20 06:23

/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
}