Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gdevabuf.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* Alpha-buffering memory devices */
17
#include "memory_.h"
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gxdevice.h"
21
#include "gxdevmem.h"   /* semi-public definitions */
22
#include "gdevmem.h"    /* private definitions */
23
#include "gzstate.h"
24
#include "gxdevcli.h"
25
#include "gxdevsop.h"
26
27
/* ================ Alpha devices ================ */
28
29
/*
30
 * These devices store 2 or 4 bits of alpha.  They are a hybrid of a
31
 * monobit device (for color mapping) and a 2- or 4-bit device (for painting).
32
 * Currently, we only use them for character rasterizing, but they might be
33
 * useful for other things someday.
34
 */
35
36
/* ================ Alpha-buffer device ================ */
37
38
/*
39
 * This device converts graphics sampled at a higher resolution to
40
 * alpha values at a lower resolution.  It does this by accumulating
41
 * the bits of a band and then converting the band to alphas.
42
 * In order to make this work, the client of the device must promise
43
 * only to visit each band at most once, except possibly for a single
44
 * scan line overlapping the adjacent band, and must promise only to write
45
 * a single color into the output.  In particular, this works
46
 * within a single call on gx_fill_path (if the fill loop is constrained
47
 * to process bands of limited height on each pass) or a single masked image
48
 * scanned in Y order, but not across such calls and not for other
49
 * kinds of painting operations.
50
 *
51
 * We implement this device as a subclass of a monobit memory device.
52
 * (We put its state in the definition of gx_device_memory just because
53
 * actual subclassing introduces a lot of needless boilerplate.)
54
 * We only allocate enough bits for one band.  The height of the band
55
 * must be a multiple of the Y scale factor; the minimum height
56
 * of the band is twice the Y scale factor.
57
 *
58
 * The bits in storage are actually a sliding window on the true
59
 * oversampled image.  To avoid having to copy the bits around when we
60
 * move the window, we adjust the mapping between the client's Y values
61
 * and our own, as follows:
62
 *      Client          Stored
63
 *      ------          ------
64
 *      y0..y0+m-1      n-m..n-1
65
 *      y0+m..y0+n-1    0..n-m-1
66
 * where n and m are multiples of the Y scale factor and 0 <= m <= n <=
67
 * the height of the band.  (In the device structure, m is called
68
 * mapped_start and n is called mapped_height.)  This allows us to slide
69
 * the window incrementally in either direction without copying any bits.
70
 */
71
72
/* Procedures */
73
static dev_proc_close_device(mem_abuf_close);
74
static dev_proc_copy_mono(mem_abuf_copy_mono);
75
static dev_proc_fill_rectangle(mem_abuf_fill_rectangle);
76
static dev_proc_get_clipping_box(mem_abuf_get_clipping_box);
77
static dev_proc_fill_rectangle_hl_color(mem_abuf_fill_rectangle_hl_color);
78
static dev_proc_fill_stroke_path(mem_abuf_fill_stroke_path);
79
80
/* The device descriptor. */
81
static void
82
mem_alpha_initialize_device_procs(gx_device *dev)
83
0
{
84
0
    mem_initialize_device_procs(dev);
85
86
0
    set_dev_proc(dev, map_rgb_color, gx_forward_map_rgb_color);
87
0
    set_dev_proc(dev, map_color_rgb, gx_forward_map_color_rgb);
88
0
    set_dev_proc(dev, fill_rectangle, mem_abuf_fill_rectangle);
89
0
    set_dev_proc(dev, copy_mono, mem_abuf_copy_mono);
90
0
    set_dev_proc(dev, copy_color, gx_default_copy_color);
91
0
    set_dev_proc(dev, strip_copy_rop2, gx_no_strip_copy_rop2);
92
0
    set_dev_proc(dev, fill_rectangle_hl_color, mem_abuf_fill_rectangle_hl_color);
93
0
    set_dev_proc(dev, fill_stroke_path, mem_abuf_fill_stroke_path);
94
0
}
95
96
static const gx_device_memory mem_alpha_buffer_device =
97
   mem_device("image(alpha buffer)", 0, 1, mem_alpha_initialize_device_procs);
