Coverage Report

Created: 2026-04-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pcl/pxl/pxink.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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
/* pxink.c */
18
/* PCL XL ink setting operators */
19
20
#include "math_.h"
21
#include "stdio_.h"             /* for NULL */
22
#include "memory_.h"
23
#include "gdebug.h"
24
#include "gstypes.h"
25
#include "gsmemory.h"
26
#include "pxoper.h"
27
#include "pxstate.h"
28
#include "gxarith.h"
29
#include "gsstate.h"
30
#include "gxcspace.h"           /* must precede gscolor2.h */
31
#include "gscolor2.h"
32
#include "gscoord.h"
33
#include "gsimage.h"
34
#include "gscie.h"
35
#include "gscrd.h"
36
#include "gspath.h"
37
#include "gxdevice.h"
38
#include "gxht.h"
39
#include "gxstate.h"
40
#include "plht.h"
41
#include "pxptable.h"
42
#include "gzstate.h"
43
#include "gxdevsop.h"       /* Special ops, in this case pattern management */
44
#include "gxcolor2.h"       /* Required for definition of gs_pattern1_instance_t for high level patterns */
45
#include "pxgstate.h"       /* Prototype for px_high_level_pattern */
46
#include "gxpcolor.h"       /* Prototype for gx_pattern_cache_add_dummy_entry */
47
#include "gxpath.h"         /* Prototype for gx_clip_to_rectangle */
48
49
/*
50
 * Contrary to the documentation, SetColorSpace apparently doesn't set the
51
 * brush or pen to black.  To produce this behavior, uncomment the
52
 * following #define.
53
 */
