Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxccache.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Fast case character cache routines for Ghostscript library */
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gpcheck.h"
21
#include "gserrors.h"
22
#include "gsstruct.h"
23
#include "gscencs.h"
24
#include "gxfixed.h"
25
#include "gxmatrix.h"
26
#include "gzstate.h"
27
#include "gzpath.h"
28
#include "gxdevice.h"
29
#include "gxdevmem.h"
30
#include "gzcpath.h"
31
#include "gxchar.h"
32
#include "gxfont.h"
33
#include "gxfcache.h"
34
#include "gxxfont.h"
35
#include "gximask.h"
36
#include "gscspace.h"   /* for gsimage.h */
37
#include "gsimage.h"
38
#include "gxhttile.h"
39
#include "gsptype1.h"       /* for gx_dc_is_pattern1_color_with_trans */
40
41
/* Forward references */
42
static byte *compress_alpha_bits(const cached_char *, gs_memory_t *);
43
44
/* Define a scale factor of 1. */
45
static const gs_log2_scale_point scale_log2_1 =
46
{0, 0};
47
48
void
49
gx_compute_char_matrix(const gs_matrix *char_tm, const gs_log2_scale_point *log2_scale,
50
    float *mxx, float *mxy, float *myx, float *myy)
51
26.5M
{
52
26.5M
    int scale_x = 1 << log2_scale->x;
53
26.5M
    int scale_y = 1 << log2_scale->y;
54
55
26.5M
    *mxx = char_tm->xx * scale_x;
56
26.5M
    *mxy = char_tm->xy * scale_x;
57
26.5M
    *myx = char_tm->yx * scale_y;
58
26.5M
    *myy = char_tm->yy * scale_y;
59
26.5M
}
60
61
void
62
gx_compute_ccache_key(gs_font * pfont, const gs_matrix *char_tm,
63
    const gs_log2_scale_point *log2_scale, bool design_grid,
64
    float *mxx, float *mxy, float *myx, float *myy)
65
26.6M
{
66
26.6M
    if (design_grid &&
67
26.6M
            (pfont->FontType == ft_TrueType || pfont->FontType == ft_CID_TrueType)) {
68
        /*
69
         * We need a special face for this case, because the TT interpreter
70
         * can't generate both grid_fitted and non-grid-fitted outlines
71
         * with a same face instance. This happens due to control
72
         * values in 'cvt' must be different.
73
         * Since a single face satisfies all font sizes,
74
         * we use a zero matrix as the cache entry key.
75
         */
76
66.0k
        *mxx = *mxy = *myx = *myy = 0;
77
66.0k
    } else
78
26.5M
        gx_compute_char_matrix(char_tm, log2_scale, mxx, mxy, myx, myy);
79
26.6M
}
80
81
/* Look up, and if necessary add, a font/matrix pair in the cache */
82
int
83
gx_lookup_fm_pair(gs_font * pfont, const gs_matrix *char_tm,
84
    const gs_log2_scale_point *log2_scale, bool design_grid, cached_fm_pair **ppair)