98
99
/* Make an alpha-buffer memory device. */
100
/* We use abuf instead of alpha_buffer because */
101
/* gcc under VMS only retains 23 characters of procedure names. */
102
void
103
gs_make_mem_abuf_device(gx_device_memory * adev, gs_memory_t * mem,
104
                     gx_device * target, const gs_log2_scale_point * pscale,
105
                        int alpha_bits, int mapped_x, bool devn)
106
0
{
107
0
    gs_make_mem_device(adev, &mem_alpha_buffer_device, mem, 0, target);
108
0
    adev->max_fill_band = 1 << pscale->y;
109
0
    adev->log2_scale = *pscale;
110
0
    adev->log2_alpha_bits = alpha_bits >> 1;  /* works for 1,2,4 */
111
0
    adev->mapped_x = mapped_x;
112
0
    set_dev_proc(adev, close_device, mem_abuf_close);
113
0
    set_dev_proc(adev, get_clipping_box, mem_abuf_get_clipping_box);
114
0
    if (!devn)
115
0
        adev->save_hl_color = NULL; /* This is the test for when we flush the
116
                                       the buffer as to what copy_alpha type
117
                                       use */
118
0
    adev->color_info.anti_alias.text_bits =
119
0
      adev->color_info.anti_alias.graphics_bits =
120
0
        alpha_bits;
121
0
    adev->graphics_type_tag = target->graphics_type_tag;
122
0
}
123
124
/* Test whether a device is an alpha-buffering device. */
125
bool
126
gs_device_is_abuf(const gx_device * dev)
127
20.0M
{       /* We can't just compare the procs, or even an individual proc, */
128
    /* because we might be tracing.  Instead, check the identity of */
129
    /* the device name. */
130
20.0M
    return dev->dname == mem_alpha_buffer_device.dname;
131
20.0M
}
132
133
/* Internal routine to flush a block of the buffer. */
134
/* A block is a group of scan lines whose initial Y is a multiple */
135
/* of the Y scale and whose height is equal to the Y scale. */
136
static int
137
abuf_flush_block(gx_device_memory * adev, int y)
138
0
{
139
0
    gx_device *target = adev->target;
140
0
    int block_height = 1 << adev->log2_scale.y;
141
0
    int alpha_bits = 1 << adev->log2_alpha_bits;
142
0
    int ddepth =
143
0
    (adev->width >> adev->log2_scale.x) << adev->log2_alpha_bits;
144
0
    uint draster = bitmap_raster(ddepth);
145
0
    int buffer_y = y - adev->mapped_y + adev->mapped_start;
146
0
    byte *bits;
147
148
0
    if (buffer_y >= adev->height)
149
0
        buffer_y -= adev->height;
150
0
    bits = scan_line_base(adev, buffer_y);
151
0
    {/*
152
      * Many bits are typically zero.  Save time by computing
153
      * an accurate X bounding box before compressing.
154
      * Unfortunately, in order to deal with alpha nibble swapping
155
      * (see gsbitops.c), we can't expand the box only to pixel
156
      * boundaries:
157
          int alpha_mask = -1 << adev->log2_alpha_bits;
158
      * Instead, we must expand it to byte boundaries,
159
      */
160
0
        int alpha_mask = ~7;
161
0
        gs_int_rect bbox;
162
0
        int width;
163
164
0
        bits_bounding_box(bits, block_height, adev->raster, &bbox);
165
0
        bbox.p.x &= alpha_mask;
166
0
        bbox.q.x = (bbox.q.x + ~alpha_mask) & alpha_mask;
167
0
        width = bbox.q.x - bbox.p.x;
168
0
        bits_compress_scaled(bits, bbox.p.x, width, block_height,
169
0
                             adev->raster, bits, draster, &adev->log2_scale,
170
0
                             adev->log2_alpha_bits);
171
        /* Set up with NULL when adev initialized */
172
0
        if (adev->save_hl_color == NULL) {
173
0
            return (*dev_proc(target, copy_alpha)) (target,
174
0
                                              bits, 0, draster, gx_no_bitmap_id,
175
0
                                                  (adev->mapped_x + bbox.p.x) >>
176
0
                                                    adev->log2_scale.x,
177
0
                                                    y >> adev->log2_scale.y,
178
0
                                                 width >> adev->log2_scale.x, 1,
179
0
                                                  adev->save_color, alpha_bits);
180
0
        } else {
181
0
            return (*dev_proc(target, copy_alpha_hl_color)) (target,
182
0
                                              bits, 0, draster, gx_no_bitmap_id,
183
0
                                                  (adev->mapped_x + bbox.p.x) >>
184
0
                                                    adev->log2_scale.x,
185
0
                                                    y >> adev->log2_scale.y,
186
0
                                                 width >> adev->log2_scale.x, 1,
187
0
                                                  adev->save_hl_color, alpha_bits);
188
0
        }
189
0
    }
190
0
}
191
/* Flush the entire buffer. */
192
static int
193
abuf_flush(gx_device_memory * adev)
194
0
{
195
0
    int y, code = 0;
196
0
    int block_height = 1 << adev->log2_scale.y;
197
198
0
    for (y = 0; y < adev->mapped_height; y += block_height)
199
0
        if ((code = abuf_flush_block(adev, adev->mapped_y + y)) < 0)
200
0
            return code;
201
0
    adev->mapped_height = adev->mapped_start = 0;
202
0
    return 0;
203
0
}
204
205
/* Close the device, flushing the buffer. */
206
static int
207
mem_abuf_close(gx_device * dev)
208
0
{
209
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
210
0
    int code = abuf_flush(mdev);
211
212
0
    if (code < 0)
213
0
        return code;
214
0
    return mem_close(dev);
215
0
}
216
217
/*
218
 * Framework for mapping a requested imaging operation to the buffer.
219
 * For now, we assume top-to-bottom transfers and use a very simple algorithm.
220
 */