54
#define SET_COLOR_SPACE_NO_SET_BLACK
55
56
/* ---------------- Utilities ---------------- */
57
58
/* ------ Halftones ------ */
59
60
/* Define a transfer function without gamma correction. */
61
static float
62
identity_transfer(double tint, const gx_transfer_map * ignore_map)
63
509k
{
64
509k
    return tint;
65
509k
}
66
67
/* Set the default halftone screen. */
68
static const byte order16x16[256] = {
69
#if 0
70
    /*
71
     * The following is a standard 16x16 ordered dither, except that
72
     * the very last pass goes in the order (0,1,2,3) rather than
73
     * (0,3,1,2).  This leads to excessive cancellation when the source
74
     * and paint halftones interact, but it's better than the standard
75
     * order, which has inadequate cancellation.
76
     *
77
     * This matrix is generated by the following call:
78
     *    [ <00 88 08 80> <00 44 04 40> <00 22 02 20> <00 01 10 11> ]
79
     *    { } makedither
80
     */
81
    0, 64, 32, 96, 8, 72, 40, 104, 2, 66, 34, 98, 10, 74, 42, 106,
82
    128, 192, 160, 224, 136, 200, 168, 232, 130, 194, 162, 226, 138, 202, 170,
83
        234,
84
    48, 112, 16, 80, 56, 120, 24, 88, 50, 114, 18, 82, 58, 122, 26, 90,
85
    176, 240, 144, 208, 184, 248, 152, 216, 178, 242, 146, 210, 186, 250, 154,
86
        218,
87
    12, 76, 44, 108, 4, 68, 36, 100, 14, 78, 46, 110, 6, 70, 38, 102,
88
    140, 204, 172, 236, 132, 196, 164, 228, 142, 206, 174, 238, 134, 198, 166,
89
        230,
90
    60, 124, 28, 92, 52, 116, 20, 84, 62, 126, 30, 94, 54, 118, 22, 86,
91
    188, 252, 156, 220, 180, 244, 148, 212, 190, 254, 158, 222, 182, 246, 150,
92
        214,
93
    3, 67, 35, 99, 11, 75, 43, 107, 1, 65, 33, 97, 9, 73, 41, 105,
94
    131, 195, 163, 227, 139, 203, 171, 235, 129, 193, 161, 225, 137, 201, 169,
95
        233,
96
    51, 115, 19, 83, 59, 123, 27, 91, 49, 113, 17, 81, 57, 121, 25, 89,
97
    179, 243, 147, 211, 187, 251, 155, 219, 177, 241, 145, 209, 185, 249, 153,
98
        217,
99
    15, 79, 47, 111, 7, 71, 39, 103, 13, 77, 45, 109, 5, 69, 37, 101,
100
    143, 207, 175, 239, 135, 199, 167, 231, 141, 205, 173, 237, 133, 197, 165,
101
        229,
102
    63, 127, 31, 95, 55, 119, 23, 87, 61, 125, 29, 93, 53, 117, 21, 85,
103
    191, 255, 159, 223, 183, 247, 151, 215, 189, 253, 157, 221, 181, 245, 149,
104
        213
105
#  define source_phase_x 1
106
#  define source_phase_y 1
107
#else
108
/*
109
 * The following is a 45 degree spot screen with the spots enumerated
110
 * in a defined order.  This matrix is generated by the following call:
111
112
/gamma_transfer {
113
  dup dup 0 le exch 1 ge or not {
114
    dup 0.17 lt
115
     { 3 mul }
116
     { dup 0.35 lt { 0.78 mul 0.38 add } { 0.53 mul 0.47 add } ifelse }
117
   ifelse
118
  } if
119
} def
120
121
[ [0 136 8 128 68 204 76 196]
122
  [18 33 17 34   1 2 19 35 50 49 32 16   3 48 0 51
123
   -15 -14 20 36 66 65 47 31   -13 64 15 52   -30 81 30 37]
124
] /gamma_transfer load makedither
125
126
 */
127
    38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167,
128
    30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122,
129
    27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106,
130
    35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148,
131
    152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34,
132
    69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18,
133
    79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21,
134
    193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42,
135
    39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172,
136
    31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127,
137
    28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112,
138
    36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146,
139
    157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34,
140
    67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18,
141
    78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21,
142
    191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42
143
#  define source_phase_x 4
144
#  define source_phase_y 0
145
#endif
146
};
147
148
/* Set the size for a default halftone screen. */
149
static void
150
px_set_default_screen_size(px_state_t * pxs, int method)
151
1.99k
{
152
1.99k
    px_gstate_t *pxgs = pxs->pxgs;
153
154
1.99k
    pxgs->halftone.width = pxgs->halftone.height = 16;
155
1.99k
}
156
157
/* If necessary, set the halftone in the graphics state. */
158
int
159
px_set_halftone(px_state_t * pxs)
160
4.26k
{
161
4.26k
    px_gstate_t *pxgs = pxs->pxgs;
162
4.26k
    int code;
163
164
4.26k
    if (pxgs->halftone.set)
165
2.27k
        return 0;
166
1.99k
    if (pxgs->halftone.method != eDownloaded) {
167
1.99k
        gs_string thresh;
168
169
1.99k
        thresh.data = (byte *) order16x16;
170
1.99k
        thresh.size = 256;
171
1.99k
        code = pl_set_pcl_halftone(pxs->pgs,
172
1.99k
                                   /* set transfer */ identity_transfer,
173
1.99k
                                   /* width */ 16, /*height */ 16,
174
1.99k
                                   /* dither data */ thresh,
175
1.99k
                                   /* x phase */ (int)pxgs->halftone.origin.x,
176
                                   /* y phase */
177
1.99k
                                   (int)pxgs->halftone.origin.y);
178
1.99k
    } else {                    /* downloaded */
179
0
        int ht_width, ht_height;
180
181
0
        switch (pxs->orientation) {
182
0
            case ePortraitOrientation:
183
0
            case eReversePortrait:
184
0
                ht_width = pxgs->halftone.width;
185
0
                ht_height = pxgs->halftone.height;
186
0
                break;
187
0
            case eLandscapeOrientation:
188
0
            case eReverseLandscape:
189
0
                ht_width = pxgs->halftone.height;
190
0
                ht_height = pxgs->halftone.width;
191
0
                break;
192
0
            default:
193
0
                return -1;
194
0
        }
195
196
0
        code = pl_set_pcl_halftone(pxs->pgs,
197
0
                                   /* set transfer */ identity_transfer,
198
0
                                   /* width */ ht_width, /*height */
199
0
                                   ht_height,
200
                                   /* dither data */
201
0
                                   pxgs->halftone.thresholds,
202
0
                                   /* x phase */ (int)pxgs->halftone.origin.x,
203
                                   /* y phase */
204
0
                                   (int)pxgs->halftone.origin.y);
205
0
        if (code < 0)
206
0
            gs_free_string(pxs->memory, pxgs->halftone.thresholds.data,
207
0
                           pxgs->halftone.thresholds.size,
208
0
                           "px_set_halftone(thresholds)");
209
0
        else {
210
0
            gs_free_string(pxs->memory, (byte *) pxgs->dither_matrix.data,
211
0
                           pxgs->dither_matrix.size,
212
0
                           "px_set_halftone(dither_matrix)");
213
0
            pxgs->dither_matrix = pxgs->halftone.thresholds;
214
0
        }
215
0
        pxgs->halftone.thresholds.data = 0;
216
0
        pxgs->halftone.thresholds.size = 0;
217
0
    }
218
1.99k
    if (code < 0)
219
0
        return code;
220
1.99k
    pxgs->halftone.set = true;
221
    /* Cached patterns have already been halftoned, so clear the cache. */
222
1.99k
    px_purge_pattern_cache(pxs, eSessionPattern);
223
1.99k
    return 0;
224
1.99k
}
225
226
/* ------ Patterns ------ */
227
228
/*
229
 * The library caches patterns in their fully rendered form, i.e., after
230
 * halftoning.  In order to avoid seams or anomalies, we have to replicate
231
 * the pattern so that its size is an exact multiple of the halftone size.
232
 */
