Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/devices/gdevpbm.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Portable Bit/Gray/PixMap drivers */
17
#include "gdevprn.h"
18
#include "gscdefs.h"
19
#include "gscspace.h" /* For pnm_begin_typed_image(..) */
20
#include "gxgetbit.h"
21
#include "gxlum.h"
22
#include "gxiparam.h" /* For pnm_begin_typed_image(..) */
23
#include "gdevmpla.h"
24
#include "gdevplnx.h"
25
#include "gdevppla.h"
26
27
/*
28
 * Thanks are due to Jos Vos (jos@bull.nl) for an earlier P*M driver,
29
 * on which this one is based; to Nigel Roles (ngr@cotswold.demon.co.uk),
30
 * for the plan9bm changes; and to Leon Bottou (leonb@research.att.com)
31
 * for the color detection code in pnm_begin_typed_image.
32
 */
33
34
/*
35
 * There are 8 (families of) drivers here, plus one less related one:
36
 *      pbm[raw] - outputs PBM (black and white).
37
 *      pgm[raw] - outputs PGM (gray-scale).
38
 *      pgnm[raw] - outputs PBM if the page contains only black and white,
39
 *        otherwise PGM.
40
 *      ppm[raw] - outputs PPM (RGB).
41
 *      pnm[raw] - outputs PBM if the page contains only black and white,
42
 *        otherwise PGM if the page contains only gray shades,
43
 *        otherwise PPM.
44
 *        If GrayDetection is true, then the pageneutral color is used to decide
45
          between PGM and PPM.
46
 *      pkm[raw] - computes internally in CMYK, outputs PPM (RGB).
47
 *      pksm[raw] - computes internally in CMYK, outputs 4 PBM pages.
48
 *      pamcmyk4 - outputs CMYK as PAM 1-bit per color
49
 *      pamcmyk32 - outputs CMYK as PAM 8-bits per color
50
 *      pnmcmyk - With GrayDetection true, outputs either the 8-bit K plane as PGM or
51
 *                32-bit CMYK (pam 8-bit per component) depending on pageneutralcolor.
52
 *      pam - previous name for the pamcmyk32 device retained for backwards compatibility
53
 *      plan9bm - outputs Plan 9 bitmap format.
54
 */
55
56
/*
57
 * The code here is designed to work with variable depths for PGM and PPM.
58
 * The code will work with any of the values in brackets, but the
59
 * Ghostscript imager requires that depth be a power of 2 or be 24,
60
 * so the actual allowed values are more limited.
61
 *      pgm, pgnm: 1, 2, 4, 8, 16.  [1-16]
62
 *      pgmraw, pgnmraw: 1, 2, 4, 8.  [1-8]
63
 *      ppm, pnm: 4(3x1), 8(3x2), 16(3x5), 24(3x8), 32(3x10).  [3-32]
64
 *      ppmraw, pnmraw: 4(3x1), 8(3x2), 16(3x5), 24(3x8).  [3-24]
65
 *      pkm, pkmraw: 4(4x1), 8(4x2), 16(4x4), 32(4x8).  [4-32]
66
 *      pksm, pksmraw: ibid.
67
 *      pam: 32 (CMYK), 4 (CMYK)
68
 */
69
70
/* Structure for P*M devices, which extend the generic printer device. */
71
72
#define MAX_COMMENT 70          /* max user-supplied comment */
73
struct gx_device_pbm_s {
74
    gx_device_common;
75
    gx_prn_device_common;
76
    /* Additional state for P*M devices */
77
    char magic;                 /* n for "Pn" */
78
    char comment[MAX_COMMENT + 1];      /* comment for head of file */
79
    byte is_raw;                /* 1 if raw format, 0 if plain */
80
    byte optimize;              /* 1 if optimization OK, 0 if not */
81
    byte uses_color;            /* 0 if image is black and white, */
82
                                /* 1 if gray (PGM or PPM only), */
83
                                /* 2 or 3 if colored (PPM only) */
84
    bool UsePlanarBuffer;       /* 0 if chunky buffer, 1 if planar */
85
    dev_proc_copy_alpha((*save_copy_alpha));
86
    dev_proc_begin_typed_image((*save_begin_typed_image));
87
};
88
typedef struct gx_device_pbm_s gx_device_pbm;
89
90
/* ------ The device descriptors ------ */
91
92
/*
93
 * Default X and Y resolution.
94
 */