221
typedef struct y_transfer_s {
222
    int y_next;
223
    int height_left;
224
    int transfer_y;
225
    int transfer_height;
226
} y_transfer;
227
static int
228
y_transfer_init(y_transfer * pyt, gx_device * dev, int ty, int th)
229
0
{
230
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
231
0
    int bh = 1 << mdev->log2_scale.y;
232
233
0
    if (ty < mdev->mapped_y || ty > mdev->mapped_y + mdev->mapped_height) {
234
0
        int code = abuf_flush(mdev);
235
0
        if (code < 0)
236
0
            return code;
237
0
        mdev->mapped_y = ty & -bh;
238
0
        mdev->mapped_height = bh;
239
0
        memset(scan_line_base(mdev, 0), 0, (size_t)bh * mdev->raster);
240
0
    }
241
0
    pyt->y_next = ty;
242
0
    pyt->height_left = th;
243
0
    pyt->transfer_height = 0;
244
245
0
    return 0;
246
0
}
247
/* while ( yt.height_left > 0 ) { y_transfer_next(&yt, mdev); ... } */
248
static int
249
y_transfer_next(y_transfer * pyt, gx_device * dev)
250
0
{
251
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
252
0
    int my = mdev->mapped_y, mh = mdev->mapped_height;
253
0
    int ms = mdev->mapped_start;
254
0
    int ty = pyt->y_next += pyt->transfer_height;
255
0
    int th = pyt->height_left;
256
0
    int bh = 1 << mdev->log2_scale.y;
257
258
    /* From here on, we know that my <= ty <= my + mh. */
259
0
    int tby, tbh;
260
261
0
    if (ty == my + mh) { /* Add a new block at my1. */
262
0
        if (mh == mdev->height) {
263
0
            int code = abuf_flush_block(mdev, my);
264
265
0
            if (code < 0)
266
0
                return code;
267
0
            mdev->mapped_y = my += bh;
268
0
            if ((mdev->mapped_start = ms += bh) == mh)
269
0
                mdev->mapped_start = ms = 0;
270
0
        } else {   /* Because we currently never extend backwards, */
271
            /* we know we can't wrap around in this case. */
272
0
            mdev->mapped_height = mh += bh;
273
0
        }
274
0
        memset(scan_line_base(mdev, (ms == 0 ? mh : ms) - bh),
275
0
               0, (size_t)bh * mdev->raster);
276
0
    }
277
    /* Now we know that my <= ty < my + mh. */
278
0
    tby = ty - my + ms;
279
0
    if (tby < mdev->height) {
280
0
        tbh = mdev->height - ms;
281
0
        if (tbh > mh)
282
0
            tbh = mh;
283
0
        tbh -= tby - ms;
284
0
    } else {     /* wrap around */
285
0
        tby -= mdev->height;
286
0
        tbh = ms + mh - dev->height - tby;
287
0
    }
288
0
    if_debug7m('V', mdev->memory,
289
0
               "[V]abuf: my=%d, mh=%d, ms=%d, ty=%d, th=%d, tby=%d, tbh=%d\n",
290
0
               my, mh, ms, ty, th, tby, tbh);
291
0
    if (tbh > th)
292
0
        tbh = th;
293
0
    pyt->height_left = th - tbh;
294
0
    pyt->transfer_y = tby;
295
0
    pyt->transfer_height = tbh;
296
0
    return 0;
297
0
}
298
299
/* Copy a monobit image. */
300
static int
301
mem_abuf_copy_mono(gx_device * dev,
302
               const byte * base, int sourcex, int sraster, gx_bitmap_id id,
303
        int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
304
0
{
305
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
306
0
    y_transfer yt;
307
0
    int code;
308
309
0
    if (zero != gx_no_color_index || one == gx_no_color_index)
310
0
        return_error(gs_error_undefinedresult);
311
0
    x -= mdev->mapped_x;
312
0
    fit_copy_xyw(dev, base, sourcex, sraster, id, x, y, w, h);  /* don't limit h */
313
0
    if (w <= 0 || h <= 0)
314
0
        return 0;
315
0
    if (mdev->mapped_height != 0 && mdev->save_color != one) {
316
        /* Color has changed. Better flush. */
317
0
        int code = abuf_flush(mdev);
318
0
        if (code < 0)
319
0
            return code;
320
0
    }
321
0
    mdev->save_color = one;
322
0
    code = y_transfer_init(&yt, dev, y, h);
323
0
    if (code < 0)
324
0
        return code;
325
0
    while (yt.height_left > 0) {
326
0
        code = y_transfer_next(&yt, dev);
327
0
        if (code < 0)
328
0
            return code;
329
0
        code = mem_mono_copy_mono(dev,
330
0
                                  base + (yt.y_next - y) * sraster,
331
0
                                  sourcex, sraster, gx_no_bitmap_id,
332
0
                                  x, yt.transfer_y, w, yt.transfer_height,
333
0
                                  gx_no_color_index, (gx_color_index) 1);
334
0
        if (code < 0)
335
0
            return code;
336
0
    }
337
0
    return 0;
338
0
}
339
340
/* Fill a rectangle. */
341
static int
342
mem_abuf_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
343
                        gx_color_index color)