233
234
static uint
235
ilcm(uint x, uint y)
236
424
{
237
424
    return x * (y / igcd(x, y));
238
424
}
239
240
/* Render a pattern. */
241
static int
242
px_paint_pattern(const gs_client_color * pcc, gs_gstate * pgs)
243
0
{
244
0
    const gs_client_pattern *ppat = gs_getpattern(pcc);
245
0
    const px_pattern_t *pattern = (px_pattern_t *)gs_get_pattern_client_data(pcc);
246
0
    const byte *dp = pattern->data;
247
0
    gs_image_enum *penum = NULL;
248
0
    gs_image_t image;
249
0
    int code;
250
0
    int num_components =
251
0
        (pattern->params.indexed || pattern->params.color_space == eGray ?
252
0
         1 : 3);
253
0
    uint rep_width = pattern->params.width;
254
0
    uint rep_height = pattern->params.height;
255
0
    uint full_width = (uint) ppat->XStep;
256
0
    uint full_height = (uint) ppat->YStep;
257
0
    uint bits_per_row, bytes_per_row;
258
0
    int x;
259
260
0
    code =
261
0
        px_image_color_space(&image, &pattern->params, &pattern->palette,
262
0
                             pgs);
263
0
    if (code < 0)
264
0
        return code;
265
266
0
    bits_per_row = rep_width * image.BitsPerComponent * num_components;
267
0
    bytes_per_row = (bits_per_row + 7) >> 3;
268
    /*
269
     * As noted above, in general, we have to replicate the original
270
     * pattern to a multiple that avoids halftone seams.  If the
271
     * number of bits per row is a multiple of 8, we can do this with
272
     * a single image; otherwise, we need one image per X replica.
273
     * To simplify the code, we always use the (slightly) slower method.
274
     */
275
0
    image.Width = rep_width;
276
0
    image.Height = full_height;
277
0
    image.CombineWithColor = true;
278
0
    for (x = 0; x < full_width; x += rep_width) {
279
0
        int y;
280
281
0
        image.ImageMatrix.tx = (float)-x;
282
0
        penum = gs_image_enum_alloc(gs_gstate_memory(pgs), "px_paint_pattern");
283
0
        if (penum == NULL) {
284
0
            code = gs_note_error(gs_error_VMerror);
285
0
            break;
286
0
        }
287
0
        code = gs_image_init(penum, &image, 0, false, pgs);
288
0
        if (code < 0)
289
0
            break;
290
0
        for (y = 0; y < full_height; ++y) {
291
0
            const byte *row = dp + (y % rep_height) * bytes_per_row;
292
0
            uint used;          /* better named not_used */
293
294
0
            code = gs_image_next(penum, row, bytes_per_row, &used);
295
0
            if (code < 0)
296
0
                break;
297
0
        }
298
0
        code = gs_image_cleanup_and_free_enum(penum, pgs);
299
0
        penum = NULL;
300
0
        if (code < 0)
301
0
            break;
302
0
    }
303
0
    if (code < 0) {
304
0
        (void)gs_image_cleanup_and_free_enum(penum, pgs);
305
0
        penum = NULL;
306
0
    }
307
0
    return code;
308
0
}
309
310
int px_high_level_pattern(gs_gstate * pgs)
311
0
{
312
0
    gs_matrix m;
313
0
    gs_rect bbox;
314
0
    gs_fixed_rect clip_box;
315
0
    int code;
316
0
    gx_device_color *pdc = gs_currentdevicecolor_inline(pgs);
317
0
    const gs_client_pattern *ppat = gs_getpattern(&pdc->ccolor);
318
0
    gs_color_space *pcs;
319
0
    gs_pattern1_instance_t *pinst =
320
0
        (gs_pattern1_instance_t *)gs_currentcolor(pgs)->pattern;
321
0
    const px_pattern_t *pattern = (const px_pattern_t *)gs_get_pattern_client_data(gs_currentcolor(pgs));
322
323
0
    code = gx_pattern_cache_add_dummy_entry(pgs, pinst,
324
0
        pgs->device->color_info.depth);
325
0
    if (code < 0)
326
0
        return code;
327
328
0
    code = gs_gsave(pgs);
329
0
    if (code < 0)
330
0
        return code;
331
332
0
    dev_proc(pgs->device, get_initial_matrix)(pgs->device, &m);
333
0
    gs_setmatrix(pgs, &m);
334
0
    code = gs_bbox_transform(&ppat->BBox, &ctm_only(pgs), &bbox);
335
0
    if (code < 0) {
336
0
        gs_grestore(pgs);
337
0
            return code;
338
0
    }
339
0
    clip_box.p.x = float2fixed(bbox.p.x);
340
0
    clip_box.p.y = float2fixed(bbox.p.y);
341
0
    clip_box.q.x = float2fixed(bbox.q.x);
342
0
    clip_box.q.y = float2fixed(bbox.q.y);
343
0
    code = gx_clip_to_rectangle(pgs, &clip_box);
344
0
    if (code < 0) {
345
0
        gs_grestore(pgs);
346
0
        return code;
347
0
    }
348
349
0
    {
350
0
        pattern_accum_param_s param;
351
0
        param.pinst = (void *)pinst;
352
0
        param.graphics_state = (void *)pgs;
353
0
        param.pinst_id = pinst->id;
354
355
0
        code = (*dev_proc(pgs->device, dev_spec_op))((gx_device *)pgs->device,
356
0
            gxdso_pattern_start_accum, &param, sizeof(pattern_accum_param_s));
357
0
    }
358
359
0
    if (code < 0) {
360
0
        gs_grestore(pgs);
361
0
        return code;
362
0
    }
363
364
    /* set the color space, the 'current' space is pattern, we need to set the
365
     * proper space for the image that we draw.
366
     */
367
0
    switch (pattern->params.color_space) {
368
0
        case eGray:
369
0
            pcs = gs_cspace_new_DeviceGray(pgs->memory);
370
0
            if (pcs == NULL) {
371
0
                gs_grestore(pgs);
372
0
                return_error(errorInsufficientMemory);
373
0
            }
374
0
            break;
375
0
        case eRGB:
376
0
        case eSRGB:
377
0
            pcs = gs_cspace_new_DeviceRGB(pgs->memory);
378
0
            if (pcs == NULL) {
379
0
                gs_grestore(pgs);
380
0
                return_error(errorInsufficientMemory);
381
0
            }
382
0
            break;
383
0
        default:
384
0
            gs_grestore(pgs);
385
0
            return_error(errorIllegalAttributeValue);
386
0
    }
387
0
    gs_setcolorspace(pgs, pcs);
388
389
0
    code = px_paint_pattern(&pdc->ccolor, pgs);
390
0
    if (code < 0)
391
0
        return code;
392
393
0
    code = gs_grestore(pgs);
394
0
    if (code < 0)
395
0
        return code;
396
0
    {
397
0
        pattern_accum_param_s param;
398
0
        param.pinst = (void *)pinst;
399
0
        param.graphics_state = (void *)pgs;
400
0
        param.pinst_id = pinst->id;
401
402
0
        code = dev_proc(pgs->device, dev_spec_op)(pgs->device,
403
0
                          gxdso_pattern_finish_accum, &param, sizeof(pattern_accum_param_s));
404
0
    }
405
406
0
    return code;
407
0
}
408
409
static int px_remap_pattern(const gs_client_color *pcc, gs_gstate *pgs)
410
0
{
411
0
    const gs_client_pattern *ppat = gs_getpattern(pcc);
412
0
    int code = 0;
413
414
    /* pgs->device is the newly created pattern accumulator, but we want to test the device
415
     * that is 'behind' that, the actual output device, so we use the one from
416
     * the saved graphics state.
417
     */
418
0
    if (pgs->have_pattern_streams)
419
0
        code = dev_proc(pcc->pattern->saved->device, dev_spec_op)(pcc->pattern->saved->device,
420
0
                                gxdso_pattern_can_accum, (void *)ppat, ppat->uid.id);
421
422
0
    if (code == 1) {
423
        /* Device handles high-level patterns, so return 'remap'.
424
         * This closes the internal accumulator device, as we no longer need
425
         * it, and the error trickles back up to the PDL client. The client
426
         * must then take action to start the device's accumulator, draw the
427
         * pattern, close the device's accumulator and generate a cache entry.
428
         * See px_high_level_pattern above.
429
         */
430
0
        return_error(gs_error_Remap_Color);
431
0
    } else {
432
0
        return px_paint_pattern(pcc, pgs);
433
0
    }
434
0
}
435
436
/* Create the rendering of a pattern. */
437
static int
438
render_pattern(gs_client_color * pcc, const px_pattern_t * pattern,
439
               const px_value_t * porigin, const px_value_t * pdsize,
440
               px_state_t * pxs)