95
#define X_DPI 72
96
#define Y_DPI 72
97
98
/* Macro for generating P*M device descriptors. */
99
#define pbm_prn_device(procs, dev_name, magic, is_raw, num_comp, depth, max_gray, max_rgb, optimize, x_dpi, y_dpi, print_page)\
100
{       prn_device_body(gx_device_pbm, procs, dev_name,\
101
          DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
102
          0, 0, 0, 0,\
103
          num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
104
          print_page),\
105
        magic,\
106
         { 0 },\
107
        is_raw,\
108
        optimize,\
109
        0, 0, 0\
110
}
111
112
/* For all but PBM, we need our own color mapping and alpha procedures. */
113
static dev_proc_encode_color(pgm_encode_color);
114
static dev_proc_encode_color(pnm_encode_color);
115
static dev_proc_decode_color(pgm_decode_color);
116
static dev_proc_decode_color(ppm_decode_color);
117
static dev_proc_map_cmyk_color(pkm_map_cmyk_color);
118
static dev_proc_map_color_rgb(pkm_map_color_rgb);
119
static dev_proc_get_params(ppm_get_params);
120
static dev_proc_put_params(ppm_put_params);
121
static dev_proc_copy_alpha(pnm_copy_alpha);
122
static dev_proc_begin_typed_image(pnm_begin_typed_image);
123
124
/* We need to initialize uses_color when opening the device, */
125
/* and after each showpage. */
126
static dev_proc_open_device(ppm_open);
127
static dev_proc_open_device(pnmcmyk_open);
128
static dev_proc_output_page(ppm_output_page);
129
130
/* And of course we need our own print-page routines. */
131
static dev_proc_print_page(pbm_print_page);
132
static dev_proc_print_page(pgm_print_page);
133
static dev_proc_print_page(ppm_print_page);
134
static dev_proc_print_page(pkm_print_page);
135
static dev_proc_print_page(psm_print_page);
136
static dev_proc_print_page(psm_print_page);
137
static dev_proc_print_page(pam_print_page);
138
static dev_proc_print_page(pam4_print_page);
139
static dev_proc_print_page(pnmcmyk_print_page);
140
141
/* The device procedures */
142
143
static void
144
pbm_initialize_device_procs(gx_device *dev)
145
9.83k
{
146
9.83k
    gdev_prn_initialize_device_procs_mono(dev);
147
148
9.83k
    set_dev_proc(dev, encode_color, gx_default_b_w_mono_encode_color);
149
9.83k
    set_dev_proc(dev, decode_color, gx_default_b_w_mono_decode_color);
150
9.83k
    set_dev_proc(dev, put_params, ppm_put_params);
151
9.83k
    set_dev_proc(dev, output_page, ppm_output_page);
152
9.83k
}
153
154
static void
155
ppm_initialize_device_procs(gx_device *dev)
156
0
{
157
0
    pbm_initialize_device_procs(dev);
158
159
0
    set_dev_proc(dev, get_params, ppm_get_params);
160
0
    set_dev_proc(dev, map_rgb_color, gx_default_rgb_map_rgb_color);
161
0
    set_dev_proc(dev, map_color_rgb, ppm_decode_color);
162
0
    set_dev_proc(dev, encode_color, gx_default_rgb_map_rgb_color);
163
0
    set_dev_proc(dev, decode_color, ppm_decode_color);
164
0
    set_dev_proc(dev, open_device, ppm_open);
165
0
}
166
167
static void
168
pgm_initialize_device_procs(gx_device *dev)
169
9.83k
{
170
9.83k
    pbm_initialize_device_procs(dev);
171
172
9.83k
    set_dev_proc(dev, map_rgb_color, pgm_encode_color);
173
9.83k
    set_dev_proc(dev, map_color_rgb, pgm_decode_color);
174
9.83k
    set_dev_proc(dev, encode_color, pgm_encode_color);
175
9.83k
    set_dev_proc(dev, decode_color, pgm_decode_color);
176
9.83k
    set_dev_proc(dev, open_device, ppm_open);
177
9.83k
}
178
179
static void
180
pnm_initialize_device_procs(gx_device *dev)
181
0
{
182
0
    ppm_initialize_device_procs(dev);
183
184
0
    set_dev_proc(dev, encode_color, pnm_encode_color);
185
0
    set_dev_proc(dev, decode_color, ppm_decode_color);
186
0
}
187
188
static void
189
pkm_initialize_device_procs(gx_device *dev)
190
0
{
191
0
    ppm_initialize_device_procs(dev);
192
193
0
    set_dev_proc(dev, map_rgb_color, NULL);
194
0
    set_dev_proc(dev, decode_color, cmyk_1bit_map_color_rgb);
195
0
    set_dev_proc(dev, encode_color, cmyk_1bit_map_cmyk_color);
196
0
}
197
198
static void
199
pam32_initialize_device_procs(gx_device *dev)
200
0
{
201
0
    ppm_initialize_device_procs(dev);
202
203
0
    set_dev_proc(dev, map_rgb_color, NULL);
204
0
    set_dev_proc(dev, map_color_rgb, cmyk_8bit_map_color_rgb);
205
0
    set_dev_proc(dev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
206
0
    set_dev_proc(dev, decode_color, cmyk_8bit_map_color_cmyk);
207
0
    set_dev_proc(dev, encode_color, cmyk_8bit_map_cmyk_color);
208
0
}
209
210
static void
211
pam4_initialize_device_procs(gx_device *dev)
212
0
{
213
0
    ppm_initialize_device_procs(dev);
214
215
0
    set_dev_proc(dev, map_rgb_color, NULL);
216
0
    set_dev_proc(dev, map_color_rgb, NULL);
217
0
    set_dev_proc(dev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
218
0
    set_dev_proc(dev, decode_color, cmyk_1bit_map_color_cmyk);
219
0
    set_dev_proc(dev, encode_color, cmyk_1bit_map_cmyk_color);
220
0
}
221
222
static void
223
pnmcmyk_initialize_device_procs(gx_device *dev)
224
0
{
225
0
    pam32_initialize_device_procs(dev);
226
227
0
    set_dev_proc(dev, open_device, pnmcmyk_open);
228
0
}
229
230
/* The device descriptors themselves */
231
const gx_device_pbm gs_pbm_device =
232
pbm_prn_device(pbm_initialize_device_procs, "pbm", '1', 0, 1, 1, 1, 0, 0,
233
               X_DPI, Y_DPI, pbm_print_page);
234
const gx_device_pbm gs_pbmraw_device =
235
pbm_prn_device(pbm_initialize_device_procs, "pbmraw", '4', 1, 1, 1, 1, 1, 0,
236
               X_DPI, Y_DPI, pbm_print_page);
237
const gx_device_pbm gs_pgm_device =
238
pbm_prn_device(pgm_initialize_device_procs, "pgm", '2', 0, 1, 8, 255, 0, 0,
239
               X_DPI, Y_DPI, pgm_print_page);
240
const gx_device_pbm gs_pgmraw_device =
241
pbm_prn_device(pgm_initialize_device_procs, "pgmraw", '5', 1, 1, 8, 255, 0, 0,
242
               X_DPI, Y_DPI, pgm_print_page);
243
const gx_device_pbm gs_pgnm_device =
244
pbm_prn_device(pgm_initialize_device_procs, "pgnm", '2', 0, 1, 8, 255, 0, 1,
245
               X_DPI, Y_DPI, pgm_print_page);
246
const gx_device_pbm gs_pgnmraw_device =
247
pbm_prn_device(pgm_initialize_device_procs, "pgnmraw", '5', 1, 1, 8, 255, 0, 1,
248
               X_DPI, Y_DPI, pgm_print_page);
249
const gx_device_pbm gs_ppm_device =
250
pbm_prn_device(ppm_initialize_device_procs, "ppm", '3', 0, 3, 24, 255, 255, 0,
251
               X_DPI, Y_DPI, ppm_print_page);
252
const gx_device_pbm gs_ppmraw_device =
253
pbm_prn_device(ppm_initialize_device_procs, "ppmraw", '6', 1, 3, 24, 255, 255, 0,
254
               X_DPI, Y_DPI, ppm_print_page);
255
const gx_device_pbm gs_pnm_device =
256
pbm_prn_device(pnm_initialize_device_procs, "pnm", '3', 0, 3, 24, 255, 255, 1,
257
               X_DPI, Y_DPI, ppm_print_page);
258
const gx_device_pbm gs_pnmraw_device =
259
pbm_prn_device(pnm_initialize_device_procs, "pnmraw", '6', 1, 3, 24, 255, 255, 1,
260
               X_DPI, Y_DPI, ppm_print_page);
261
const gx_device_pbm gs_pkm_device =
262
pbm_prn_device(pkm_initialize_device_procs, "pkm", '3', 0, 4, 4, 1, 1, 0,
263
               X_DPI, Y_DPI, pkm_print_page);
264
const gx_device_pbm gs_pkmraw_device =
265
pbm_prn_device(pkm_initialize_device_procs, "pkmraw", '6', 1, 4, 4, 1, 1, 0,
266
               X_DPI, Y_DPI, pkm_print_page);
267
const gx_device_pbm gs_pksm_device =
268
pbm_prn_device(pkm_initialize_device_procs, "pksm", '1', 0, 4, 4, 1, 1, 0,
269
               X_DPI, Y_DPI, psm_print_page);
270
const gx_device_pbm gs_pksmraw_device =
271
pbm_prn_device(pkm_initialize_device_procs, "pksmraw", '4', 1, 4, 4, 1, 1, 0,
272
               X_DPI, Y_DPI, psm_print_page);
273
const gx_device_pbm gs_pamcmyk32_device =
274
pbm_prn_device(pam32_initialize_device_procs, "pamcmyk32", '7', 1, 4, 32, 255, 255, 0,
275
               X_DPI, Y_DPI, pam_print_page);
276
const gx_device_pbm gs_pnmcmyk_device =
277
pbm_prn_device(pnmcmyk_initialize_device_procs, "pnmcmyk", '7', 1, 4, 32, 255, 255, 0, /* optimize false since this relies on GrayDetection */
278
               X_DPI, Y_DPI, pnmcmyk_print_page); /* May output PGM, magic = 5 */
279
const gx_device_pbm gs_pamcmyk4_device =
280
pbm_prn_device(pam4_initialize_device_procs, "pamcmyk4", '7', 1, 4, 4, 1, 1, 0,
281
               X_DPI, Y_DPI, pam4_print_page);
282
/* Also keep the old device name so anyone using it won't be surprised */
283
const gx_device_pbm gs_pam_device =
284
pbm_prn_device(pam32_initialize_device_procs, "pam", '7', 1, 4, 32, 255, 255, 0,
285
               X_DPI, Y_DPI, pam_print_page);
286
287
/* Plan 9 bitmaps default to 100 dpi. */
288
const gx_device_pbm gs_plan9bm_device =
289
pbm_prn_device(pbm_initialize_device_procs, "plan9bm", '9', 1, 1, 1, 1, 1, 1,
290
               100, 100, pbm_print_page);
291
292
/* ------ Initialization ------ */
293
294
/* Set the copy_alpha and color mapping procedures if necessary. */
295
static void
296
ppm_set_dev_procs(gx_device * pdev)
297
82.6k
{
298
82.6k
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
299
300
82.6k
    if (dev_proc(pdev, copy_alpha) != pnm_copy_alpha) {
301
31.1k
        bdev->save_copy_alpha = dev_proc(pdev, copy_alpha);
302
31.1k
        if (pdev->color_info.depth > 4)
303
31.1k
            set_dev_proc(pdev, copy_alpha, pnm_copy_alpha);
304
31.1k
    }
305
82.6k
    if (dev_proc(pdev, begin_typed_image) != pnm_begin_typed_image) {
306
31.1k
        bdev->save_begin_typed_image = dev_proc(pdev, begin_typed_image);
307
31.1k
        set_dev_proc(pdev, begin_typed_image, pnm_begin_typed_image);
308
31.1k
    }
309
82.6k
    if (bdev->color_info.num_components == 4) {
310
0
        if (bdev->color_info.depth == 4) {
311
0
            set_dev_proc(pdev, map_color_rgb, cmyk_1bit_map_color_rgb);
312
0
            set_dev_proc(pdev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
313
0
        } else if (bdev->magic == '7') {
314
0
            set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
315
0
            set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
316
0
        } else {
317
0
            set_dev_proc(pdev, map_color_rgb, pkm_map_color_rgb);
318
0
            set_dev_proc(pdev, map_cmyk_color, pkm_map_cmyk_color);
319
0
        }
320
0
    }
321
82.6k
}
322
323
/*
324
 * Define a special open procedure that changes create_buf_device to use
325
 * a planar device.
326
 */
327
328
static int
329
ppm_open(gx_device * pdev)
330
9.83k
{
331
9.83k
    gx_device_pbm * bdev = (gx_device_pbm *)pdev;
332
9.83k
    int code;
333
334
#ifdef TEST_PAD_AND_ALIGN
335
    pdev->pad = 5;
336
    pdev->log2_align_mod = 6;
337
#endif
338
339
9.83k
    code = gdev_prn_open_planar(pdev, bdev->UsePlanarBuffer ? pdev->color_info.num_components : 0);
340
9.83k
    while (pdev->child)
341
0
        pdev = pdev->child;
342
343
9.83k
    bdev = (gx_device_pbm *)pdev;;
344
345
9.83k
    if (code < 0)
346
0
        return code;
347
9.83k
    pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
348
9.83k
    set_linear_color_bits_mask_shift(pdev);
349
9.83k
    bdev->uses_color = 0;
350
9.83k
    ppm_set_dev_procs(pdev);
351
9.83k
    return code;
352
9.83k
}
353
354
/*
355
 * For pnmcmyk, we set GrayDection true by default. This may be overridden by
356
 * the default_put_params, but that's OK.
357
 */
358
static int
359
pnmcmyk_open(gx_device *pdev)
360
0
{
361
0
    pdev->icc_struct->graydetection = true;
362
0
    pdev->icc_struct->pageneutralcolor = true;   /* enable detection */
363
364
0
    return ppm_open(pdev);
365
0
}
366
367
/* Print a page, and reset uses_color if this is a showpage. */
368
static int
369
ppm_output_page(gx_device * pdev, int num_copies, int flush)
370
8.47k
{
371
    /* Safe to start the page in the background */
372
8.47k
    int code = gdev_prn_bg_output_page(pdev, num_copies, flush);
373
8.47k
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
374
375
8.47k
    if (code < 0)
376
1
        return code;
377
8.47k
    if (flush)
378
8.47k
        bdev->uses_color = 0;
379
8.47k
    return code;
380
8.47k
}
381
382
/* ------ Color mapping routines ------ */
383
384
/* Map an RGB color to a PGM gray value. */
385
/* Keep track of whether the image is black-and-white or gray. */
386
static gx_color_index
387
pgm_encode_color(gx_device * pdev, const gx_color_value cv[])
388
47.3M
{
389
47.3M
    gx_color_value gray;
390
47.3M
    gray = cv[0] * pdev->color_info.max_gray / gx_max_color_value;
391
392
47.3M
    if (!(gray == 0 || gray == pdev->color_info.max_gray)) {
393
39.8M
        gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
394
395
39.8M
        bdev->uses_color = 1;
396
39.8M
    }
397
47.3M
    return gray;
398
47.3M
}
399
400
/* Map a PGM gray value back to an RGB color. */
401
static int
402
pgm_decode_color(gx_device * dev, gx_color_index color,
403
                  gx_color_value *pgray)
404
7.55M
{
405
7.55M
    gx_color_value gray =
406
7.55M
    color * gx_max_color_value / dev->color_info.max_gray;
407
408
7.55M
    pgray[0] = gray;
409
7.55M
    return 0;
410
7.55M
}
411
412
/*
413
 * Pre gs8.00 version of RGB mapping for 24-bit true (RGB) color devices
414
 * It is kept here for backwards comparibility since the gs8.00 version
415
 * has changed in functionality.  The new one requires that the device be
416
 * 'separable'.  This routine is logically separable but does not require
417
 * the various color_info fields associated with separability (comp_shift,
418
 * comp_bits, and comp_mask) be setup.
419
 */
420
421
static gx_color_index
422
gx_old_default_rgb_map_rgb_color(gx_device * dev,
423
                       gx_color_value r, gx_color_value g, gx_color_value b)
424
0
{
425
0
    if (dev->color_info.depth == 24)
426
0
        return gx_color_value_to_byte(b) +
427
0
            ((uint) gx_color_value_to_byte(g) << 8) +
428
0
            ((ulong) gx_color_value_to_byte(r) << 16);
429
0
    else {
430
0
        int bpc = dev->color_info.depth / 3;
431
0
        int drop = sizeof(gx_color_value) * 8 - bpc;
432
433
0
        return (((((gx_color_index)(r >> drop)) << bpc) + (g >> drop)) << bpc) + (b >> drop);
434
0
    }
435
0
}
436
437
/* Map an RGB color to a PPM color tuple. */
438
/* Keep track of whether the image is black-and-white, gray, or colored. */
439
static gx_color_index
440
pnm_encode_color(gx_device * pdev, const gx_color_value cv[])
441
0
{
442
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
443
0
    gx_color_index color =
444
0
            gx_old_default_rgb_map_rgb_color(pdev, cv[0], cv[1], cv[2]);
445
0
    uint bpc = pdev->color_info.depth / 3;
446
0
    gx_color_index mask =
447
0
        ((gx_color_index)1 << (pdev->color_info.depth - bpc)) - 1;
448
0
    if (!(((color >> bpc) ^ color) & mask)) { /* gray shade */
449
0
        if (color != 0 && (~color & mask))
450
0
            bdev->uses_color |= 1;
451
0
    } else                      /* color */
452
0
        bdev->uses_color = 2;
453
0
    return color;
454
0
}
455
456
/* Map a PPM color tuple back to an RGB color. */
457
static int
458
ppm_decode_color(gx_device * dev, gx_color_index color,
459
                 gx_color_value prgb[])
460
0
{
461
0
    uint bitspercolor = dev->color_info.depth / 3;
462
0
    uint colormask = (1 << bitspercolor) - 1;
463
0
    uint max_rgb = dev->color_info.max_color;
464
465
0
    prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
466
0
        (ulong) gx_max_color_value / max_rgb;
467
0
    prgb[1] = ((color >> bitspercolor) & colormask) *
468
0
        (ulong) gx_max_color_value / max_rgb;
469
0
    prgb[2] = (color & colormask) *
470
0
        (ulong) gx_max_color_value / max_rgb;
471
0
    return 0;
472
0
}
473
474
/* Map a CMYK color to a pixel value. */
475
static gx_color_index
476
pkm_map_cmyk_color(gx_device * pdev, const gx_color_value cv[])
477
0
{
478
0
    uint bpc = pdev->color_info.depth >> 2;
479
0
    uint max_value = pdev->color_info.max_color;
480
0
    uint cc = cv[0] * max_value / gx_max_color_value;
481
0
    uint mc = cv[1] * max_value / gx_max_color_value;
482
0
    uint yc = cv[2] * max_value / gx_max_color_value;
483
0
    uint kc = cv[3] * max_value / gx_max_color_value;
484
0
    gx_color_index color =
485
0
        ((((((gx_color_index)cc << bpc) + mc) << bpc) + yc) << bpc) + kc;
486
487
0
    return (color == gx_no_color_index ? color ^ 1 : color);
488
0
}
489
490
/* Map a CMYK pixel value to RGB. */
491
static int
492
pkm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
493
0
{
494
0
    int bpc = dev->color_info.depth >> 2;
495
0
    gx_color_index cshift = color;
496
0
    uint mask = (1 << bpc) - 1;
497
0
    uint k = cshift & mask;
498
0
    uint y = (cshift >>= bpc) & mask;
499
0
    uint m = (cshift >>= bpc) & mask;
500
0
    uint c = cshift >> bpc;
501
0
    uint max_value = dev->color_info.max_color;
502
0
    uint not_k = max_value - k;
503
504
0
#define CVALUE(c)\
505
0
  ((gx_color_value)((ulong)(c) * gx_max_color_value / max_value))
506
        /* We use our improved conversion rule.... */
507
0
        rgb[0] = CVALUE((max_value - c) * not_k / max_value);
508
0
    rgb[1] = CVALUE((max_value - m) * not_k / max_value);
509
0
    rgb[2] = CVALUE((max_value - y) * not_k / max_value);
510
0
#undef CVALUE
511
0
    return 0;
512
0
}
513
514
/* Augment get/put_params to add UsePlanarBuffer */
515
516
static int
517
ppm_get_params(gx_device * pdev, gs_param_list * plist)
518
0
{
519
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
520
0
    int code;
521
522
0
    code = gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
523
0
    if (code < 0) return code;
524
0
    code = param_write_null(plist, "OutputIntent");
525
0
    return code;
526
0
}
527
528
static int
529
ppm_put_params(gx_device * pdev, gs_param_list * plist)
530
72.8k
{
531
72.8k
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
532
72.8k
    gx_device_color_info save_info;
533
72.8k
    int ncomps = pdev->color_info.num_components;
534
72.8k
    int bpc = pdev->color_info.depth / ncomps;
535
72.8k
    int ecode = 0;
536
72.8k
    int code;
537
72.8k
    long v;
538
72.8k
    gs_param_string_array intent;
539
72.8k
    const char *vname;
540
541
72.8k
    if ((code = param_read_string_array(plist, "OutputIntent", &intent)) == 0) {
542
        /* This device does not use the OutputIntent parameter.
543
           We include this code just as a sample how to handle it.
544
           The PDF interpreter extracts OutputIntent from a PDF file and sends it to here,
545
           if a device includes it in the .getdeviceparams response.
546
           This device does include due to ppm_get_params implementation.
547
           ppm_put_params must handle it (and ingore it) against 'rangecheck'.
548
         */
549
0
        static const bool debug_print_OutputIntent = false;
550
551
0
        if (debug_print_OutputIntent) {
552
0
            int i, j;
553
554
0
            dmlprintf1(pdev->memory, "%d strings:\n", intent.size);
555
0
            for (i = 0; i < intent.size; i++) {
556
0
                const gs_param_string *s = &intent.data[i];
557
0
                dmlprintf2(pdev->memory, "  %d: size %d:", i, s->size);
558
0
                if (i < 4) {
559
0
                    for (j = 0; j < s->size; j++)
560
0
                        dmlprintf1(pdev->memory, "%c", s->data[j]);
561
0
                } else {
562
0
                    for (j = 0; j < s->size; j++)
563
0
                        dmlprintf1(pdev->memory, " %02x", s->data[j]);
564
0
                }
565
0
                dmlprintf(pdev->memory, "\n");
566
0
            }
567
0
        }
568
0
    }
569
72.8k
    save_info = pdev->color_info;
570
72.8k
    if ((code = param_read_long(plist, (vname = "GrayValues"), &v)) != 1 ||
571
72.8k
        (code = param_read_long(plist, (vname = "RedValues"), &v)) != 1 ||
572
72.8k
        (code = param_read_long(plist, (vname = "GreenValues"), &v)) != 1 ||
573
72.8k
        (code = param_read_long(plist, (vname = "BlueValues"), &v)) != 1
574
72.8k
        ) {
575
3.07k
        if (code < 0)
576
0
            ecode = code;
577
3.07k
        else if (v < 2 || v > (bdev->is_raw || ncomps > 1 ? 256 : 65536L))
578
0
            param_signal_error(plist, vname,
579
3.07k
                               ecode = gs_error_rangecheck);
580
3.07k
        else if (v == 2)
581
0
            bpc = 1;
582
3.07k
        else if (v <= 4)
583
0
            bpc = 2;
584
3.07k
        else if (v <= 16)
585
0
            bpc = 4;
586
3.07k
        else if (v <= 32 && ncomps == 3)
587
0
            bpc = 5;
588
3.07k
        else if (v <= 256)
589
3.07k
            bpc = 8;
590
0
        else
591
0
            bpc = 16;
592
3.07k
        if (ecode >= 0) {
593
3.07k
            static const byte depths[4][16] =
594
3.07k
            {
595
3.07k
                {1, 2, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 16},
596
3.07k
                {0},
597
3.07k
                {4, 8, 0, 16, 16, 0, 0, 24},
598
3.07k
                {4, 8, 0, 16, 0, 0, 0, 32},
599
3.07k
            };
600
601
3.07k
            pdev->color_info.depth = depths[ncomps - 1][bpc - 1];
602
3.07k
            pdev->color_info.max_gray = pdev->color_info.max_color =
603
3.07k
                (pdev->color_info.dither_grays =
604
3.07k
                 pdev->color_info.dither_colors = (int)v) - 1;
605
3.07k
        }
606
3.07k
    }
607
72.8k
    if ((code = ecode) < 0 ||
608
72.8k
        (code = gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer)) < 0
609
72.8k
        )
610
26
        pdev->color_info = save_info;
611
72.8k
    ppm_set_dev_procs(pdev);
612
72.8k
    return code;
613
72.8k
}
614
615
/* Copy an alpha map, noting whether we may generate some non-black/white */
616
/* colors through blending. */
617
static int
618
pnm_copy_alpha(gx_device * pdev, const byte * data, int data_x,
619
           int raster, gx_bitmap_id id, int x, int y, int width, int height,
620
               gx_color_index color, int depth)
621
0
{
622
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
623
624
0
    if (pdev->color_info.depth < 24 ||
625
0
        (color >> 8) == (color & 0xffff)
626
0
        )
627
0
        bdev->uses_color |= 1;
628
0
    else
629
0
        bdev->uses_color |= 2;
630
0
    return (*bdev->save_copy_alpha) (pdev, data, data_x, raster, id,
631
0
                                     x, y, width, height, color, depth);
632
0
}
633
634
/* Begin processing an image, noting whether we may generate some */
635
/* non-black/white colors in the process. */
636
static int
637
pnm_begin_typed_image(gx_device *dev,
638
                      const gs_gstate *pgs, const gs_matrix *pmat,
639
                      const gs_image_common_t *pim, const gs_int_rect *prect,
640
                      const gx_drawing_color *pdcolor,
641
                      const gx_clip_path *pcpath,
642
                      gs_memory_t *memory, gx_image_enum_common_t **pinfo)
643
79.5k
{
644
79.5k
    gx_device_pbm * const bdev = (gx_device_pbm *)dev;
645
79.5k
    bool has_gray_icc;
646
647
    /* Conservatively guesses whether this operation causes color usage
648
       that might not be otherwise captured by ppm_map_color_rgb. */
649
79.5k
    if (pim && pim->type) {
650
79.5k
        switch (pim->type->index) {
651
79.5k
        case 1: case 3: case 4: {
652
            /* Use colorspace to handle image types 1,3,4 */
653
79.5k
            const gs_pixel_image_t *pim1 = (const gs_pixel_image_t *)pim;
654
655
79.5k
            if (pim1->ColorSpace) {
656
6.20k
                has_gray_icc = false;
657
6.20k
                if (pim1->ColorSpace->cmm_icc_profile_data) {
658
5.92k
                    if (pim1->ColorSpace->cmm_icc_profile_data->num_comps == 1) {
659
2.82k
                        has_gray_icc = true;
660
2.82k
                    }
661
5.92k
                }
662
6.20k
                if (gs_color_space_get_index(pim1->ColorSpace) ==
663
6.20k
                            gs_color_space_index_DeviceGray || has_gray_icc) {
664
2.82k
                    if (pim1->BitsPerComponent > 1)
665
2.56k
                        bdev->uses_color |= 1;
666
2.82k
                } else
667
3.37k
                    bdev->uses_color = 2;
668
6.20k
            }
669
79.5k
            break;
670
79.5k
        }
671
0
        default:
672
            /* Conservatively handles other image types */
673
0
            bdev->uses_color = 2;
674
79.5k
        }
675
79.5k
    }
676
    /* Forward to saved routine */
677
79.5k
    return (*bdev->save_begin_typed_image)(dev, pgs, pmat, pim, prect,
678
79.5k
                                           pdcolor, pcpath, memory, pinfo);
679
79.5k
}
680
681
/* ------ Internal routines ------ */
682
683
/* NOP row processing function used when no output */
684
static int nop_row_proc(gx_device_printer *pdev, byte *data, int len, gp_file *f)
685
19.0M
{
686
19.0M
    return 0;
687
19.0M
}
688
689
/* Print a page using a given row printing routine. */
690
static int
691
pbm_print_page_loop(gx_device_printer * pdev, char magic, gp_file * pstream,
692
             int (*row_proc) (gx_device_printer *, byte *, int, gp_file *))
693
8.47k
{
694
8.47k
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
695
8.47k
    uint raster = gdev_prn_raster_chunky(pdev);
696
8.47k
    byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
697
8.47k
    int lnum = 0;
698
8.47k
    int code = 0;
699
8.47k
    int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
700
8.47k
        !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
701
702
8.47k
    if (data == 0)
703
0
        return_error(gs_error_VMerror);
704
8.47k
    if (!output_is_nul) {
705
        /* Hack.  This should be done in the callers.  */
706
0
        if (magic == '9') {
707
0
            if (gp_fprintf(pstream, "%11d %11d %11d %11d %11d ",
708
0
                0, 0, 0, pdev->width, pdev->height) < 0) {
709
0
                code = gs_note_error(gs_error_ioerror);
710
0
                goto punt;
711
0
            }
712
0
        } else if (magic == '7') {
713
0
            int ncomps = pdev->color_info.num_components;
714
0
            if (gp_fprintf(pstream, "P%c\n", magic) < 0) {
715
0
                code = gs_note_error(gs_error_ioerror);
716
0
                goto punt;
717
0
            }
718
0
            if (gp_fprintf(pstream, "WIDTH %d\n", pdev->width) < 0) {
719
0
                code = gs_note_error(gs_error_ioerror);
720
0
                goto punt;
721
0
            }
722
0
            if (gp_fprintf(pstream, "HEIGHT %d\n", pdev->height) < 0) {
723
0
                code = gs_note_error(gs_error_ioerror);
724
0
                goto punt;
725
0
            }
726
0
            if (gp_fprintf(pstream, "DEPTH %d\n", ncomps) < 0) {
727
0
                code = gs_note_error(gs_error_ioerror);
728
0
                goto punt;
729
0
            }
730
0
            if (gp_fprintf(pstream, "MAXVAL %d\n", 255) < 0) { /* force MAXVAL to 255 */
731
0
                code = gs_note_error(gs_error_ioerror);
732
0
                goto punt;
733
0
            }
734
0
            if (gp_fprintf(pstream, "TUPLTYPE %s\n",
735
0
                (ncomps == 4) ? "CMYK" :
736
0
                ((ncomps == 3) ? "RGB" : "GRAYSCALE")) < 0) {
737
0
                code = gs_note_error(gs_error_ioerror);
738
0
                goto punt;
739
0
            }
740
0
            if (bdev->comment[0]) {
741
0
                if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
742
0
                    code = gs_note_error(gs_error_ioerror);
743
0
                    goto punt;
744
0
                }
745
0
            } else {
746
0
                if (gp_fprintf(pstream, "# Image generated by %s\n", gs_product) < 0) {
747
0
                    code = gs_note_error(gs_error_ioerror);
748
0
                    goto punt;
749
0
                }
750
0
            }
751
0
            if (gp_fprintf(pstream, "ENDHDR\n") < 0) {
752
0
                 code = gs_note_error(gs_error_ioerror);
753
0
                 goto punt;
754
0
            }
755
0
        } else {
756
0
            if (gp_fprintf(pstream, "P%c\n", magic) < 0) {
757
0
                code = gs_note_error(gs_error_ioerror);
758
0
                goto punt;
759
0
            }
760
0
            if (bdev->comment[0]) {
761
0
                if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
762
0
                    code = gs_note_error(gs_error_ioerror);
763
0
                    goto punt;
764
0
                }
765
0
            } else {
766
0
                if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
767
0
                        gs_product, pdev->dname) < 0) {
768
0
                    code = gs_note_error(gs_error_ioerror);
769
0
                    goto punt;
770
0
                }
771
0
            }
772
0
            if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
773
0
                code = gs_note_error(gs_error_ioerror);
774
0
                goto punt;
775
0
            }
