Coverage Report

Created: 2026-07-24 07:44

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
572k
{
64
572k
    return tint;
65
572k
}
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
2.24k
{
152
2.24k
    px_gstate_t *pxgs = pxs->pxgs;
153
154
2.24k
    pxgs->halftone.width = pxgs->halftone.height = 16;
155
2.24k
}
156
157
/* If necessary, set the halftone in the graphics state. */
158
int
159
px_set_halftone(px_state_t * pxs)
160
4.80k
{
161
4.80k
    px_gstate_t *pxgs = pxs->pxgs;
162
4.80k
    int code;
163
164
4.80k
    if (pxgs->halftone.set)
165
2.57k
        return 0;
166
2.23k
    if (pxgs->halftone.method != eDownloaded) {
167
2.23k
        gs_string thresh;
168
169
2.23k
        thresh.data = (byte *) order16x16;
170
2.23k
        thresh.size = 256;
171
2.23k
        code = pl_set_pcl_halftone(pxs->pgs,
172
2.23k
                                   /* set transfer */ identity_transfer,
173
2.23k
                                   /* width */ 16, /*height */ 16,
174
2.23k
                                   /* dither data */ thresh,
175
2.23k
                                   /* x phase */ (int)pxgs->halftone.origin.x,
176
                                   /* y phase */
177
2.23k
                                   (int)pxgs->halftone.origin.y);
178
2.23k
    } 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
2.23k
    if (code < 0)
219
0
        return code;
220
2.23k
    pxgs->halftone.set = true;
221
    /* Cached patterns have already been halftoned, so clear the cache. */
222
2.23k
    px_purge_pattern_cache(pxs, eSessionPattern);
223
2.23k
    return 0;
224
2.23k
}
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
406
{
237
406
    return x * (y / igcd(x, y));
238
406
}
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
203
{
442
203
    px_gstate_t *pxgs = pxs->pxgs;
443
203
    uint rep_width = pattern->params.width;
444
203
    uint rep_height = pattern->params.height;
445
203
    uint full_width, full_height;
446
203
    gs_gstate *pgs = pxs->pgs;
447
203
    gs_client_pattern templat;
448
449
    /*
450
     * If halftoning may occur, replicate the pattern so we don't get
451
     * halftone seams.
452
     */
453
203
    {
454
203
        gx_device *dev = gs_currentdevice(pgs);
455
456
203
        if (!gx_device_must_halftone(dev)) {    /* No halftoning. */
457
0
            full_width = rep_width;
458
0
            full_height = rep_height;
459
203
        } else {
460
203
            full_width = ilcm(rep_width, pxgs->halftone.width);
461
203
            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
203
            if (full_width > 10000)
467
0
                full_width = rep_width;
468
203
            if (full_height > 10000)
469
0
                full_height = rep_height;
470
203
        }
471
203
    }
472
    /* Construct a Pattern for the library, and render it. */
473
203
    gs_pattern1_init(&templat);
474
203
    uid_set_UniqueID(&templat.uid, pattern->id);
475
203
    templat.PaintType = 1;
476
203
    templat.TilingType = 1;
477
203
    templat.BBox.p.x = 0;
478
203
    templat.BBox.p.y = 0;
479
203
    templat.BBox.q.x = full_width;
480
203
    templat.BBox.q.y = full_height;
481
203
    templat.XStep = (float)full_width;
482
203
    templat.YStep = (float)full_height;
483
203
    templat.PaintProc = px_remap_pattern;
484
203
    {
485
203
        gs_matrix mat;
486
203
        gs_point dsize;
487
203
        int code;
488
489
203
        if (porigin)
490
0
            gs_make_translation(real_value(porigin, 0),
491
0
                                real_value(porigin, 1), &mat);
492
203
        else
493
203
            gs_make_identity(&mat);
494
495
203
        if (pdsize) {
496
0
            dsize.x = real_value(pdsize, 0);
497
0
            dsize.y = real_value(pdsize, 1);
498
203
        } else {
499
203
            dsize.x = pattern->params.dest_width;
500
203
            dsize.y = pattern->params.dest_height;
501
203
        }
502
203
        gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height,
503
203
                        &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
203
        code = gs_gsave(pgs);
516
203
        if (code < 0)
517
0
            return code;
518
203
        {
519
203
            px_gstate_t *pxgs = pxs->pxgs;
520
521
203
            px_gstate_rc_adjust(pxgs, -1, pxgs->memory);
522
203
            pxgs->brush.type = pxgs->pen.type = pxpNull;
523
203
            gs_newpath(pgs);
524
203
            px_initclip(pxs);
525
203
        }
526
203
        {
527
203
            gs_color_space *pcs;
528
529
            /* set the color space */
530
203
            switch (pattern->params.color_space) {
531
203
                case eGray:
532
203
                    pcs = gs_cspace_new_DeviceGray(pxgs->memory);
533
203
                    if (pcs == NULL) {
534
0
                        gs_grestore(pgs);
535
0
                        return_error(errorInsufficientMemory);
536
0
                    }
537
203
                    break;
538
203
                case eRGB:
539
0
                case eSRGB:
540
0
                    pcs = gs_cspace_new_DeviceRGB(pxgs->memory);
541
0
                    if (pcs == NULL) {
542
0
                        gs_grestore(pgs);
543
0
                        return_error(errorInsufficientMemory);
544
0
                    }
545
0
                    break;
546
0
                default:
547
0
                    gs_grestore(pgs);
548
0
                    return_error(errorIllegalAttributeValue);
549
203
            }
550
203
            gs_setcolorspace(pgs, pcs);
551
203
        }
552
203
        code = gs_makepattern(pcc, &templat, &mat, pgs, NULL);
553
203
        pcc->pattern->client_data = (void *)pattern;
554
203
        gs_grestore(pgs);
555
203
        return code;
556
203
    }
557
203
}
558
559
/* ------ Brush/pen ------ */
560
561
/* Check parameters and execute SetBrushSource or SetPenSource: */
562
/* pxaRGBColor, pxaGrayLevel, pxaNullBrush/Pen, pxaPatternSelectID, */
563
/* pxaPatternOrigin, pxaNewDestinationSize */
564
static ulong
565
int_type_max(px_data_type_t type)
566
14.0k
{
567
14.0k
    return
568
14.0k
        (type & pxd_ubyte ? 255 :
569
14.0k
         type & pxd_uint16 ? 65535 :
570
0
         type & pxd_sint16 ? 32767 : type & pxd_uint32 ? (ulong) 0xffffffff :
571
0
         /* type & pxd_sint32 */ 0x7fffffff);
572
14.0k
}
573
static real
574
fraction_value(const px_value_t * pv, int i)
575
3.76k
{
576
3.76k
    px_data_type_t type = pv->type;
577
3.76k
    real v;
578
579
3.76k
    if (type & pxd_real32)
580
0
        return pv->value.ra[i];
581
3.76k
    v = (real) pv->value.ia[i];
582
3.76k
    return (v < 0 ? 0 : v / int_type_max(type));
583
3.76k
}
584
585
/* we use an enumeration instead of index numbers improve readability in this
586
   "very busy" routine */