85
24.8M
{
86
24.8M
    float mxx, mxy, myx, myy;
87
24.8M
    gs_font *font = pfont;
88
24.8M
    register gs_font_dir *dir = font->dir;
89
24.8M
    register cached_fm_pair *pair = dir->fmcache.mdata + dir->fmcache.used;
90
24.8M
    int count = dir->fmcache.msize;
91
24.8M
    gs_uid uid;
92
93
24.8M
    gx_compute_ccache_key(pfont, char_tm, log2_scale, design_grid,
94
24.8M
                            &mxx, &mxy, &myx, &myy);
95
24.8M
    if (font->FontType == ft_composite || font->PaintType != 0) { /* We can't cache by UID alone. */
96
0
        uid_set_invalid(&uid);
97
24.8M
    } else {
98
24.8M
        uid = ((gs_font_base *) font)->UID;
99
24.8M
        if (uid_is_valid(&uid))
100
8.20M
            font = 0;
101
24.8M
    }
102
224M
    for (;count--; pair = dir->fmcache.mdata + pair->next) {
103
        /* We have either a non-zero font and an invalid UID, */
104
        /* or a zero font and a valid UID. */
105
        /* We have to break up the test */
106
        /* because of a bug in the Zortech compiler. */
107
223M
        if (font != 0) {
108
210M
            if (pair->font != font)
109
6.36M
                continue;
110
210M
        } else {
111
12.2M
            if (!uid_equal(&pair->UID, &uid) ||
112
12.2M
                pair->FontType != pfont->FontType
113
12.2M
                )
114
2.02M
                continue;
115
12.2M
        }
116
214M
        if (pair->mxx == mxx && pair->mxy == mxy &&
117
214M
            pair->myx == myx && pair->myy == myy
118
214M
            && pair->design_grid == design_grid) {
119
23.0M
            int code;
120
121
23.0M
            if (pair->font == 0) {
122
82
                pair->font = pfont;
123
82
                if_debug2m('k', pfont->memory, "[k]updating pair "PRI_INTPTR" with font "PRI_INTPTR"\n",
124
82
                           (intptr_t)pair, (intptr_t)pfont);
125
23.0M
            } else {
126
23.0M
                if_debug2m('k', pfont->memory, "[k]found pair "PRI_INTPTR": font="PRI_INTPTR"\n",
127
23.0M
                           (intptr_t)pair, (intptr_t)pair->font);
128
23.0M
            }
129
23.0M
            code = gx_touch_fm_pair(dir, pair);
130
23.0M
            if (code < 0)
131
0
                return code;
132
23.0M
            code = gx_provide_fm_pair_attributes(dir, pfont, pair,
133
23.0M
                                char_tm, log2_scale, design_grid);
134
23.0M
            if (code < 0)
135
0
                return code;
136
23.0M
            *ppair = pair;
137
23.0M
            return 0;
138
23.0M
        }
139
214M
    }
140
1.80M
    return gx_add_fm_pair(dir, pfont, &uid, char_tm, log2_scale, design_grid, ppair);
141
24.8M
}
142
143
/* Look up a glyph with the right depth in the cache. */
144
/* Return the cached_char or 0. */
145
cached_char *
146
gx_lookup_cached_char(const gs_font * pfont, const cached_fm_pair * pair,
147
                      gs_glyph glyph, int wmode, int depth,
148
                      gs_fixed_point *subpix_origin)