776
0
        }
777
0
        switch (magic) {
778
0
        case '1':               /* pbm */
779
0
        case '4':               /* pbmraw */
780
0
        case '7':               /* pam */
781
0
        case '9':               /* plan9bm */
782
0
            break;
783
0
        case '3':               /* pkm */
784
0
        case '6':               /* pkmraw */
785
0
            if (gp_fprintf(pstream, "%d\n", 255) < 0) {
786
0
                code = gs_note_error(gs_error_ioerror);
787
0
                goto punt;
788
0
            }
789
0
            break;
790
0
        default:
791
0
            if (gp_fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
792
0
                code = gs_note_error(gs_error_ioerror);
793
0
                goto punt;
794
0
            }
795
0
        }
796
0
    }
797
8.47k
    if (output_is_nul)
798
8.47k
        row_proc = nop_row_proc;
799
19.0M
    for (; lnum < pdev->height; lnum++) {
800
19.0M
        byte *row;
801
802
19.0M
        code = gdev_prn_get_bits(pdev, lnum, data, &row);
803
19.0M
        if (code < 0)
804
1
            break;
805
19.0M
        code = (*row_proc) (pdev, row, pdev->color_info.depth, pstream);
806
19.0M
        if (code < 0)
807
0
            break;
808
19.0M
    }