441
212
{
442
212
    px_gstate_t *pxgs = pxs->pxgs;
443
212
    uint rep_width = pattern->params.width;
444
212
    uint rep_height = pattern->params.height;
445
212
    uint full_width, full_height;
446
212
    gs_gstate *pgs = pxs->pgs;
447
212
    gs_client_pattern templat;
448
449
    /*
450
     * If halftoning may occur, replicate the pattern so we don't get
451
     * halftone seams.
452
     */
453
212
    {
454
212
        gx_device *dev = gs_currentdevice(pgs);
455
456
212
        if (!gx_device_must_halftone(dev)) {    /* No halftoning. */
457
0
            full_width = rep_width;
458
0
            full_height = rep_height;
459
212
        } else {
460
212
            full_width = ilcm(rep_width, pxgs->halftone.width);
461
212
            full_height = ilcm(rep_height, pxgs->halftone.height);
462
            /*
463
             * If the pattern would be enormous, don't replicate it.
464
             * This is a HACK.
465
             */
466
212
            if (full_width > 10000)
467
0
                full_width = rep_width;
468
212
            if (full_height > 10000)
469
1
                full_height = rep_height;
470
212
        }
471
212
    }
472
    /* Construct a Pattern for the library, and render it. */
473
212
    gs_pattern1_init(&templat);
474
212
    uid_set_UniqueID(&templat.uid, pattern->id);
475
212
    templat.PaintType = 1;
476
212
    templat.TilingType = 1;
477
212
    templat.BBox.p.x = 0;
478
212
    templat.BBox.p.y = 0;
479
212
    templat.BBox.q.x = full_width;
480
212
    templat.BBox.q.y = full_height;
481
212
    templat.XStep = (float)full_width;
482
212
    templat.YStep = (float)full_height;
483
212
    templat.PaintProc = px_remap_pattern;
484
212
    {
485
212
        gs_matrix mat;
486
212
        gs_point dsize;
487
212
        int code;
488
489
212
        if (porigin)
490
0
            gs_make_translation(real_value(porigin, 0),
491
0
                                real_value(porigin, 1), &mat);
492
212
        else
493
212
            gs_make_identity(&mat);
494
495
212
        if (pdsize) {
496
0
            dsize.x = real_value(pdsize, 0);
497
0
            dsize.y = real_value(pdsize, 1);
498
212
        } else {
499
212
            dsize.x = pattern->params.dest_width;
500
212
            dsize.y = pattern->params.dest_height;
501
212
        }
502
212
        gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height,
503
212
                        &mat);
504
        /*
505
         * gs_makepattern will make a copy of the current gstate.
506
         * We don't want this copy to contain any circular back pointers
507
         * to px_pattern_ts: such pointers are unnecessary, because
508
         * px_paint_pattern doesn't use the pen and brush (in fact,
509
         * it doesn't even reference the px_gstate_t).  We also want to
510
         * reset the path and clip path.  The easiest (although not by
511
         * any means the most efficient) way to do this is to do a gsave,
512
         * reset the necessary things, do the makepattern, and then do
513
         * a grestore.
514
         */
515
212
        code = gs_gsave(pgs);
516
212
        if (code < 0)
517
0
            return code;
518
212
        {
519
212
            px_gstate_t *pxgs = pxs->pxgs;
520
521
212
            px_gstate_rc_adjust(pxgs, -1, pxgs->memory);
522
212
            pxgs->brush.type = pxgs->pen.type = pxpNull;
523
212
            gs_newpath(pgs);
524
212
            px_initclip(pxs);
525
212
        }
526
212
        {
527
212
            gs_color_space *pcs;
528
529
            /* set the color space */
530
212
            switch (pattern->params.color_space) {
531
212
                case eGray:
532
212
                    pcs = gs_cspace_new_DeviceGray(pxgs->memory);
533
212
                    if (pcs == NULL)
534
0
                        return_error(errorInsufficientMemory);
535
212
                    break;
536
212
                case eRGB:
537
0
                case eSRGB:
538
0
                    pcs = gs_cspace_new_DeviceRGB(pxgs->memory);
539
0
                    if (pcs == NULL)
540
0
                        return_error(errorInsufficientMemory);
541
0
                    break;
542
0
                default:
543
0
                    return_error(errorIllegalAttributeValue);
544
212
            }
545
212
            gs_setcolorspace(pgs, pcs);
546
212
        }
547
212
        code = gs_makepattern(pcc, &templat, &mat, pgs, NULL);
548
212
        pcc->pattern->client_data = (void *)pattern;
549
212
        gs_grestore(pgs);
550
212
        return code;
551
212
    }