587
typedef enum
588
{
589
    aRGBColor = 0,
590
    aGrayLevel = 1,
591
    aPrimaryArray = 2,
592
    aPrimaryDepth = 3,
593
    aNullBrushPen = 4,
594
    aPatternSelectID = 5,
595
    aPatternOrigin = 6,
596
    aNewDestinationSize = 7
597
} pxl_source_t;
598
599
/* given a paint type decide if a halftone is necessary */
600
static bool
601
px_needs_halftone(const gs_memory_t * mem, px_paint_t * ppt)
602
14.3k
{
603
14.3k
    bool needs_halftone;
604
605
14.3k
    if (ppt->type == pxpPattern)
606
203
        needs_halftone = true;
607
14.1k
    else if (ppt->type == pxpNull)
608
6.92k
        needs_halftone = false;
609
7.21k
    else if (ppt->type == pxpGray)
610
3.76k
        needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1;
611
3.44k
    else if (ppt->type == pxpRGB || ppt->type == pxpSRGB) {     /* rgb or srgb */
612
3.44k
        int i;
613
614
3.44k
        needs_halftone = false;
615
8.26k
        for (i = 0; i < 3; i++) {
616
6.95k
            if (ppt->value.rgb[i] != 0 && ppt->value.rgb[i] != 1) {
617
2.13k
                needs_halftone = true;
618
2.13k
                break;
619
2.13k
            }
620
6.95k
        }
621
3.44k
    } else {
622
0
        dmprintf(mem, "unknown paint type\n");
623
0
        needs_halftone = true;
624
0
    }
625
14.3k
    return needs_halftone;
626
14.3k
}
627
628
static int
629
set_source(const px_args_t * par, px_state_t * pxs, px_paint_t * ppt)
630
14.3k
{
631
14.3k
    px_gstate_t *pxgs = pxs->pxgs;
632
14.3k
    int code = 0;
633
634
    /* pxaPatternSelectID */
635
14.3k
    if (par->pv[aPatternSelectID]) {
636
204
        px_value_t key;
637
204
        void *value;
638
204
        px_pattern_t *pattern;
639
204
        gs_client_color ccolor;
640
204
        int code;
641
642
204
        if (par->pv[aRGBColor] || par->pv[aGrayLevel]
643
204
            || par->pv[aNullBrushPen])
644
0
            return_error(errorIllegalAttributeCombination);
645
204
        key.type = pxd_array | pxd_ubyte;
646
204
        key.value.array.data = (byte *) & par->pv[aPatternSelectID]->value.i;
647
204
        key.value.array.size = sizeof(int32_t);
648
204
        if (!(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) ||
649
204
              px_dict_find(&pxs->page_pattern_dict, &key, &value) ||
650
204
              px_dict_find(&pxs->session_pattern_dict, &key, &value))
651
204
            )
652
1
            return_error(errorRasterPatternUndefined);
653
203
        pattern = value;
654
655
203
        px_set_halftone(pxs);
656
203
        code = render_pattern(&ccolor, pattern, par->pv[aPatternOrigin],
657
203
                              par->pv[aNewDestinationSize], pxs);
658
        /*
659
         * We don't use px_paint_rc_adjust(... 1 ...) here, because
660
         * gs_makepattern creates pattern instances with a reference
661
         * count already set to 1.
662
         */
663
203
        if (code < 0)
664
0
            return code;
665
203
        px_paint_rc_adjust(ppt, -1, pxs->memory);
666
203
        ppt->type = pxpPattern;
667
203
        ppt->value.pattern.pattern = pattern;
668
203
        rc_increment(pattern);
669
203
        ppt->value.pattern.color = ccolor;
670
        /* not pxaPatternSelectID but we have a pattern origin or
671
           newdestination size */
672
14.1k
    } else if (par->pv[aPatternOrigin] || par->pv[aNewDestinationSize]) {
673
0
        return_error(errorIllegalAttributeCombination);
674
14.1k
    } else if (par->pv[aRGBColor]) {
675
3.44k
        const px_value_t *prgb = par->pv[aRGBColor];
676
3.44k
        int i;
677
678
3.44k
        if (par->pv[aGrayLevel] || par->pv[aNullBrushPen])
679
0
            return_error(errorIllegalAttributeCombination);
680
3.44k
        if (pxgs->color_space != eRGB && pxgs->color_space != eSRGB)
681
5
            return_error(errorColorSpaceMismatch);
682
3.44k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
683
3.44k
        ppt->type = pxpRGB;
684
685
13.7k
        for (i = 0; i < 3; ++i)
686
10.3k
            if (prgb->type & pxd_any_real)
687
0
                ppt->value.rgb[i] = real_elt(prgb, i);
688
10.3k
            else {
689
10.3k
                int32_t v = integer_elt(prgb, i);
690
691
10.3k
                ppt->value.rgb[i] =
692
10.3k
                    (v < 0 ? 0 : (real) v / int_type_max(prgb->type));
693
10.3k
            }
694
10.7k
    } else if (par->pv[aGrayLevel]) {   /* pxaGrayLevel */
695
3.76k
        if (par->pv[aNullBrushPen])
696
0
            return_error(errorIllegalAttributeCombination);
697
3.76k
        if (pxgs->color_space != eGray)
698
0
            return_error(errorColorSpaceMismatch);
699
3.76k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
700
3.76k
        ppt->type = pxpGray;
701
3.76k
        ppt->value.gray = fraction_value(par->pv[aGrayLevel], 0);
702
6.94k
    } else if (par->pv[aNullBrushPen]) {        /* pxaNullBrush/Pen */
703
6.92k
        px_paint_rc_adjust(ppt, -1, pxs->memory);
704
6.92k
        ppt->type = pxpNull;
705
6.92k
    } else if (par->pv[aPrimaryDepth] && par->pv[aPrimaryArray]) {
706
0
        px_paint_rc_adjust(ppt, -1, pxs->memory);
707
0
        if (pxgs->color_space == eRGB)
708
0
            ppt->type = pxpRGB;
709
0
        else if (pxgs->color_space == eGray)
710
0
            ppt->type = pxpGray;
711
0
        else if (pxgs->color_space == eSRGB)
712
0
            ppt->type = pxpSRGB;
713
0
        else {
714
0
            dmprintf1(pxgs->memory, "Warning unknown color space %d\n",
715
0
                      pxgs->color_space);
716
0
            ppt->type = pxpGray;
717
0
        }
718
        /* NB depth?? - for range checking */
719
0
        if (ppt->type == pxpRGB || ppt->type == pxpSRGB) {
720
0
            ppt->value.rgb[0] =
721
0
                (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0;
722
0
            ppt->value.rgb[1] =
723
0
                (float)par->pv[aPrimaryArray]->value.array.data[1] / 255.0;
724
0
            ppt->value.rgb[2] =
725
0
                (float)par->pv[aPrimaryArray]->value.array.data[2] / 255.0;
726
0
        } else
727
            /* NB figure out reals and ints */
728
0
            ppt->value.gray =
729
0
                (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0;
730
0
    } else
731
16
        return_error(errorMissingAttribute);
732
    /*
733
     * Update the halftone to the most recently set one.
734
     * This will do the wrong thing if we set the brush or pen source,
735
     * set the halftone, and then set the other source, but we have
736
     * no way to handle this properly with the current library.
737
     */
738
14.3k
    if (px_needs_halftone(pxs->memory, ppt))
739
2.36k
        code = px_set_halftone(pxs);
740
14.3k
    return code;
741
14.3k
}
742
743
/* Set up a brush or pen for drawing. */
744
/* If it is a pattern, SetBrush/PenSource guaranteed that it is compatible */
745
/* with the current color space. */
746
int
747
px_set_paint(const px_paint_t * ppt, px_state_t * pxs)
748
11.2k
{
749
11.2k
    gs_gstate *pgs = pxs->pgs;
750
11.2k
    px_paint_type_t type;
751
752
11.2k
    type = ppt->type;
753
11.2k
    switch (type) {
754
0
        case pxpNull:
755
0
            return gs_setnullcolor(pgs);
756
7.99k
        case pxpRGB:
757
7.99k
            return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1],
758
7.99k
                                  ppt->value.rgb[2]);
759
3.18k
        case pxpGray:
760
3.18k
            return gs_setgray(pgs, ppt->value.gray);
761
84
        case pxpPattern:
762
84
            return gs_setpattern(pgs, &ppt->value.pattern.color);
763
0
        case pxpSRGB:
764
0
            return gs_setrgbcolor(pgs,
765
0
                                  ppt->value.rgb[0],
766
0
                                  ppt->value.rgb[1],
767
0
                                  ppt->value.rgb[2]);
768
0
        default:               /* can't happen */
769
0
            return_error(errorIllegalAttributeValue);
770
11.2k
    }