809
8.47k
  punt:
810
8.47k
    gs_free_object(pdev->memory, data, "pbm_print_page_loop");
811
8.47k
    return (code < 0 ? code : 0);
812
8.47k
}
813
814
/* ------ Individual page printing routines ------ */
815
816
/* Print a monobit page. */
817
static int
818
pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
819
              gp_file * pstream)
820
0
{
821
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
822
823
0
    if (bdev->is_raw) {
824
0
        uint n = (pdev->width + 7) >> 3;
825
826
0
        if (gp_fwrite(data, 1, n, pstream) != n)
827
0
            return_error(gs_error_ioerror);
828
0
    } else {
829
0
        byte *bp;
830
0
        uint x, mask;
831
832
0
        for (bp = data, x = 0, mask = 0x80; x < pdev->width;) {
833
0
            if (gp_fputc((*bp & mask ? '1' : '0'), pstream) == EOF)
834
0
                return_error(gs_error_ioerror);
835
0
            if (++x == pdev->width || !(x & 63)) {
836
0
                if (gp_fputc('\n', pstream) == EOF)
837
0
                    return_error(gs_error_ioerror);
838
0
            }
839
0
            if ((mask >>= 1) == 0)
840
0
                bp++, mask = 0x80;
841
0
        }
842
0
    }
843
0
    return 0;
844
0
}
845
static int
846
pbm_print_page(gx_device_printer * pdev, gp_file * pstream)
847
0
{
848
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
849
850
0
    return pbm_print_page_loop(pdev, bdev->magic, pstream, pbm_print_row);
851
0
}
852
853
/* Print a gray-mapped page. */
854
static int
855
pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
856
              gp_file * pstream)