149
61.3M
{
150
61.3M
    gs_font_dir *dir = pfont->dir;
151
61.3M
    uint chi = chars_head_index(glyph, pair);
152
61.3M
    register cached_char *cc;
153
154
61.7M
    while ((cc = dir->ccache.table[chi & dir->ccache.table_mask]) != 0) {
155
46.3M
        if (cc->code == glyph && cc_pair(cc) == pair &&
156
46.3M
            cc->subpix_origin.x == subpix_origin->x &&
157
46.3M
            cc->subpix_origin.y == subpix_origin->y &&
158
46.3M
            cc->wmode == wmode && cc_depth(cc) == depth
159
46.3M
            ) {
160
45.9M
            if_debug4m('K', pfont->memory,
161
45.9M
                       "[K]found "PRI_INTPTR" (depth=%d) for glyph=0x%lx, wmode=%d\n",
162
45.9M
                       (intptr_t)cc, cc_depth(cc), (ulong)glyph, wmode);
163
45.9M
            return cc;
164
45.9M
        }
165
431k
        chi++;
166
431k
    }
167
61.3M
    if_debug3m('K', pfont->memory, "[K]not found: glyph=0x%lx, wmode=%d, depth=%d\n",
168
15.3M
              (ulong) glyph, wmode, depth);
169
15.3M
    return 0;
170
61.3M
}
171
172
/* Copy a cached character to the screen. */
173
/* Assume the caller has already done gx_color_load. */
174
/* Return 0 if OK, 1 if we couldn't do the operation but no error */
175
/* should be signalled, or a negative error code. */
176
int
177
gx_image_cached_char(register gs_show_enum * penum, register cached_char * cc)
178
51.9M
{
179
51.9M
    register gs_gstate *pgs = penum->pgs;
180
51.9M
    gx_device_color *pdevc = gs_currentdevicecolor_inline(pgs);
181
51.9M
    int x, y, w, h, depth;
182
51.9M
    int code;
183
51.9M
    gs_fixed_point pt;
184
51.9M
    gx_device *dev = penum->dev;
185
51.9M
    gx_device *imaging_dev = penum->imaging_dev ? penum->imaging_dev : dev;
186
51.9M
    gx_device *orig_dev = imaging_dev;
187
51.9M
    gx_device_clip cdev;
188
51.9M
    gx_xglyph xg = cc->xglyph;
189
51.9M
    gx_xfont *xf;
190
51.9M
    byte *bits;
191
192
    /* This is only to silence a Coverity warning */
193
51.9M
    cdev.target = NULL;
194
51.9M
    cdev.cpath = NULL;
195
196
51.9M
  top:code = gx_path_current_point_inline(pgs, &pt);
197
51.9M
    if (code < 0)
198
0
        return code;
199
    /*
200
     * If the character doesn't lie entirely within the inner
201
     * clipping rectangle, we set up an intermediate clipping device.
202
     * Note that if the original device implements fill_mask, we may
203
     * never actually use the clipping device.
204
     */
205
51.9M
    pt.x -= cc->offset.x + cc->subpix_origin.x;
206
51.9M
    x = fixed2int_var_rounded(pt.x) + penum->ftx;
207
51.9M
    pt.y -= cc->offset.y + cc->subpix_origin.y;
208
51.9M
    y = fixed2int_var_rounded(pt.y) + penum->fty;
209
51.9M
    w = cc->width;
210
51.9M
    h = cc->height;
211
#ifdef DEBUG
212
    if (gs_debug_c('K')) {
213
        if (cc_has_bits(cc))
214
            debug_dump_bitmap(penum->memory, cc_bits(cc), cc_raster(cc), h,
215
                              "[K]bits");
216
        else
217
            dmputs(penum->memory, "[K]no bits\n");
218
        dmlprintf3(penum->memory, "[K]copying "PRI_INTPTR", offset=(%g,%g)\n",
219
                   (intptr_t) cc,
220
                   fixed2float(-cc->offset.x),
221
                   fixed2float(-cc->offset.y));
222
        dmlprintf6(penum->memory, "   at (%g,%g)+(%d,%d)->(%d,%d)\n",
223
                   fixed2float(pt.x), fixed2float(pt.y),
224
                   penum->ftx, penum->fty, x, y);
225
    }
226
#endif
227
51.9M
    if ((x < penum->ibox.p.x || x + w > penum->ibox.q.x ||
228
51.9M
         y < penum->ibox.p.y || y + h > penum->ibox.q.y) &&
229
51.9M
        imaging_dev != (gx_device *) & cdev  /* might be 2nd time around */
230
51.9M
        ) {     /* Check for the character falling entirely outside */
231
        /* the clipping region. */
232
6.32M
        gx_clip_path *pcpath;
233
234
6.32M
        if (x >= penum->obox.q.x || x + w <= penum->obox.p.x ||
235
6.32M
            y >= penum->obox.q.y || y + h <= penum->obox.p.y
236
6.32M
            )
237
5.83M
            return 0;    /* nothing to do */
238
482k
        code = gx_effective_clip_path(pgs, &pcpath);
239
482k
        if (code < 0)
240
0
            return code;
241
482k
        gx_make_clip_device_on_stack(&cdev, pcpath, imaging_dev);
242
482k
        imaging_dev = (gx_device *) & cdev;
243
482k
        if_debug0m('K', penum->memory, "[K](clipping)\n");
244
482k
    }
245
46.1M
    code = gx_set_dev_color(pgs);
246
46.1M
    if (code != 0) {
247
0
        if (imaging_dev == (gx_device *) & cdev)
248
0
            gx_destroy_clip_device_on_stack(&cdev);
249
0
        return code;
250
0
    }
251
    /* If an xfont can render this character, use it. */
252
46.1M
    if (xg != gx_no_xglyph && (xf = cc_pair(cc)->xfont) != 0) {
253
0
        int cx = x + fixed2int(cc->offset.x);
254
0
        int cy = y + fixed2int(cc->offset.y);
255
256
        /*
257
         * Note that we prefer a 1-bit xfont implementation over
258
         * a multi-bit cached bitmap.  Eventually we should change
259
         * the xfont interface so it can deliver multi-bit bitmaps,
260
         * or else implement oversampling for xfonts.
261
         */
262
0
        if (gs_color_writes_pure(pgs)) {
263
0
            code = (*xf->common.procs->render_char) (xf, xg,
264
0
                                        imaging_dev, cx, cy,
265
0
                                        pdevc->colors.pure, 0);
266
0
            if_debug8m('K', penum->memory,
267
0
                       "[K]render_char display: xfont="PRI_INTPTR", glyph=0x%lx\n\tdev="PRI_INTPTR"(%s) x,y=%d,%d, color=0x%lx => %d\n",
268
0
                       (intptr_t)xf, (ulong)xg, (intptr_t)imaging_dev,
269
0
                       imaging_dev->dname, cx, cy,
270
0
                       (ulong) pdevc->colors.pure, code);
271
0
            if (code == 0) {
272
0
                if (imaging_dev == (gx_device *) & cdev)
273
0
                    gx_destroy_clip_device_on_stack(&cdev);
274
0
                return_check_interrupt(penum->memory, 0);
275
0
            }
276
0
        }
277
        /* Can't render directly.  If we don't have a bitmap yet, */
278
        /* get it from the xfont now. */
279
0
        if (!cc_has_bits(cc)) {
280
0
            gx_device_memory mdev;
281
282
0
            gs_make_mem_mono_device(&mdev, dev->memory, imaging_dev);
283
0
            gx_open_cache_device(&mdev, cc);
284
0
            code = (*xf->common.procs->render_char) (xf, xg,
285
0
                                       (gx_device *) & mdev, cx - x, cy - y,
286
0
                                                     (gx_color_index) 1, 1);
287
0
            if_debug7m('K', penum->memory,
288
0
                       "[K]render_char to bits: xfont="PRI_INTPTR", glyph=0x%lx\n\tdev="PRI_INTPTR"(%s) x,y=%d,%d => %d\n",
289
0
                      (intptr_t)xf, (ulong) xg, (intptr_t)&mdev,
290
0
                      mdev.dname, cx - x, cy - y, code);
291
0
            if (code != 0) {
292
0
                if (imaging_dev == (gx_device *) & cdev)
293
0
                    gx_destroy_clip_device_on_stack(&cdev);
294
0
                return_check_interrupt(penum->memory, 1);
295
0
            }
296
0
            gx_add_char_bits(cc_pair(cc)->font->dir,
297
0
                             cc, &scale_log2_1);
298
            /* gx_add_char_bits may change width, height, */
299
            /* raster, and/or offset.  It's easiest to */
300
            /* start over from the top.  Clear xg so that */
301
            /* we don't waste time trying render_char again. */
302
0
            xg = gx_no_xglyph;
303
0
            goto top;
304
0
        }
305
0
    }
306
    /*
307
     * No xfont.  Render from the cached bits.  If the cached bits
308
     * have more than 1 bit of alpha, and the color isn't pure or
309
     * the copy_alpha operation fails, construct a single-bit mask
310
     * by taking the high-order alpha bit.
311
     */
312
46.1M
    bits = cc_bits(cc);
313
    /* With 4x2 scale, depth == 3.
314
     * An example is -dTextAlphaBits=4 comparefiles/fonttest.pdf .
315
     * We need to map 4 bitmap bits to 2 alpha bits.
316
     */
317
46.1M
    depth = (cc_depth(cc) == 3 ? 2 : cc_depth(cc));
318
46.1M
    if ((dev_proc(orig_dev, fill_mask) != gx_default_fill_mask ||
319
46.1M
        !lop_no_S_is_T(pgs->log_op))) {
320
321
44.9M
        gx_clip_path *pcpath;
322
323
44.9M
        penum->use_wxy_float = false;
324
44.9M
        penum->wxy_float.x = penum->wxy_float.y = 0.0;
325
44.9M
        penum->wxy = cc->wxy;
326
327
44.9M
        code = gx_effective_clip_path(pgs, &pcpath);
328
44.9M
        if (code >= 0) {
329
44.9M
            code = gx_image_fill_masked
330
44.9M
                (orig_dev, bits, 0, cc_raster(cc), cc->id,
331
44.9M
                 x, y, w, h, pdevc, depth, pgs->log_op, pcpath);
332
44.9M
            if (code >= 0)
333
44.9M
                goto done;
334
44.9M
        }
335
44.9M
    } else if (gs_color_writes_pure(pgs)) {
336
1.20M
        gx_color_index color = pdevc->colors.pure;
337
338
1.20M
        if (depth > 1) {
339
0
            code = (*dev_proc(imaging_dev, copy_alpha))
340
0
                (imaging_dev, bits, 0, cc_raster(cc), cc->id,
341
0
                 x, y, w, h, color, depth);
342
0
            if (code >= 0)
343
0
                return_check_interrupt(penum->memory, 0);
344
            /* copy_alpha failed, construct a monobit mask. */
345
0
            bits = compress_alpha_bits(cc, penum->memory->non_gc_memory);
346
0
            if (bits == 0) {
347
0
                if (imaging_dev == (gx_device *) & cdev)
348
0
                    gx_destroy_clip_device_on_stack(&cdev);
349
0
                return 1; /* VMerror, but recoverable */
350
0
            }
351
0
        }
352
1.20M
        code = (*dev_proc(imaging_dev, copy_mono))
353
1.20M
            (imaging_dev, bits, 0, bitmap_raster(w), gs_no_id,
354
1.20M
             x, y, w, h, gx_no_color_index, color);
355
1.20M
        goto done;
356
1.20M
    }
357
1.23k
    if (depth > 1) {   /* Complex color or fill_mask / copy_alpha failed, */
358
        /* construct a monobit mask. */
359
0
        bits = compress_alpha_bits(cc, penum->memory->non_gc_memory);
360
0
        if (bits == 0) {
361
0
            if (imaging_dev == (gx_device *) & cdev)
362
0
                gx_destroy_clip_device_on_stack(&cdev);
363
0
            return 1;   /* VMerror, but recoverable */
364
0
        }
365
1.23k
    } {       /* Use imagemask to render the character. */
366
1.23k
        gs_memory_t *mem = penum->memory->non_gc_memory;
367
1.23k
        gs_image_enum *pie =
368
1.23k
            gs_image_enum_alloc(mem, "image_char(image_enum)");
369
1.23k
        gs_image_t image;
370
1.23k
        int iy;
371
1.23k
        uint used, raster = (bits == cc_bits(cc) ? cc_raster(cc)
372
1.23k
                             : bitmap_raster(cc->width) );
373
1.23k
        int code1;
374
375
1.23k
        if (pie == 0) {
376
0
            if (bits != cc_bits(cc))
377
0
                gs_free_object(mem, bits,
378
0
                               "compress_alpha_bits");
379
0
            if (imaging_dev == (gx_device *) & cdev)
380
0
                gx_destroy_clip_device_on_stack(&cdev);
381
0
            return 1;   /* VMerror, but recoverable */
382
0
        }
383
        /* Make a matrix that will place the image */
384
        /* at (x,y) with no transformation. */
385
1.23k
        gs_image_t_init_mask(&image, true);
386
1.23k
        gs_make_translation((double) - x, (double) - y, &image.ImageMatrix);
387
1.23k
        gs_matrix_multiply(&ctm_only(pgs), &image.ImageMatrix, &image.ImageMatrix);
388
1.23k
        image.Width = w;
389
1.23k
        image.Height = h;
390
1.23k
        image.adjust = false;
391
1.23k
        code = gs_image_init(pie, &image, false, true, pgs);
392
1.23k
        switch (code) {
393
127
            case 1:   /* empty image */
394
127
                code = 0;
395
127
            default:
396
127
                break;
397
1.10k
            case 0:
398
62.9k
                for (iy = 0; iy < h && code >= 0; iy++)
399
61.8k
                    code = gs_image_next(pie, bits + iy * raster,
400
61.8k
                                         (w + 7) >> 3, &used);
401
1.23k
        }
402
1.23k
        code1 = gs_image_cleanup_and_free_enum(pie, pgs);
403
1.23k
        if (code >= 0 && code1 < 0)
404
12
            code = code1;
405
1.23k
    }
406
46.1M
  done:if (bits != cc_bits(cc))
407
0
        gs_free_object(penum->memory->non_gc_memory, bits, "compress_alpha_bits");
408
46.1M
    if (code > 0)
409
1.09k
        code = 0;
410
46.1M
    if (imaging_dev == (gx_device *) & cdev)
411
482k
        gx_destroy_clip_device_on_stack(&cdev);
412
46.1M
    return_check_interrupt(penum->memory, code);
413
1.23k
}
414
415
/* ------ Image manipulation ------ */
416
417
/*
418
 * Compress a mask with 2 or 4 bits of alpha to a monobit mask.
419
 * Allocate and return the address of the monobit mask.
420
 */