771
11.2k
}
772
773
/* ---------------- Operators ---------------- */
774
775
const byte apxSetBrushSource[] = {
776
    0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth,
777
        pxaNullBrush, pxaPatternSelectID,
778
    pxaPatternOrigin, pxaNewDestinationSize, 0
779
};
780
781
int
782
pxSetBrushSource(px_args_t * par, px_state_t * pxs)
783
7.41k
{
784
7.41k
    return set_source(par, pxs, &pxs->pxgs->brush);
785
7.41k
}
786
787
const byte apxSetColorSpace[] = {
788
    0, pxaColorSpace, pxaColorimetricColorSpace, pxaXYChromaticities,
789
        pxaWhiteReferencePoint,
790
    pxaCRGBMinMax, pxaGammaGain, pxaPaletteDepth, pxaPaletteData, 0
791
};
792
793
/* it appears the 4600 does not support CRGB define this to enable support */
794
/* #define SUPPORT_COLORIMETRIC */
795
796
int
797
pxSetColorSpace(px_args_t * par, px_state_t * pxs)
798
3.45k
{
799
3.45k
    px_gstate_t *pxgs = pxs->pxgs;
800
3.45k
    pxeColorSpace_t cspace;
801
802
3.45k
    if (par->pv[0])
803
3.45k
        cspace = par->pv[0]->value.i;
804
1
    else if (par->pv[1])
805
0
        cspace = par->pv[1]->value.i;
806
1
    else
807
1
        return_error(errorIllegalAttributeValue);
808
809
3.45k
    if (par->pv[6] && par->pv[7]) {
810
0
        int ncomp =
811
0
            ((cspace == eRGB || cspace == eSRGB) ? 3 : 1);
812
0
        uint size = par->pv[7]->value.array.size;
813
814
0
        if (!(size == ncomp << 1 || size == ncomp << 4 || size == ncomp << 8)
815
0
            ) {
816
            /* The HP printers we've tested appear to truncate
817
               this value and not produce an error on overflow */
818
0
            if (size > ncomp << 8)
819
0
                size = ncomp << 8;
820
0
            else
821
0
                return_error(errorIllegalAttributeValue);
822
0
        }
823
        /* The palette is in an array, but we want a string. */
824
0
        {
825
0
            if (pxgs->palette.data && !pxgs->palette_is_shared &&
826
0
                pxgs->palette.size != size) {
827
0
                gs_free_string(pxs->memory, (byte *) pxgs->palette.data,
828
0
                               pxgs->palette.size,
829
0
                               "pxSetColorSpace(old palette)");
830
0
                pxgs->palette.data = 0;
831
0
                pxgs->palette.size = 0;
832
0
            }
833
0
            if (pxgs->palette.data == 0 || pxgs->palette_is_shared) {
834
0
                byte *pdata = gs_alloc_string(pxs->memory, size,
835
0
                                              "pxSetColorSpace(palette)");
836
837
0
                if (pdata == 0)
838
0
                    return_error(errorInsufficientMemory);
839
0
                pxgs->palette.data = pdata;
840
0
                pxgs->palette.size = size;
841
0
            }
842
0
            memcpy((void *)pxgs->palette.data, par->pv[7]->value.array.data,
843
0
                   size);
844
0
        }
845
3.45k
    } else if (par->pv[6] || par->pv[7])
846
0
        return_error(errorMissingAttribute);
847
3.45k
    else if (pxgs->palette.data) {
848
0
        if (!pxgs->palette_is_shared)
849
0
            gs_free_string(pxs->memory, (byte *) pxgs->palette.data,
850
0
                           pxgs->palette.size,
851
0
                           "pxSetColorSpace(old palette)");
852
0
        pxgs->palette.data = 0;
853
0
        pxgs->palette.size = 0;
854
0
    }
855
3.45k
    pxgs->palette_is_shared = false;
856
3.45k
    pxgs->color_space = cspace;
857
#ifndef SET_COLOR_SPACE_NO_SET_BLACK
858
    {
859
        px_paint_rc_adjust(&pxgs->brush, -1, pxs->memory);
860
        pxgs->brush.type = pxpGray;
861
        pxgs->brush.value.gray = 0;
862
    }
863
    {
864
        px_paint_rc_adjust(&pxgs->pen, -1, pxs->memory);
865
        pxgs->pen.type = pxpGray;
866
        pxgs->pen.value.gray = 0;
867
    }
868
#endif
869
3.45k
    return 0;
870
3.45k
}
871
872
const byte apxSetHalftoneMethod[] = {
873
    0, pxaDitherOrigin, pxaDeviceMatrix, pxaDitherMatrixDataType,
874
    pxaDitherMatrixSize, pxaDitherMatrixDepth, pxaAllObjectTypes,
875
    pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0
876
};
877
878
int
879
pxSetHalftoneMethod(px_args_t * par, px_state_t * pxs)
880
2.49k
{
881
2.49k
    gs_gstate *pgs = pxs->pgs;
882
2.49k
    px_gstate_t *pxgs = pxs->pxgs;
883
2.49k
    pxeDitherMatrix_t method;
884
885
2.49k
    if (par->pv[5] || par->pv[6] || par->pv[7] || par->pv[8])
886
        /* Placeholder to support halftones per object type. */
887
222
        ;
888
889
2.49k
    if (par->pv[1]) {           /* Internal halftone */
890
2.24k
        if (par->pv[2] || par->pv[3] || par->pv[4])
891
0
            return_error(errorIllegalAttributeCombination);
892
2.24k
        method = par->pv[1]->value.i;
893
2.24k
        px_set_default_screen_size(pxs, method);
894
2.24k
        pxs->download_string.data = 0;
895
2.24k
        pxs->download_string.size = 0;
896
2.24k
    } else if (par->pv[2] && par->pv[3] && par->pv[4]) {        /* Dither matrix */
897
0
        uint width = par->pv[3]->value.ia[0];
898
0
        uint source_width = (width + 3) & ~3;
899
0
        uint height = par->pv[3]->value.ia[1];
900
0
        uint size = width * height;
901
0
        uint source_size = source_width * height;
902
903
0
        if (par->source.position == 0) {
904
0
            byte *data;
905
906
0
            if (par->source.available == 0)
907
0
                return pxNeedData;
908
0
            data = gs_alloc_string(pxs->memory, size, "dither matrix");
909
0
            if (data == 0)
910
0
                return_error(errorInsufficientMemory);
911
0
            pxs->download_string.data = data;
912
0
            pxs->download_string.size = size;
913
0
        }
914
0
        while (par->source.position < source_size) {
915
0
            uint source_x = par->source.position % source_width;
916
0
            uint source_y = par->source.position / source_width;
917
0
            uint used;
918
919
0
            if (par->source.available == 0)
920
0
                return pxNeedData;
921
0
            if (source_x >= width) {    /* Skip padding bytes at end of row. */
922
0
                used = min(par->source.available, source_width - source_x);
923
0
            } else {            /* Read data. */
924
0
                const byte *src = par->source.data;
925
0
                byte *dest = pxs->download_string.data;
926
0
                byte *pdata_min = dest;
927
0
                byte *pdata_max = pdata_min + pxs->download_string.size;
928
0
                uint i;
929
0
                int skip;
930
931
0
                used = min(par->source.available, width - source_x);
932
                /*
933
                 * The documentation doesn't say this, but we have to
934
                 * rotate the dither matrix to match the orientation,
935
                 * remembering that we have a Y-inverted coordinate
936
                 * system.  This is quite a nuisance!
937
                 */
938
0
                switch (pxs->orientation) {
939
0
                    case ePortraitOrientation:
940
0
                        dest += source_y * width + source_x;
941
0
                        skip = 1;
942
0
                        break;
943
0
                    case eLandscapeOrientation:
944
0
                        dest += (width - 1 - source_x) * height + source_y;
945
0
                        skip = -(int)height;
946
0
                        break;
947
0
                    case eReversePortrait:
948
0
                        dest += (height - 1 - source_y) * width +
949
0
                            width - 1 - source_x;
950
0
                        skip = -1;
951
0
                        break;
952
0
                    case eReverseLandscape:
953
0
                        dest += (source_x + 1) * height - source_y - 1;
954
0
                        skip = height;
955
0
                        break;
956
0
                    default:
957
0
                        return -1;
958
0
                }
959
0
                if ((dest < pdata_min) || (pdata_max < dest + ((used - 1) * (int64_t)skip)))
960
0
                    return_error(gs_error_rangecheck);
961
0
                for (i = 0; i < used; ++i, ++src, dest += skip) {
962
0
                    *dest = *src;
963
0
                }
964
0
            }
965
0
            par->source.position += used;
966
0
            par->source.available -= used;
967
0
            par->source.data += used;
968
969
0
        }
970
0
        pxgs->halftone.width = width;
971
0
        pxgs->halftone.height = height;
972
0
        method = eDownloaded;
973
0
    } else
974
252
        return 0;
975
2.24k
    if (par->pv[0])
976
0
        gs_transform(pgs, real_value(par->pv[0], 0),
977
0
                     real_value(par->pv[0], 1), &pxgs->halftone.origin);
978
2.24k
    else
979
2.24k
        gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin);