857
0
{                               /* Note that bpp <= 8 for raw format, bpp <= 16 for plain. */
858
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
859
0
    uint mask = (1 << depth) - 1;
860
    /*
861
     * If we're writing planes for a CMYK device, we have 0 = white,
862
     * mask = black, which is the opposite of the pgm convention.
863
     */
864
0
    uint invert = (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE);
865
0
    byte *bp;
866
0
    uint x;
867
0
    int shift;
868
869
0
    if (bdev->is_raw && depth == 8) {
870
0
        if (invert) {
871
0
            for (bp = data, x = 0; x < pdev->width; bp++, x++) {
872
0
                if (gp_fputc((byte)~*bp, pstream) == EOF)
873
0
                    return_error(gs_error_ioerror);
874
0
            }
875
0
        } else {
876
0
            if (gp_fwrite(data, 1, pdev->width, pstream) != pdev->width)
877
0
                return_error(gs_error_ioerror);
878
0
        }
879
0
    } else
880
0
        for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
881
0
            uint pixel;
882
883
0
            if (shift < 0) {    /* bpp = 16 */
884
0
                pixel = ((uint) * bp << 8) + bp[1];
885
0
                bp += 2;
886
0
            } else {
887
0
                pixel = (*bp >> shift) & mask;
888
0
                if ((shift -= depth) < 0)
889
0
                    bp++, shift += 8;
890
0
            }
891
0
            ++x;
892
0
            pixel ^= invert;
893
0
            if (bdev->is_raw) {
894
0
                if (gp_fputc(pixel, pstream) == EOF)
895
0
                    return_error(gs_error_ioerror);
896
0
            } else {
897
0
                if (gp_fprintf(pstream, "%d%c", pixel,
898
0
                        (x == pdev->width || !(x & 15) ? '\n' : ' ')) < 0)
899
0
                    return_error(gs_error_ioerror);
900
0
            }
901
0
        }