344
0
{
345
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
346
0
    y_transfer yt;
347
0
    int code;
348
349
0
    x -= mdev->mapped_x;
350
0
    fit_fill_xy(dev, x, y, w, h);
351
0
    fit_fill_w(dev, x, w);  /* don't limit h */
352
    /* or check w <= 0, h <= 0 */
353
0
    if (mdev->mapped_height != 0 && mdev->save_color != color) {
354
        /* Color has changed. Better flush. */
355
0
        int code = abuf_flush(mdev);
356
0
        if (code < 0)
357
0
            return code;
358
0
    }
359
0
    mdev->save_color = color;
360
0
    code = y_transfer_init(&yt, dev, y, h);
361
0
    if (code < 0)
362
0
        return code;
363
0
    while (yt.height_left > 0) {
364
0
        code = y_transfer_next(&yt, dev);
365
0
        if (code < 0)
366
0
            return code;
367
0
        code = mem_mono_fill_rectangle(dev, x, yt.transfer_y,
368
0
                                       w, yt.transfer_height,
369
0
                                       (gx_color_index) 1);
370
0
        if (code < 0)
371
0
            return code;
372
0
    }
373
0
    return 0;
374
0
}
375
376
/* Fill a rectangle. */
377
static int
378
mem_abuf_fill_rectangle_hl_color(gx_device * dev, const gs_fixed_rect *rect,
379
                                 const gs_gstate *pgs,
380
                                 const gx_drawing_color *pdcolor,
381
                                 const gx_clip_path *pcpath)