552
212
}
553
554
/* ------ Brush/pen ------ */
555
556
/* Check parameters and execute SetBrushSource or SetPenSource: */
557
/* pxaRGBColor, pxaGrayLevel, pxaNullBrush/Pen, pxaPatternSelectID, */
558
/* pxaPatternOrigin, pxaNewDestinationSize */
559
static ulong
560
int_type_max(px_data_type_t type)
561
14.2k
{
562
14.2k
    return
563
14.2k
        (type & pxd_ubyte ? 255 :
564
14.2k
         type & pxd_uint16 ? 65535 :
565
0
         type & pxd_sint16 ? 32767 : type & pxd_uint32 ? (ulong) 0xffffffff :
566
0
         /* type & pxd_sint32 */ 0x7fffffff);
567
14.2k
}
568
static real
569
fraction_value(const px_value_t * pv, int i)
570
4.38k
{
571
4.38k
    px_data_type_t type = pv->type;
572
4.38k
    real v;
573
574
4.38k
    if (type & pxd_real32)
575
0
        return pv->value.ra[i];
576
4.38k
    v = (real) pv->value.ia[i];
577
4.38k
    return (v < 0 ? 0 : v / int_type_max(type));
578
4.38k
}
579
580
/* we use an enumeration instead of index numbers improve readability in this
581
   "very busy" routine */
