Coverage Report

Created: 2026-04-01 07:17

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
450k
{
64
450k
    return tint;
65
450k
}
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.75k
{
152
1.75k
    px_gstate_t *pxgs = pxs->pxgs;
153
154
1.75k
    pxgs->halftone.width = pxgs->halftone.height = 16;
155
1.75k
}
156
157
/* If necessary, set the halftone in the graphics state. */
158
int
159
px_set_halftone(px_state_t * pxs)
160
3.81k
{
161
3.81k
    px_gstate_t *pxgs = pxs->pxgs;
162
3.81k
    int code;
163
164
3.81k
    if (pxgs->halftone.set)
165
2.05k
        return 0;
166
1.75k
    if (pxgs->halftone.method != eDownloaded) {
167
1.75k
        gs_string thresh;
168
169
1.75k
        thresh.data = (byte *) order16x16;
170
1.75k
        thresh.size = 256;
171
1.75k
        code = pl_set_pcl_halftone(pxs->pgs,
172
1.75k
                                   /* set transfer */ identity_transfer,
173
1.75k
                                   /* width */ 16, /*height */ 16,
174
1.75k
                                   /* dither data */ thresh,
175
1.75k
                                   /* x phase */ (int)pxgs->halftone.origin.x,
176
                                   /* y phase */
177
1.75k
                                   (int)pxgs->halftone.origin.y);
178
1.75k
    } 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.75k
    if (code < 0)
219
0
        return code;
220
1.75k
    pxgs->halftone.set = true;
221
    /* Cached patterns have already been halftoned, so clear the cache. */
222
1.75k
    px_purge_pattern_cache(pxs, eSessionPattern);
223
1.75k
    return 0;
224
1.75k
}
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
336
{
237
336
    return x * (y / igcd(x, y));
238
336
}
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
168
{
442
168
    px_gstate_t *pxgs = pxs->pxgs;
443
168
    uint rep_width = pattern->params.width;
444
168
    uint rep_height = pattern->params.height;
445
168
    uint full_width, full_height;
446
168
    gs_gstate *pgs = pxs->pgs;
447
168
    gs_client_pattern templat;
448
449
    /*
450
     * If halftoning may occur, replicate the pattern so we don't get
451
     * halftone seams.
452
     */
453
168
    {
454
168
        gx_device *dev = gs_currentdevice(pgs);
455
456
168
        if (!gx_device_must_halftone(dev)) {    /* No halftoning. */
457
0
            full_width = rep_width;
458
0
            full_height = rep_height;
459
168
        } else {
460
168
            full_width = ilcm(rep_width, pxgs->halftone.width);
461
168
            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
168
            if (full_width > 10000)
467
0
                full_width = rep_width;
468
168
            if (full_height > 10000)
469
1
                full_height = rep_height;
470
168
        }
471
168
    }
472
    /* Construct a Pattern for the library, and render it. */
473
168
    gs_pattern1_init(&templat);
474
168
    uid_set_UniqueID(&templat.uid, pattern->id);
475
168
    templat.PaintType = 1;
476
168
    templat.TilingType = 1;
477
168
    templat.BBox.p.x = 0;
478
168
    templat.BBox.p.y = 0;
479
168
    templat.BBox.q.x = full_width;
480
168
    templat.BBox.q.y = full_height;
481
168
    templat.XStep = (float)full_width;
482
168
    templat.YStep = (float)full_height;
483
168
    templat.PaintProc = px_remap_pattern;
484
168
    {
485
168
        gs_matrix mat;
486
168
        gs_point dsize;
487
168
        int code;
488
489
168
        if (porigin)
490
0
            gs_make_translation(real_value(porigin, 0),
491
0
                                real_value(porigin, 1), &mat);
492
168
        else
493
168
            gs_make_identity(&mat);
494
495
168
        if (pdsize) {
496
0
            dsize.x = real_value(pdsize, 0);
497
0
            dsize.y = real_value(pdsize, 1);
498
168
        } else {
499
168
            dsize.x = pattern->params.dest_width;
500
168
            dsize.y = pattern->params.dest_height;
501
168
        }
502
168
        gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height,
503
168
                        &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
168
        code = gs_gsave(pgs);
516
168
        if (code < 0)
517
0
            return code;
518
168
        {
519
168
            px_gstate_t *pxgs = pxs->pxgs;
520
521
168
            px_gstate_rc_adjust(pxgs, -1, pxgs->memory);
522
168
            pxgs->brush.type = pxgs->pen.type = pxpNull;
523
168
            gs_newpath(pgs);
524
168
            px_initclip(pxs);
525
168
        }
526
168
        {
527
168
            gs_color_space *pcs;
528
529
            /* set the color space */
530
168
            switch (pattern->params.color_space) {
531
168
                case eGray:
532
168
                    pcs = gs_cspace_new_DeviceGray(pxgs->memory);
533
168
                    if (pcs == NULL)
534
0
                        return_error(errorInsufficientMemory);
535
168
                    break;
536
168
                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
168
            }
545
168
            gs_setcolorspace(pgs, pcs);
546
168
        }
547
168
        code = gs_makepattern(pcc, &templat, &mat, pgs, NULL);
548
168
        pcc->pattern->client_data = (void *)pattern;
549
168
        gs_grestore(pgs);
550
168
        return code;
551
168
    }
552
168
}
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
13.1k
{
562
13.1k
    return
563
13.1k
        (type & pxd_ubyte ? 255 :
564
13.1k
         type & pxd_uint16 ? 65535 :
565
0
         type & pxd_sint16 ? 32767 : type & pxd_uint32 ? (ulong) 0xffffffff :
566
0
         /* type & pxd_sint32 */ 0x7fffffff);
567
13.1k
}
568
static real
569
fraction_value(const px_value_t * pv, int i)
570
3.62k
{
571
3.62k
    px_data_type_t type = pv->type;
572
3.62k
    real v;
573
574
3.62k
    if (type & pxd_real32)
575
0
        return pv->value.ra[i];
576
3.62k
    v = (real) pv->value.ia[i];
577
3.62k
    return (v < 0 ? 0 : v / int_type_max(type));
578
3.62k
}
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
13.5k
{
598
13.5k
    bool needs_halftone;
599
600
13.5k
    if (ppt->type == pxpPattern)
601
168
        needs_halftone = true;
602
13.3k
    else if (ppt->type == pxpNull)
603
6.53k
        needs_halftone = false;
604
6.81k
    else if (ppt->type == pxpGray)
605
3.62k
        needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1;
606
3.18k
    else if (ppt->type == pxpRGB || ppt->type == pxpSRGB) {     /* rgb or srgb */
607
3.18k
        int i;
608
609
3.18k
        needs_halftone = false;
610
8.24k
        for (i = 0; i < 3; i++) {
611
6.78k
            if (ppt->value.rgb[i] != 0 && ppt->value.rgb[i] != 1) {
612
1.71k
                needs_halftone = true;
613
1.71k
                break;
614
1.71k
            }
615
6.78k
        }
616
3.18k
    } else {
617
0
        dmprintf(mem, "unknown paint type\n");
618
0
        needs_halftone = true;
619
0
    }
620
13.5k
    return needs_halftone;
621
13.5k
}
622
623
static int
624
set_source(const px_args_t * par, px_state_t * pxs, px_paint_t * ppt)
625
13.5k
{
626
13.5k
    px_gstate_t *pxgs = pxs->pxgs;
627
13.5k
    int code = 0;
628
629
    /* pxaPatternSelectID */
630
13.5k
    if (par->pv[aPatternSelectID]) {
631
169
        px_value_t key;
632
169
        void *value;
633
169
        px_pattern_t *pattern;
634
169
        gs_client_color ccolor;
635
169
        int code;
636
637
169
        if (par->pv[aRGBColor] || par->pv[aGrayLevel]
638
169
            || par->pv[aNullBrushPen])
639
0
            return_error(errorIllegalAttributeCombination);
640
169
        key.type = pxd_array | pxd_ubyte;
641
169
        key.value.array.data = (byte *) & par->pv[aPatternSelectID]->value.i;
642
169
        key.value.array.size = sizeof(int32_t);
643
169
        if (!(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) ||
644
169
              px_dict_find(&pxs->page_pattern_dict, &key, &value) ||
645
169
              px_dict_find(&pxs->session_pattern_dict, &key, &value))
646
169
            )
647
1
            return_error(errorRasterPatternUndefined);
648
168
        pattern = value;
649
650
168
        px_set_halftone(pxs);
651
168
        code = render_pattern(&ccolor, pattern, par->pv[aPatternOrigin],
652
168
                              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
168
        rc_increment(pattern);
659
168
        if (code < 0)
660
0
            return code;
661
168
        px_paint_rc_adjust(ppt, -1, pxs->memory);
662
168
        ppt->type = pxpPattern;
663
168
        ppt->value.pattern.pattern = pattern;
664
168
        ppt->value.pattern.color = ccolor;
665
        /* not pxaPatternSelectID but we have a pattern origin or
666
           newdestination size */
667
13.3k
    } else if (par->pv[aPatternOrigin] || par->pv[aNewDestinationSize]) {
668
0
        return_error(errorIllegalAttributeCombination);
669
13.3k
    } else if (par->pv[aRGBColor]) {
670
3.18k
        const px_value_t *prgb = par->pv[aRGBColor];
671
3.18k
        int i;
672
673
3.18k
        if (par->pv[aGrayLevel] || par->pv[aNullBrushPen])
674
0
            return_error(errorIllegalAttributeCombination);
675
3.18k
        if (pxgs->color_space != eRGB && pxgs->color_space != eSRGB)
676
5
            return_error(errorColorSpaceMismatch);
677
3.18k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
678
3.18k
        ppt->type = pxpRGB;
679
680
12.7k
        for (i = 0; i < 3; ++i)
681
9.54k
            if (prgb->type & pxd_any_real)
682
0
                ppt->value.rgb[i] = real_elt(prgb, i);
683
9.54k
            else {
684
9.54k
                int32_t v = integer_elt(prgb, i);
685
686
9.54k
                ppt->value.rgb[i] =
687
9.54k
                    (v < 0 ? 0 : (real) v / int_type_max(prgb->type));
688
9.54k
            }
689
10.1k
    } else if (par->pv[aGrayLevel]) {   /* pxaGrayLevel */
690
3.62k
        if (par->pv[aNullBrushPen])
691
0
            return_error(errorIllegalAttributeCombination);
692
3.62k
        if (pxgs->color_space != eGray)
693
0
            return_error(errorColorSpaceMismatch);
694
3.62k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
695
3.62k
        ppt->type = pxpGray;
696
3.62k
        ppt->value.gray = fraction_value(par->pv[aGrayLevel], 0);
697
6.53k
    } else if (par->pv[aNullBrushPen]) {        /* pxaNullBrush/Pen */
698
6.53k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
699
6.53k
        ppt->type = pxpNull;
700
6.53k
    } 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
13.5k
    if (px_needs_halftone(pxs->memory, ppt))
734
1.88k
        code = px_set_halftone(pxs);
735
13.5k
    return code;
736
13.5k
}
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
11.1k
{
744
11.1k
    gs_gstate *pgs = pxs->pgs;
745
11.1k
    px_paint_type_t type;
746
747
11.1k
    type = ppt->type;
748
11.1k
    switch (type) {
749
0
        case pxpNull:
750
0
            return gs_setnullcolor(pgs);
751
7.85k
        case pxpRGB:
752
7.85k
            return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1],
753
7.85k
                                  ppt->value.rgb[2]);