980
2.24k
    pxgs->halftone.thresholds = pxs->download_string;
981
2.24k
    pxgs->halftone.method = method;
982
2.24k
    pxgs->halftone.set = false;
983
2.24k
    return 0;
984
2.49k
}
985
986
const byte apxSetPenSource[] = {
987
    0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth,
988
        pxaNullPen, pxaPatternSelectID,
989
    pxaPatternOrigin, pxaNewDestinationSize, 0
990
};
991
992
int
993
pxSetPenSource(px_args_t * par, px_state_t * pxs)
994
6.94k
{
995
6.94k
    return set_source(par, pxs, &pxs->pxgs->pen);
996
6.94k
}
997
998
const byte apxSetColorTreatment[] =
999
    { 0, pxaColorTreatment, pxaAllObjectTypes, pxaTextObjects,
1000
pxaVectorObjects, pxaRasterObjects, 0 };
1001
1002
int
1003
pxSetColorTreatment(px_args_t * par, px_state_t * pxs)
1004
244
{
1005
244
    return 0;
1006
244
}
1007
1008
const byte apxSetNeutralAxis[] = { 0, pxaAllObjectTypes, pxaTextObjects,
1009
    pxaVectorObjects, pxaRasterObjects, 0
1010
};
1011
1012
int
1013
pxSetNeutralAxis(px_args_t * par, px_state_t * pxs)
1014
227
{
1015
227
    return 0;
1016
227
}
1017
1018
const byte apxSetColorTrapping[] = { pxaAllObjectTypes, 0, 0 };
1019
1020
int
1021
pxSetColorTrapping(px_args_t * par, px_state_t * pxs)
1022
224
{
1023
224
    return 0;
1024
224
}
1025
1026
const byte apxSetAdaptiveHalftoning[] =
1027
    { 0, pxaAllObjectTypes, pxaTextObjects, pxaVectorObjects,
1028
pxaRasterObjects, 0 };
1029
1030
int
1031
pxSetAdaptiveHalftoning(px_args_t * par, px_state_t * pxs)
1032
224
{
1033
224
    return 0;
1034
224
}