582
typedef enum
583
{
584
    aRGBColor = 0,
585
    aGrayLevel = 1,
586
    aPrimaryArray = 2,
587
    aPrimaryDepth = 3,
588
    aNullBrushPen = 4,
589
    aPatternSelectID = 5,
590
    aPatternOrigin = 6,
591
    aNewDestinationSize = 7
592
} pxl_source_t;
593
594
/* given a paint type decide if a halftone is necessary */
595
static bool
596
px_needs_halftone(const gs_memory_t * mem, px_paint_t * ppt)
597
15.2k
{
598
15.2k
    bool needs_halftone;
599
600
15.2k
    if (ppt->type == pxpPattern)
601
212
        needs_halftone = true;
602
15.0k
    else if (ppt->type == pxpNull)
603
7.35k
        needs_halftone = false;
604
7.66k
    else if (ppt->type == pxpGray)
605
4.38k
        needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1;
606
3.28k
    else if (ppt->type == pxpRGB || ppt->type == pxpSRGB) {     /* rgb or srgb */
607
3.28k
        int i;
608
609
3.28k
        needs_halftone = false;
610
8.33k
        for (i = 0; i < 3; i++) {
611
6.89k
            if (ppt->value.rgb[i] != 0 && ppt->value.rgb[i] != 1) {
612
1.84k
                needs_halftone = true;
613
1.84k
                break;
614
1.84k
            }
615
6.89k
        }
616
3.28k
    } else {
617
0
        dmprintf(mem, "unknown paint type\n");
618
0
        needs_halftone = true;
619
0
    }
620
15.2k
    return needs_halftone;
621
15.2k
}
622
623
static int
624
set_source(const px_args_t * par, px_state_t * pxs, px_paint_t * ppt)
625
15.2k
{
626
15.2k
    px_gstate_t *pxgs = pxs->pxgs;
627
15.2k
    int code = 0;
628
629
    /* pxaPatternSelectID */
630
15.2k
    if (par->pv[aPatternSelectID]) {
631
213
        px_value_t key;
632
213
        void *value;
633
213
        px_pattern_t *pattern;
634
213
        gs_client_color ccolor;
635
213
        int code;
636
637
213
        if (par->pv[aRGBColor] || par->pv[aGrayLevel]
638
213
            || par->pv[aNullBrushPen])
639
0
            return_error(errorIllegalAttributeCombination);
640
213
        key.type = pxd_array | pxd_ubyte;
641
213
        key.value.array.data = (byte *) & par->pv[aPatternSelectID]->value.i;
642
213
        key.value.array.size = sizeof(int32_t);
643
213
        if (!(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) ||
644
213
              px_dict_find(&pxs->page_pattern_dict, &key, &value) ||
645
213
              px_dict_find(&pxs->session_pattern_dict, &key, &value))
646
213
            )
647
1
            return_error(errorRasterPatternUndefined);
648
212
        pattern = value;
649
650
212
        px_set_halftone(pxs);
651
212
        code = render_pattern(&ccolor, pattern, par->pv[aPatternOrigin],
652
212
                              par->pv[aNewDestinationSize], pxs);
653
        /*
654
         * We don't use px_paint_rc_adjust(... 1 ...) here, because
655
         * gs_makepattern creates pattern instances with a reference
656
         * count already set to 1.
657
         */
658
212
        rc_increment(pattern);
659
212
        if (code < 0)
660
0
            return code;
661
212
        px_paint_rc_adjust(ppt, -1, pxs->memory);
662
212
        ppt->type = pxpPattern;
663
212
        ppt->value.pattern.pattern = pattern;
664
212
        ppt->value.pattern.color = ccolor;
665
        /* not pxaPatternSelectID but we have a pattern origin or
666
           newdestination size */
667
15.0k
    } else if (par->pv[aPatternOrigin] || par->pv[aNewDestinationSize]) {
668
0
        return_error(errorIllegalAttributeCombination);
669
15.0k
    } else if (par->pv[aRGBColor]) {
670
3.28k
        const px_value_t *prgb = par->pv[aRGBColor];
671
3.28k
        int i;
672
673
3.28k
        if (par->pv[aGrayLevel] || par->pv[aNullBrushPen])
674
0
            return_error(errorIllegalAttributeCombination);
675
3.28k
        if (pxgs->color_space != eRGB && pxgs->color_space != eSRGB)
676
5
            return_error(errorColorSpaceMismatch);
677
3.28k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
678
3.28k
        ppt->type = pxpRGB;
679
680
13.1k
        for (i = 0; i < 3; ++i)
681
9.84k
            if (prgb->type & pxd_any_real)
682
0
                ppt->value.rgb[i] = real_elt(prgb, i);
683
9.84k
            else {
684
9.84k
                int32_t v = integer_elt(prgb, i);
685
686
9.84k
                ppt->value.rgb[i] =
687
9.84k
                    (v < 0 ? 0 : (real) v / int_type_max(prgb->type));
688
9.84k
            }
689
11.7k
    } else if (par->pv[aGrayLevel]) {   /* pxaGrayLevel */
690
4.38k
        if (par->pv[aNullBrushPen])
691
0
            return_error(errorIllegalAttributeCombination);
692
4.38k
        if (pxgs->color_space != eGray)
693
0
            return_error(errorColorSpaceMismatch);
694
4.38k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
695
4.38k
        ppt->type = pxpGray;
696
4.38k
        ppt->value.gray = fraction_value(par->pv[aGrayLevel], 0);
697
7.35k
    } else if (par->pv[aNullBrushPen]) {        /* pxaNullBrush/Pen */
698
7.35k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
699
7.35k
        ppt->type = pxpNull;
700
7.35k
    } else if (par->pv[aPrimaryDepth] && par->pv[aPrimaryArray]) {
701
0
        px_paint_rc_adjust(ppt, -1, pxs->memory);
702
0
        if (pxgs->color_space == eRGB)
703
0
            ppt->type = pxpRGB;
704
0
        else if (pxgs->color_space == eGray)
705
0
            ppt->type = pxpGray;
706
0
        else if (pxgs->color_space == eSRGB)
707
0
            ppt->type = pxpSRGB;
708
0
        else {
709
0
            dmprintf1(pxgs->memory, "Warning unknown color space %d\n",
710
0
                      pxgs->color_space);
711
0
            ppt->type = pxpGray;
712
0
        }
713
        /* NB depth?? - for range checking */
714
0
        if (ppt->type == pxpRGB || ppt->type == pxpSRGB) {
715
0
            ppt->value.rgb[0] =
716
0
                (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0;
717
0
            ppt->value.rgb[1] =
718
0
                (float)par->pv[aPrimaryArray]->value.array.data[1] / 255.0;
719
0
            ppt->value.rgb[2] =
720
0
                (float)par->pv[aPrimaryArray]->value.array.data[2] / 255.0;
721
0
        } else
722
            /* NB figure out reals and ints */
723
0
            ppt->value.gray =
724
0
                (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0;
725
0
    } else
726
7
        return_error(errorMissingAttribute);
727
    /*
728
     * Update the halftone to the most recently set one.
729
     * This will do the wrong thing if we set the brush or pen source,
730
     * set the halftone, and then set the other source, but we have
731
     * no way to handle this properly with the current library.
732
     */
733
15.2k
    if (px_needs_halftone(pxs->memory, ppt))
734
2.06k
        code = px_set_halftone(pxs);
735
15.2k
    return code;
736
15.2k
}
737
738
/* Set up a brush or pen for drawing. */
739
/* If it is a pattern, SetBrush/PenSource guaranteed that it is compatible */
740
/* with the current color space. */
741
int
742
px_set_paint(const px_paint_t * ppt, px_state_t * pxs)
743
12.5k
{
744
12.5k
    gs_gstate *pgs = pxs->pgs;
745
12.5k
    px_paint_type_t type;
746
747
12.5k
    type = ppt->type;
748
12.5k
    switch (type) {
749
0
        case pxpNull:
750
0
            return gs_setnullcolor(pgs);
751
8.57k
        case pxpRGB:
752
8.57k
            return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1],
753
8.57k
                                  ppt->value.rgb[2]);
754
3.86k
        case pxpGray:
755
3.86k
            return gs_setgray(pgs, ppt->value.gray);
756
88
        case pxpPattern:
757
88
            return gs_setpattern(pgs, &ppt->value.pattern.color);
758
0
        case pxpSRGB:
759
0
            return gs_setrgbcolor(pgs,
760
0
                                  ppt->value.rgb[0],
761
0
                                  ppt->value.rgb[1],
762
0
                                  ppt->value.rgb[2]);
763
0
        default:               /* can't happen */
764
0
            return_error(errorIllegalAttributeValue);
765
12.5k
    }