902
0
    return 0;
903
0
}
904
static int
905
pxm_pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
906
                  gp_file * pstream)
907
0
{                               /* Compress a PGM or PPM row to a PBM row. */
908
    /* This doesn't have to be very fast. */
909
    /* Note that we have to invert the data as well. */
910
0
    int delta = (depth + 7) >> 3;
911
0
    byte *src = data + delta - 1;       /* always big-endian */
912
0
    byte *dest = data;
913
0
    int x;
914
0
    byte out_mask = 0x80;
915
0
    byte out = 0;
916
917
0
    if (depth >= 8) {           /* One or more bytes per source pixel. */
918
0
        for (x = 0; x < pdev->width; x++, src += delta) {
919
0
            if (!(*src & 1))
920
0
                out |= out_mask;
921
0
            out_mask >>= 1;
922
0
            if (!out_mask)
923
0
                out_mask = 0x80,
924
0
                    *dest++ = out,
925
0
                    out = 0;
926
0
        }
927
0
    } else {                    /* Multiple source pixels per byte. */
928
0
        byte in_mask = 0x100 >> depth;
929
930
0
        for (x = 0; x < pdev->width; x++) {
931
0
            if (!(*src & in_mask))
932
0
                out |= out_mask;
933
0
            in_mask >>= depth;
934
0
            if (!in_mask)
935
0
                in_mask = 0x100 >> depth,
936
0
                    src++;
937
0
            out_mask >>= 1;
938
0
            if (!out_mask)
939
0
                out_mask = 0x80,
940
0
                    *dest++ = out,
941
0
                    out = 0;
942
0
        }
943
0
    }
944
0
    if (out_mask != 0x80)
945
0
        *dest = out;
946
0
    return pbm_print_row(pdev, data, 1, pstream);
947
0
}
948
static int
949
pgm_print_page(gx_device_printer * pdev, gp_file * pstream)
950
8.47k
{
951
8.47k
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
952
953
8.47k
    return (bdev->uses_color == 0 && bdev->optimize ?
954
0
            pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
955
0
                                pxm_pbm_print_row) :
956
8.47k
            pbm_print_page_loop(pdev, bdev->magic, pstream,
957
8.47k
                                pgm_print_row));
958
8.47k
}
959
960
/* Print a color-mapped page. */
961
static int
962
ppgm_print_row(gx_device_printer * pdev, byte * data, int depth,
963
               gp_file * pstream, bool color)
964
0
{                               /* If color=false, write only one value per pixel; */
965
    /* if color=true, write 3 values per pixel. */
966
    /* Note that depth <= 24 for raw format, depth <= 32 for plain. */
967
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
968
0
    uint bpe = depth / 3;       /* bits per r/g/b element */
969
0
    uint mask = (1 << bpe) - 1;
970
0
    byte *bp;
971
0
    uint x;
972
0
    uint eol_mask = (color ? 7 : 15);
973
0
    int shift;
974
975
0
    if (bdev->is_raw && depth == 24 && color) {
976
0
        uint n = pdev->width * (depth / 8);
977
978
0
        if (gp_fwrite(data, 1, n, pstream) != n)
979
0
            return_error(gs_error_ioerror);
980
0
    } else {
981
0
        for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
982
0
            bits32 pixel = 0;
983
0
            uint r, g, b;
984
985
0
            switch (depth >> 3) {
986
0
                case 4:
987
0
                    pixel = (bits32) * bp << 24;
988
0
                    bp++;
989
                    /* falls through */
990
0
                case 3:
991
0
                    pixel += (bits32) * bp << 16;
992
0
                    bp++;
993
                    /* falls through */
994
0
                case 2:
995
0
                    pixel += (uint) * bp << 8;
996
0
                    bp++;
997
                    /* falls through */
998
0
                case 1:
999
0
                    pixel += *bp;
1000
0
                    bp++;
1001
0
                    break;
1002
0
                case 0: /* bpp == 4, bpe == 1 */
1003
0
                    pixel = *bp >> shift;
1004
0
                    if ((shift -= depth) < 0)
1005
0
                        bp++, shift += 8;
1006
0
                    break;
1007
0
            }
1008
0
            ++x;
1009
0
            b = pixel & mask;
1010
0
            pixel >>= bpe;
1011
0
            g = pixel & mask;
1012
0
            pixel >>= bpe;
1013
0
            r = pixel & mask;
1014
0
            if (bdev->is_raw) {
1015
0
                if (color) {
1016
0
                    if (gp_fputc(r, pstream) == EOF)
1017
0
                        return_error(gs_error_ioerror);
1018
0
                    if (gp_fputc(g, pstream) == EOF)
1019
0
                        return_error(gs_error_ioerror);
1020
0
                }
1021
0
                if (gp_fputc(b, pstream) == EOF)
1022
0
                    return_error(gs_error_ioerror);
1023
0
            } else {
1024
0
                if (color) {
1025
0
                    if (gp_fprintf(pstream, "%d %d ", r, g) < 0)
1026
0
                        return_error(gs_error_ioerror);
1027
0
                }
1028
0
                if (gp_fprintf(pstream, "%d%c", b,
1029
0
                        (x == pdev->width || !(x & eol_mask) ?
1030
0
                         '\n' : ' ')) < 0)
1031
0
                    return_error(gs_error_ioerror);
1032
0
            }
1033
0
        }
1034
0
    }
1035
0
    return 0;
1036
0
}
1037
static int
1038
ppm_print_row(gx_device_printer * pdev, byte * data, int depth,
1039
              gp_file * pstream)
1040
0
{
1041
0
    return ppgm_print_row(pdev, data, depth, pstream, true);
1042
0
}
1043
static int
1044
ppm_pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
1045
                  gp_file * pstream)
1046
0
{
1047
0
    return ppgm_print_row(pdev, data, depth, pstream, false);
1048
0
}
1049
static int
1050
ppm_print_page(gx_device_printer * pdev, gp_file * pstream)
1051
0
{
1052
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1053
1054
0
    return (bdev->uses_color >= 2 || !bdev->optimize ?
1055
0
            pbm_print_page_loop(pdev, bdev->magic, pstream,
1056
0
                                ppm_print_row) :
1057
0
            bdev->uses_color == 1 ?
1058
0
            pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
1059
0
                                ppm_pgm_print_row) :
1060
0
            pbm_print_page_loop(pdev, (char)((int)bdev->magic - 2), pstream,
1061
0
                                pxm_pbm_print_row));
1062
0
}
1063
1064
static int
1065
pam_print_row(gx_device_printer * pdev, byte * data, int depth,
1066
               gp_file * pstream)