421
static byte *
422
compress_alpha_bits(const cached_char * cc, gs_memory_t * mem)
423
0
{
424
0
    const byte *data = cc_const_bits(cc);
425
0
    uint width = cc->width;
426
0
    uint height = cc->height;
427
    /* With 4x2 scale, depth == 3.
428
     * An example is -dTextAlphaBits=4 comparefiles/fonttest.pdf .
429
     * We need to map 4 bitmap bits to 2 alpha bits.
430
     */
431
0
    int depth = (cc_depth(cc) == 3 ? 2 : cc_depth(cc));
432
0
    uint sraster = cc_raster(cc);
433
0
    uint sskip = sraster - ((width * depth + 7) >> 3);
434
0
    uint draster = bitmap_raster(width);
435
0
    uint dskip = draster - ((width + 7) >> 3);
436
0
    byte *mask = gs_alloc_bytes(mem, (size_t)draster * height,
437
0
                                "compress_alpha_bits");
438
0
    const byte *sptr = data;
439
0
    byte *dptr = mask;
440
0
    uint h;
441
442
0
    if (mask == 0)
443
0
        return 0;
444
0
    for (h = height; h; --h) {
445
0
        byte sbit = 0x80;
446
0
        byte d = 0;
447
0
        byte dbit = 0x80;
448
0
        uint w;
449
450
0
        for (w = width; w; --w) {
451
0
            if (*sptr & sbit)
452
0
                d += dbit;
453
0
            if (!(sbit >>= depth))
454
0
                sbit = 0x80, sptr++;
455
0
            if (!(dbit >>= 1)) {
456
0
                *dptr++ = d;
457
0
                dbit = 0x80, d = 0;
458
0
            }
459
0
        }
460
0
        if (dbit != 0x80)
461
0
            *dptr++ = d;
462
0
        for (w = dskip; w != 0; --w)
463
0
            *dptr++ = 0;
464
0
        if (sbit != 0x80)
465
0
            ++sptr;
466
0
        sptr += sskip;
467
0
    }
468
0
    return mask;
469
0
}