754
3.16k
        case pxpGray:
755
3.16k
            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
11.1k
    }
766
11.1k
}
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
6.98k
{
779
6.98k
    return set_source(par, pxs, &pxs->pxgs->brush);
780
6.98k
}
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.19k
{
794
3.19k
    px_gstate_t *pxgs = pxs->pxgs;
795
3.19k
    pxeColorSpace_t cspace;
796
797
3.19k
    if (par->pv[0])
798
3.19k
        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.19k
    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.19k
    } else if (par->pv[6] || par->pv[7])
841
0
        return_error(errorMissingAttribute);
842
3.19k
    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.19k
    pxgs->palette_is_shared = false;
851
3.19k
    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.19k
    return 0;
865
3.19k
}
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.06k
{
876
2.06k
    gs_gstate *pgs = pxs->pgs;
877
2.06k
    px_gstate_t *pxgs = pxs->pxgs;
878
2.06k
    pxeDitherMatrix_t method;
879
880
2.06k
    if (par->pv[5] || par->pv[6] || par->pv[7] || par->pv[8])
881
        /* Placeholder to support halftones per object type. */
882
218
        ;
883
884
2.06k
    if (par->pv[1]) {           /* Internal halftone */
885
1.75k
        if (par->pv[2] || par->pv[3] || par->pv[4])
886
0
            return_error(errorIllegalAttributeCombination);
887
1.75k
        method = par->pv[1]->value.i;
888
1.75k
        px_set_default_screen_size(pxs, method);
889
1.75k
        pxs->download_string.data = 0;
890
1.75k
        pxs->download_string.size = 0;
891
1.75k
    } 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
301
        return 0;
970
1.75k
    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.75k
    else
974
1.75k
        gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin);
975
1.75k
    pxgs->halftone.thresholds = pxs->download_string;
976
1.75k
    pxgs->halftone.method = method;
977
1.75k
    pxgs->halftone.set = false;
978
1.75k
    return 0;
979
2.06k
}
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
6.53k
{
990
6.53k
    return set_source(par, pxs, &pxs->pxgs->pen);
991
6.53k
}
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
335
{
1000
335
    return 0;
1001
335
}
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
226
{
1010
226
    return 0;
1011
226
}
1012
1013
const byte apxSetColorTrapping[] = { pxaAllObjectTypes, 0, 0 };
1014
1015
int
1016
pxSetColorTrapping(px_args_t * par, px_state_t * pxs)
1017
225
{
1018
225
    return 0;
1019
225
}
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
225
{
1028
225
    return 0;
1029
225
}