1067
0
{
1068
0
    if (depth == 32) {
1069
0
        uint n = pdev->width * (depth / 8);
1070
1071
0
        if (gp_fwrite(data, 1, n, pstream) != n)
1072
0
            return_error(gs_error_ioerror);
1073
0
    }
1074
0
    return 0;
1075
0
}
1076
1077
static int
1078
pam_print_page(gx_device_printer * pdev, gp_file * pstream)
1079
0
{
1080
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1081
1082
0
    return pbm_print_page_loop(pdev, bdev->magic, pstream,
1083
0
                                pam_print_row);
1084
0
}
1085
1086
static int
1087
pnmcmyk_print_page(gx_device_printer *pdev, gp_file *pstream)
1088
0
{
1089
0
    if (pdev->icc_struct->graydetection == true && pdev->icc_struct->pageneutralcolor == true) {
1090
        /* Here we need to convert the data from CMYK to K (gray) then print */
1091
0
        gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1092
0
        uint raster = gdev_prn_raster_chunky(pdev); /* enough space for the CMYK data */
1093
0
        byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
1094
0
        int lnum = 0;
1095
0
        int code = 0;
1096
0
        int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
1097
0
            !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
1098
0
        int (*row_proc) (gx_device_printer *, byte *, int, gp_file *);
1099
1100
0
        if (data == NULL)
1101
0
            return_error(gs_error_VMerror);
1102
0
        if (!output_is_nul) {
1103
0
            if (gp_fprintf(pstream, "P5\n") < 0) { /* PGM raw */
1104
0
                code = gs_note_error(gs_error_ioerror);
1105
0
                goto punt;
1106
0
            }
1107
0
            if (bdev->comment[0]) {
1108
0
                if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
1109
0
                    code = gs_note_error(gs_error_ioerror);
1110
0
                    goto punt;
1111
0
                }
1112
0
            } else {
1113
0
                if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
1114
0
                        gs_product, pdev->dname) < 0) {
1115
0
                    code = gs_note_error(gs_error_ioerror);
1116
0
                    goto punt;
1117
0
                }
1118
0
            }
1119
0
            if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
1120
0
                code = gs_note_error(gs_error_ioerror);
1121
0
                goto punt;
1122
0
            }
1123
0
            if (gp_fprintf(pstream, "255\n") < 0) {
1124
0
                code = gs_note_error(gs_error_ioerror);
1125
0
                goto punt;
1126
0
            }
1127
0
            row_proc = pgm_print_row;
1128
0
        } else
1129
0
            row_proc = nop_row_proc;
1130
1131
1132
0
        for (; lnum < pdev->height; lnum++) {
1133
0
            byte *row, *row_end;
1134
0
            byte *pcmyk, *pgray;    /* scan pointers through the row */
1135
1136
0
            code = gdev_prn_get_bits(pdev, lnum, data, &row);
1137
0
            if (code < 0)
1138
0
                break;
1139
            /* convert the CMYK to Gray */
1140
0
            pgray = row;      /* destination for converted color */
1141
0
            row_end = row + (4 * pdev->width);
1142
0
            for (pcmyk = row; pcmyk < row_end;) {
1143
0
                int32_t cmy;
1144
0
                byte k;
1145
1146
                /* For now we assume that the CMYK may have gone through an ICC profile */
1147
                /* so we do a more complex conversion from CMYK to K. If we are using   */
1148
                /* FastColor, we may be able to do this more efficiently.               */
1149
0
                cmy  = ((255 - *pcmyk++) * lum_red_weight);
1150
0
                cmy += ((255 - *pcmyk++) * lum_green_weight);
1151
0
                cmy += ((255 - *pcmyk++) * lum_blue_weight);
1152
0
                cmy += (lum_all_weights / 2);
1153
0
                cmy /= lum_all_weights;
1154
1155
0
                k = *pcmyk++;
1156
0
                if (k > cmy)
1157
0
                    k = 0;   /* additive black */
1158
0
                else
1159
0
                    k = cmy - k; /* additive gray */
1160
0
                *pgray++ = k;   /* store it */
1161
0
            }
1162
            /* we converted to normal "additive" gray (white == 1) so set */
1163
            /* the color_info.polarity so that pgm_print_row doesn't invert */
1164
0
            pdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
1165
0
            code = (*row_proc) (pdev, row, 8, pstream);
1166
0
            pdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE; /* restore actual polarity */
1167
0
            if (code < 0)
1168
0
                break;
1169
0
        }
1170
0
        punt:
1171
0
        gs_free_object(pdev->memory, data, "pbm_print_page_loop");
1172
0
        return (code < 0 ? code : 0);
1173
0
    }
1174
    /* otherwise, just spit out the CMYK 32-bit PAM format */
1175
0
    return pam_print_page(pdev, pstream);
1176
0
}
1177
1178
static int
1179
pam4_print_row(gx_device_printer * pdev, byte * data, int depth,
1180
               gp_file * pstream)
1181
0
{
1182
0
    int w, s;
1183
0
    if (depth == 4) {
1184
0
        for (w = pdev->width; w > 0;) {
1185
0
            byte C = *data++;
1186
0
            for (s = 7; s >= 0; s -= 4)
1187
0
            {
1188
0
                gp_fputc(((C>>s    )&1)*0xff, pstream);
1189
0
                gp_fputc(((C>>(s-1))&1)*0xff, pstream);
1190
0
                gp_fputc(((C>>(s-2))&1)*0xff, pstream);
1191
0
                gp_fputc(((C>>(s-3))&1)*0xff, pstream);
1192
0
                w--;
1193
0
                if (w == 0)
1194
0
                    break;
1195
0
            }
1196
0
        }
1197
0
    }
1198
0
    return 0;
1199
0
}
1200
1201
static int
1202
pam4_print_page(gx_device_printer * pdev, gp_file * pstream)
1203
0
{
1204
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1205
1206
0
    return pbm_print_page_loop(pdev, bdev->magic, pstream,
1207
0
                               pam4_print_row);
1208
0
}
1209
1210
/* Print a faux CMYK page. */
1211
/* Print a row where each pixel occupies 4 bits (depth == 4). */
1212
/* In this case, we also know pdev->color_info.max_color == 1. */
1213
static int
1214
pkm_print_row_4(gx_device_printer * pdev, byte * data, int depth,
1215
                gp_file * pstream)
1216
0
{
1217
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1218
0
    byte *bp;
1219
0
    uint x;
1220
0
    byte rv[16], gv[16], bv[16], i;
1221
1222
    /* Precompute all the possible pixel values. */
1223
0
    for (i = 0; i < 16; ++i) {
1224
0
        gx_color_value rgb[3];
1225
1226
0
        cmyk_1bit_map_color_rgb((gx_device *)pdev, (gx_color_index)i, rgb);
1227
0
        rv[i] = rgb[0] / gx_max_color_value * 0xff;
1228
0
        gv[i] = rgb[1] / gx_max_color_value * 0xff;
1229
0
        bv[i] = rgb[2] / gx_max_color_value * 0xff;
1230
0
    }
1231
    /*
1232
     * Contrary to what the documentation implies, gcc compiles putc
1233
     * as a procedure call.  This is ridiculous, but since we can't
1234
     * change it, we buffer groups of pixels ourselves and use fwrite.
1235
     */
1236
0
    if (bdev->is_raw) {
1237
0
#ifdef PACIFY_VALGRIND
1238
0
        if ((pdev->width & 1) != 0) {
1239
0
            data[pdev->width>>1] &= 0xf0;
1240
0
        }
1241
0
#endif
1242
0
        for (bp = data, x = 0; x < pdev->width;) {
1243
0
            byte raw[50 * 3];   /* 50 is arbitrary, but must be even */
1244
0
            int end = min(x + sizeof(raw) / 3, pdev->width);
1245
0
            byte *outp = raw;
1246
1247
0
            for (; x < end; bp++, outp += 6, x += 2) {
1248
0
                uint b = *bp;
1249
0
                uint pixel = b >> 4;
1250
1251
0
                outp[0] = rv[pixel], outp[1] = gv[pixel], outp[2] = bv[pixel];
1252
0
                pixel = b & 0xf;
1253
0
                outp[3] = rv[pixel], outp[4] = gv[pixel], outp[5] = bv[pixel];
1254
0
            }
1255
            /* x might overshoot the width by 1 pixel. */
1256
0
            if (x > end)
1257
0
                outp -= 3;
1258
0
            if (gp_fwrite(raw, 1, outp - raw, pstream) != outp - raw)
1259
0
                return_error(gs_error_ioerror);
1260
0
        }
1261
0
    } else {
1262
0
        int shift;
1263
1264
0
        for (bp = data, x = 0, shift = 4; x < pdev->width;) {
1265
0
            int pixel = (*bp >> shift) & 0xf;
1266
1267
0
            shift ^= 4;
1268
0
            bp += shift >> 2;
1269
0
            ++x;
1270
0
            if (gp_fprintf(pstream, "%d %d %d%c", rv[pixel], gv[pixel], bv[pixel],
1271
0
                    (x == pdev->width || !(x & 7) ?
1272
0
                     '\n' : ' ')) < 0)
1273
0
                return_error(gs_error_ioerror);
1274
0
        }
1275
0
    }
1276
0
    return 0;
1277
0
}
1278
/* Print a row where each pixel occupies 1 or more bytes (depth >= 8). */
1279
/* Note that the output is scaled up to 255 max value.                 */
1280
static int
1281
pkm_print_row(gx_device_printer * pdev, byte * data, int depth,
1282
              gp_file * pstream)