382
0
{
383
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
384
0
    y_transfer yt;
385
0
    int x = fixed2int(rect->p.x);
386
0
    int y = fixed2int(rect->p.y);
387
0
    int w = fixed2int(rect->q.x) - x;
388
0
    int h = fixed2int(rect->q.y) - y;
389
0
    int code;
390
0
    (void)pgs;
391
392
0
    x -= mdev->mapped_x;
393
0
    fit_fill_xy(dev, x, y, w, h);
394
0
    fit_fill_w(dev, x, w);  /* don't limit h */
395
    /* or check w <= 0, h <= 0 */
396
0
    if (mdev->mapped_height != 0 &&
397
0
        memcmp(mdev->save_hl_color, pdcolor, sizeof(*pdcolor)) != 0) {
398
        /* Color has changed. Better flush. */
399
0
        int code = abuf_flush(mdev);
400
0
        if (code < 0)
401
0
            return code;
402
0
    }
403
0
    mdev->save_hl_color = pdcolor;
404
0
    code = y_transfer_init(&yt, dev, y, h);
405
0
    if (code < 0)
406
0
        return code;
407
0
    while (yt.height_left > 0) {
408
0
        code = y_transfer_next(&yt, dev);
409
0
        if (code < 0)
410
0
            return code;
411
0
        code = mem_mono_fill_rectangle(dev, x, yt.transfer_y,
412
0
                                       w, yt.transfer_height,
413
0
                                       (gx_color_index) 1);
414
0
        if (code < 0)
415
0
            return code;
416
0
    }
417
0
    return 0;
418
0
}
419
420
/*
421
 * Fill/Stroke a path.  This is the default implementation of the driver
422
 * fill_path procedure.
423
 */
424
int
425
mem_abuf_fill_stroke_path(gx_device * pdev, const gs_gstate * pgs,
426
                          gx_path * ppath,
427
                          const gx_fill_params * params_fill,
428
                          const gx_device_color * pdevc_fill,
429
                          const gx_stroke_params * params_stroke,
430
                          const gx_device_color * pdevc_stroke,
431
                          const gx_clip_path * pcpath)