766
12.5k
}
767
768
/* ---------------- Operators ---------------- */
769
770
const byte apxSetBrushSource[] = {
771
    0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth,
772
        pxaNullBrush, pxaPatternSelectID,
773
    pxaPatternOrigin, pxaNewDestinationSize, 0
774
};
775
776
int
777
pxSetBrushSource(px_args_t * par, px_state_t * pxs)
778
7.88k
{
779
7.88k
    return set_source(par, pxs, &pxs->pxgs->brush);
780
7.88k
}
781
782
const byte apxSetColorSpace[] = {
783
    0, pxaColorSpace, pxaColorimetricColorSpace, pxaXYChromaticities,
784
        pxaWhiteReferencePoint,
785
    pxaCRGBMinMax, pxaGammaGain, pxaPaletteDepth, pxaPaletteData, 0
786
};
787
788
/* it appears the 4600 does not support CRGB define this to enable support */
789
/* #define SUPPORT_COLORIMETRIC */
790
791
int
792
pxSetColorSpace(px_args_t * par, px_state_t * pxs)
793
3.37k
{
794
3.37k
    px_gstate_t *pxgs = pxs->pxgs;
795
3.37k
    pxeColorSpace_t cspace;
796
797
3.37k
    if (par->pv[0])
798
3.37k
        cspace = par->pv[0]->value.i;
799
1
    else if (par->pv[1])
800
0
        cspace = par->pv[1]->value.i;
801
1
    else
802
1
        return_error(errorIllegalAttributeValue);
803
804
3.37k
    if (par->pv[6] && par->pv[7]) {
805
0
        int ncomp =
806
0
            ((cspace == eRGB || cspace == eSRGB) ? 3 : 1);
807
0
        uint size = par->pv[7]->value.array.size;
808
809
0
        if (!(size == ncomp << 1 || size == ncomp << 4 || size == ncomp << 8)
810
0
            ) {
811
            /* The HP printers we've tested appear to truncate
812
               this value and not produce an error on overflow */
813
0
            if (size > ncomp << 8)
814
0
                size = ncomp << 8;
815
0
            else
816
0
                return_error(errorIllegalAttributeValue);
817
0
        }
818
        /* The palette is in an array, but we want a string. */
819
0
        {
820
0
            if (pxgs->palette.data && !pxgs->palette_is_shared &&
821
0
                pxgs->palette.size != size) {
822
0
                gs_free_string(pxs->memory, (byte *) pxgs->palette.data,
823
0
                               pxgs->palette.size,
824
0
                               "pxSetColorSpace(old palette)");
825
0
                pxgs->palette.data = 0;
826
0
                pxgs->palette.size = 0;
827
0
            }
828
0
            if (pxgs->palette.data == 0 || pxgs->palette_is_shared) {
829
0
                byte *pdata = gs_alloc_string(pxs->memory, size,
830
0
                                              "pxSetColorSpace(palette)");
831
832
0
                if (pdata == 0)
833
0
                    return_error(errorInsufficientMemory);
834
0
                pxgs->palette.data = pdata;
835
0
                pxgs->palette.size = size;
836
0
            }
837
0
            memcpy((void *)pxgs->palette.data, par->pv[7]->value.array.data,
838
0
                   size);
839
0
        }
840
3.37k
    } else if (par->pv[6] || par->pv[7])
841
0
        return_error(errorMissingAttribute);
842
3.37k
    else if (pxgs->palette.data) {
843
0
        if (!pxgs->palette_is_shared)
844
0
            gs_free_string(pxs->memory, (byte *) pxgs->palette.data,
845
0
                           pxgs->palette.size,
846
0
                           "pxSetColorSpace(old palette)");
847
0
        pxgs->palette.data = 0;
848
0
        pxgs->palette.size = 0;
849
0
    }
850
3.37k
    pxgs->palette_is_shared = false;
851
3.37k
    pxgs->color_space = cspace;
852
#ifndef SET_COLOR_SPACE_NO_SET_BLACK
853
    {
854
        px_paint_rc_adjust(&pxgs->brush, -1, pxs->memory);
855
        pxgs->brush.type = pxpGray;
856
        pxgs->brush.value.gray = 0;
857
    }
858
    {
859
        px_paint_rc_adjust(&pxgs->pen, -1, pxs->memory);
860
        pxgs->pen.type = pxpGray;
861
        pxgs->pen.value.gray = 0;
862
    }
863
#endif
864
3.37k
    return 0;
865
3.37k
}
866
867
const byte apxSetHalftoneMethod[] = {
868
    0, pxaDitherOrigin, pxaDeviceMatrix, pxaDitherMatrixDataType,
869
    pxaDitherMatrixSize, pxaDitherMatrixDepth, pxaAllObjectTypes,
870
    pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0
871
};
872
873
int
874
pxSetHalftoneMethod(px_args_t * par, px_state_t * pxs)
875
2.30k
{
876
2.30k
    gs_gstate *pgs = pxs->pgs;
877
2.30k
    px_gstate_t *pxgs = pxs->pxgs;
878
2.30k
    pxeDitherMatrix_t method;
879
880
2.30k
    if (par->pv[5] || par->pv[6] || par->pv[7] || par->pv[8])
881
        /* Placeholder to support halftones per object type. */
882
223
        ;
883
884
2.30k
    if (par->pv[1]) {           /* Internal halftone */
885
1.99k
        if (par->pv[2] || par->pv[3] || par->pv[4])
886
0
            return_error(errorIllegalAttributeCombination);
887
1.99k
        method = par->pv[1]->value.i;
888
1.99k
        px_set_default_screen_size(pxs, method);
889
1.99k
        pxs->download_string.data = 0;
890
1.99k
        pxs->download_string.size = 0;
891
1.99k
    } else if (par->pv[2] && par->pv[3] && par->pv[4]) {        /* Dither matrix */
892
0
        uint width = par->pv[3]->value.ia[0];
893
0
        uint source_width = (width + 3) & ~3;
894
0
        uint height = par->pv[3]->value.ia[1];
895
0
        uint size = width * height;
896
0
        uint source_size = source_width * height;
897
898
0
        if (par->source.position == 0) {
899
0
            byte *data;
900
901
0
            if (par->source.available == 0)
902
0
                return pxNeedData;
903
0
            data = gs_alloc_string(pxs->memory, size, "dither matrix");
904
0
            if (data == 0)
905
0
                return_error(errorInsufficientMemory);
906
0
            pxs->download_string.data = data;
907
0
            pxs->download_string.size = size;
908
0
        }
909
0
        while (par->source.position < source_size) {
910
0
            uint source_x = par->source.position % source_width;
911
0
            uint source_y = par->source.position / source_width;
912
0
            uint used;
913
914
0
            if (par->source.available == 0)
915
0
                return pxNeedData;
916
0
            if (source_x >= width) {    /* Skip padding bytes at end of row. */
917
0
                used = min(par->source.available, source_width - source_x);
918
0
            } else {            /* Read data. */
919
0
                const byte *src = par->source.data;
920
0
                byte *dest = pxs->download_string.data;
921
0
                byte *pdata_min = dest;
922
0
                byte *pdata_max = pdata_min + pxs->download_string.size;
923
0
                uint i;
924
0
                int skip;
925
926
0
                used = min(par->source.available, width - source_x);
927
                /*
928
                 * The documentation doesn't say this, but we have to
929
                 * rotate the dither matrix to match the orientation,
930
                 * remembering that we have a Y-inverted coordinate
931
                 * system.  This is quite a nuisance!
932
                 */
933
0
                switch (pxs->orientation) {
934
0
                    case ePortraitOrientation:
935
0
                        dest += source_y * width + source_x;
936
0
                        skip = 1;
937
0
                        break;
938
0
                    case eLandscapeOrientation:
939
0
                        dest += (width - 1 - source_x) * height + source_y;
940
0
                        skip = -(int)height;
941
0
                        break;
942
0
                    case eReversePortrait:
943
0
                        dest += (height - 1 - source_y) * width +
944
0
                            width - 1 - source_x;
945
0
                        skip = -1;
946
0
                        break;
947
0
                    case eReverseLandscape:
948
0
                        dest += (source_x + 1) * height - source_y - 1;
949
0
                        skip = height;
950
0
                        break;
951
0
                    default:
952
0
                        return -1;
953
0
                }
954
0
                if ((dest < pdata_min) || (pdata_max < dest + ((used - 1) * (int64_t)skip)))
955
0
                    return_error(gs_error_rangecheck);
956
0
                for (i = 0; i < used; ++i, ++src, dest += skip) {
957
0
                    *dest = *src;
958
0
                }
959
0
            }
960
0
            par->source.position += used;
961
0
            par->source.available -= used;
962
0
            par->source.data += used;
963
964
0
        }
965
0
        pxgs->halftone.width = width;
966
0
        pxgs->halftone.height = height;
967
0
        method = eDownloaded;
968
0
    } else
969
308
        return 0;
970
1.99k
    if (par->pv[0])
971
0
        gs_transform(pgs, real_value(par->pv[0], 0),
972
0
                     real_value(par->pv[0], 1), &pxgs->halftone.origin);
973
1.99k
    else
974
1.99k
        gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin);