1283
0
{
1284
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1285
0
    byte *bp;
1286
0
    uint x;
1287
1288
0
    for (bp = data, x = 0; x < pdev->width;) {
1289
0
        bits32 pixel = 0;
1290
0
        gx_color_value rgb[3];
1291
0
        uint r, g, b;
1292
1293
0
        switch (depth >> 3) {
1294
0
            case 4:
1295
0
                pixel = (bits32) * bp << 24;
1296
0
                bp++;
1297
                /* falls through */
1298
0
            case 3:
1299
0
                pixel += (bits32) * bp << 16;
1300
0
                bp++;
1301
                /* falls through */
1302
0
            case 2:
1303
0
                pixel += (uint) * bp << 8;
1304
0
                bp++;
1305
                /* falls through */
1306
0
            case 1:
1307
0
                pixel += *bp;
1308
0
                bp++;
1309
0
        }
1310
0
        ++x;
1311
0
        pkm_map_color_rgb((gx_device *) pdev, pixel, rgb);
1312
0
        r = rgb[0] * 0xff / gx_max_color_value;
1313
0
        g = rgb[1] * 0xff / gx_max_color_value;
1314
0
        b = rgb[2] * 0xff / gx_max_color_value;
1315
0
        if (bdev->is_raw) {
1316
0
            if (gp_fputc(r, pstream) == EOF)
1317
0
                return_error(gs_error_ioerror);
1318
0
            if (gp_fputc(g, pstream) == EOF)
1319
0
                return_error(gs_error_ioerror);
1320
0
            if (gp_fputc(b, pstream) == EOF)
1321
0
                return_error(gs_error_ioerror);
1322
0
        } else {
1323
0
            if (gp_fprintf(pstream, "%d %d %d%c", r, g, b,
1324
0
                    (x == pdev->width || !(x & 7) ?
1325
0
                     '\n' : ' ')) < 0)
1326
0
                return_error(gs_error_ioerror);
1327
0
        }
1328
0
    }
1329
0
    return 0;
1330
0
}
1331
static int
1332
pkm_print_page(gx_device_printer * pdev, gp_file * pstream)
1333
0
{
1334
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1335
1336
0
    return pbm_print_page_loop(pdev, bdev->magic, pstream,
1337
0
                               (pdev->color_info.depth < 8 ?
1338
0
                                pkm_print_row_4 :
1339
0
                                pkm_print_row));
1340
0
}
1341
1342
/* Print individual separations on a single file. */
1343
static int
1344
psm_print_page(gx_device_printer * pdev, gp_file * pstream)
1345
0
{
1346
0
    gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1347
    /*
1348
     * Allocate a large enough buffer for full pixels, on the theory that we
1349
     * don't know how many bits will be allocated to each component.  (This
1350
     * is for didactic purposes only: we know perfectly well that each
1351
     * component will have 1/N of the bits.)
1352
     */
1353
0
    uint max_raster = bitmap_raster(pdev->width * pdev->color_info.depth);
1354
0
    byte *data = gs_alloc_bytes(pdev->memory, max_raster, "pksm_print_page");
1355
0
    int code = 0;
1356
0
    unsigned char plane;
1357
1358
0
    if (data == 0)
1359
0
        return_error(gs_error_VMerror);
1360
0
    for (plane = 0; plane < pdev->color_info.num_components; ++plane) {
1361
0
        int lnum, band_end;
1362
        /*
1363
         * The following initialization is unnecessary: lnum == band_end on
1364
         * the first pass through the loop below, so marked will always be
1365
         * set before it is used.  We initialize marked solely to suppress
1366
         * bogus warning messages from certain compilers.
1367
         */
1368
0
        gx_color_index marked = 0;
1369
0
        gx_render_plane_t render_plane;
1370
0
        int plane_depth;
1371
0
        int plane_shift;
1372
0
        gx_color_index plane_mask;
1373
0
        int raster;
1374
1375
0
        gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
1376
0
        plane_depth = render_plane.depth;
1377
0
        plane_shift = render_plane.shift;
1378
0
        plane_mask = ((gx_color_index)1 << plane_depth) - 1;
1379
0
        raster = bitmap_raster(pdev->width * plane_depth);
1380
0
        if (gp_fprintf(pstream, "P%c\n", bdev->magic + (plane_depth > 1)) < 0) {
1381
0
            code = gs_note_error(gs_error_ioerror);
1382
0
            goto punt;
1383
0
        }
1384
0
        if (bdev->comment[0]) {
1385
0
            if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
1386
0
                code = gs_note_error(gs_error_ioerror);
1387
0
                goto punt;
1388
0
            }
1389
0
        } else {
1390
0
            if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
1391
0
                    gs_product, pdev->dname) < 0) {
1392
0
                code = gs_note_error(gs_error_ioerror);
1393
0
                goto punt;
1394
0
            }
1395
0
        }
1396
0
        if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
1397
0
            code = gs_note_error(gs_error_ioerror);
1398
0
            goto punt;
1399
0
        }
1400
0
        if (plane_depth > 1) {
1401
0
            if (gp_fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
1402
0
                code = gs_note_error(gs_error_ioerror);
1403
0
                goto punt;
1404
0
            }
1405
0
        }
1406
0
        for (lnum = band_end = 0; lnum < pdev->height; lnum++) {
1407
0
            byte *row;
1408
1409
0
            if (lnum == band_end) {
1410
0
                gx_color_usage_t color_usage;
1411
0
                int band_start;
1412
0
                int band_height =
1413
0
                    gdev_prn_color_usage((gx_device *)pdev, lnum, 1,
1414
0
                                         &color_usage, &band_start);
1415
1416
0
                band_end = band_start + band_height;
1417
0
                marked = color_usage.or & (plane_mask << plane_shift);
1418
0
                if (!marked)
1419
0
                    memset(data, 0, raster);
1420
#ifdef DEBUG
1421
                if (plane == 0)
1422
                    if_debug4m(':', pdev->memory,
1423
                               "[:]%4d - %4d mask = 0x%lx, slow_rop = %d\n",
1424
                               lnum, band_end - 1, (ulong)color_usage.or,
1425
                               color_usage.slow_rop);
1426
#endif
1427
0
            }
1428
0
            if (marked) {
1429
0
                gx_render_plane_t render_plane;
1430
0
                uint actual_raster;
1431
1432
0
                render_plane.index = plane;
1433
0
                code = gdev_prn_get_lines(pdev, lnum, 1, data, raster,
1434
0
                                          &row, &actual_raster,
1435
0
                                          &render_plane);
1436
0
                if (code < 0)
1437
0
                    break;
1438
0
            } else
1439
0
                row = data;
1440
0
            code =
1441
0
                (plane_depth == 1 ?
1442
0
                 pbm_print_row(pdev, row, plane_depth, pstream) :
1443
0
                 pgm_print_row(pdev, row, plane_depth, pstream));
1444
0
            if (code < 0)
1445
0
                break;
1446
0
        }
1447
0
    }
1448
0
  punt:
1449
0
    gs_free_object(pdev->memory, data, "pksm_print_page");
1450
0
    return (code < 0 ? code : 0);
1451
0
}