432
0
{
433
0
    int code = 0;
434
0
    int code1;
435
0
    int has_comp = 1;
436
0
    overprint_abuf_state_t param;
437
0
    gx_device_memory* const mdev = (gx_device_memory*)pdev;
438
439
0
    param.op_trans = OP_FS_TRANS_PREFILL;
440
0
    param.pgs = pgs;
441
0
    param.pcpath = pcpath;
442
0
    param.ppath = ppath;
443
0
    param.alpha_buf_path_scale = mdev->log2_scale;
444
445
    /* Tell any overprint compositor (maybe a pdf14 device) that's listening to get ready for a fill/stroke. */
446
0
    code = dev_proc(pdev, dev_spec_op)(pdev, gxdso_abuf_optrans, &param, sizeof(param));
447
0
    if (code == gs_error_undefined)
448
0
        has_comp = false; /* No compositor listening. */
449
0
    else if (code < 0)
450
0
        return code; /* Any other error is real. */
451
452
    /* Do the fill. */
453
0
    code = dev_proc(pdev, fill_path)(pdev, pgs, ppath, params_fill, pdevc_fill, pcpath);
454
0
    if (code < 0) {
455
        /* If the fill failed do any tidy up necessary. */
456
0
        if (has_comp) {
457
0
            param.op_trans = OP_FS_TRANS_CLEANUP;
458
0
            code1 = dev_proc(pdev, dev_spec_op)(pdev, gxdso_abuf_optrans, &param, sizeof(param));
459
0
            if (code1 < 0)
460
0
                code = code1; /* If the pdf14 cleanup failed that is (more) fatal! */
461
0
        }
462
0
        return code;
463
0
    }
464
0
    abuf_flush(mdev);
465
466
    /* Handle stroke */
467
0
    gs_swapcolors_quick(pgs);
468
0
    if (has_comp) {
469
0
        param.op_trans = OP_FS_TRANS_PRESTROKE;
470
0
        code = dev_proc(pdev, dev_spec_op)(pdev, gxdso_abuf_optrans, &param, sizeof(param));
471
0
        if (code < 0)
472
0
        {
473
0
            gs_swapcolors_quick(pgs);
474
0
            return code;
475
0
        }
476
0
    }
477
0
    code = dev_proc(pdev, stroke_path)(pdev, pgs, ppath, params_stroke, pdevc_stroke, pcpath);
478
0
    abuf_flush(mdev);
479
0
    gs_swapcolors_quick(pgs);
480
481
    /* Tell the compositors we're done. */
482
0
    if (has_comp) {
483
0
        param.op_trans = OP_FS_TRANS_POSTSTROKE;
484
0
        code1 = dev_proc(pdev, dev_spec_op)(pdev, gxdso_abuf_optrans, &param, sizeof(param));
485
0
        if (code >= 0)
486
0
            code = code1;
487
0
    }
488
0
    return code;
489
0
}
490
491
/* Get the clipping box.  We must scale this up by the number of alpha bits. */
492
static void
493
mem_abuf_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
494
0
{
495
0
    gx_device_memory * const mdev = (gx_device_memory *)dev;
496
0
    gx_device *tdev = mdev->target;
497
498
0
    (*dev_proc(tdev, get_clipping_box)) (tdev, pbox);
499
0
    pbox->p.x <<= mdev->log2_scale.x;
500
0
    pbox->p.y <<= mdev->log2_scale.y;
501
0
    pbox->q.x <<= mdev->log2_scale.x;
502
0
    pbox->q.y <<= mdev->log2_scale.y;
503
0
}
504
505
/*
506
 * Determine the number of bits of alpha buffer for a stroke or fill.
507
 * We should do alpha buffering iff this value is >1.
508
 */
509
int
510
alpha_buffer_bits(gs_gstate * pgs)
511
13.9M
{
512
13.9M
    gx_device *dev;
513
514
13.9M
    dev = gs_currentdevice_inline(pgs);
515
13.9M
    if (gs_device_is_abuf(dev)) {
516
        /* We're already writing into an alpha buffer. */
517
0
        return 0;
518
0
    }
519
13.9M
    return (*dev_proc(dev, get_alpha_bits))
520
13.9M
        (dev, (pgs->in_cachedevice ? go_text : go_graphics));
521
13.9M
}