Coverage Report

Created: 2026-10-28 08:05

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