975
1.99k
    pxgs->halftone.thresholds = pxs->download_string;
976
1.99k
    pxgs->halftone.method = method;
977
1.99k
    pxgs->halftone.set = false;
978
1.99k
    return 0;
979
2.30k
}
980
981
const byte apxSetPenSource[] = {
982
    0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth,
983
        pxaNullPen, pxaPatternSelectID,
984
    pxaPatternOrigin, pxaNewDestinationSize, 0
985
};
986
987
int
988
pxSetPenSource(px_args_t * par, px_state_t * pxs)
989
7.35k
{
990
7.35k
    return set_source(par, pxs, &pxs->pxgs->pen);
991
7.35k
}
992
993
const byte apxSetColorTreatment[] =
994
    { 0, pxaColorTreatment, pxaAllObjectTypes, pxaTextObjects,
995
pxaVectorObjects, pxaRasterObjects, 0 };
996
997
int
998
pxSetColorTreatment(px_args_t * par, px_state_t * pxs)
999
340
{
1000
340
    return 0;
1001
340
}
1002
1003
const byte apxSetNeutralAxis[] = { 0, pxaAllObjectTypes, pxaTextObjects,
1004
    pxaVectorObjects, pxaRasterObjects, 0
1005
};
1006
1007
int
1008
pxSetNeutralAxis(px_args_t * par, px_state_t * pxs)
1009
264
{
1010
264
    return 0;
1011
264
}
1012
1013
const byte apxSetColorTrapping[] = { pxaAllObjectTypes, 0, 0 };
1014
1015
int
1016
pxSetColorTrapping(px_args_t * par, px_state_t * pxs)
1017
231
{
1018
231
    return 0;
1019
231
}
1020
1021
const byte apxSetAdaptiveHalftoning[] =
1022
    { 0, pxaAllObjectTypes, pxaTextObjects, pxaVectorObjects,
1023
pxaRasterObjects, 0 };
1024
1025
int
1026
pxSetAdaptiveHalftoning(px_args_t * par, px_state_t * pxs)
1027
231
{
1028
231
    return 0;
1029
231
}