Coverage Report

Created: 2025-08-28 07:06

/src/ghostpdl/devices/gdevpng.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 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
/* PNG (Portable Network Graphics) Format.  Pronounced "ping". */
18
/* lpd 1999-09-24: changes PNG_NO_STDIO to PNG_NO_CONSOLE_IO for libpng
19
   versions 1.0.3 and later. */
20
/* lpd 1999-07-01: replaced remaining uses of gs_malloc and gs_free with
21
   gs_alloc_bytes and gs_free_object. */
22
/* lpd 1999-03-08: changed png.h to png_.h to allow compiling with only
23
   headers in /usr/include, no source code. */
24
/* lpd 1997-07-20: changed from using gs_malloc/png_xxx_int to png_create_xxx
25
 * for allocating structures, and from gs_free to png_write_destroy for
26
 * freeing them. */
27
/* lpd 1997-5-7: added PNG_LIBPNG_VER conditional for operand types of
28
 * dummy png_push_fill_buffer. */
29
/* lpd 1997-4-13: Added PNG_NO_STDIO to remove library access to stderr. */
30
/* lpd 1997-3-14: Added resolution (pHYs) to output. */
31
/* lpd 1996-6-24: Added #ifdef for compatibility with old libpng versions. */
32
/* lpd 1996-6-11: Edited to remove unnecessary color mapping code. */
33
/* lpd (L. Peter Deutsch) 1996-4-7: Modified for libpng 0.88. */
34
/* Original version by Russell Lang 1995-07-04 */
35
36
/* RJW: Include png header BEFORE the gs ones to avoid warnings. */
37
/*
38
 * libpng versions 1.0.3 and later allow disabling access to the stdxxx
39
 * files while retaining support for FILE * I/O.
40
 */
41
#define PNG_NO_CONSOLE_IO
42
/*
43
 * Earlier libpng versions require disabling FILE * I/O altogether.
44
 * This produces a compiler warning about no prototype for png_init_io.
45
 * The right thing will happen at link time, since the library itself
46
 * is compiled with stdio support.  Unfortunately, we can't do this
47
 * conditionally depending on PNG_LIBPNG_VER, because this is defined
48
 * in png.h.
49
 */
50
/*#define PNG_NO_STDIO*/
51
#include "png_.h"
52
53
#include "gdevprn.h"
54
#include "gdevmem.h"
55
#include "gdevpccm.h"
56
#include "gscdefs.h"
57
#include "gxdownscale.h"
58
#include "gxdevsop.h"
59
#include "gscms.h"
60
#include "setjmp_.h"
61
62
/* ------ The device descriptors ------ */
63
64
/*
65
 * Default X and Y resolution.
66
 */
67
#define X_DPI 72
68
#define Y_DPI 72
69
70
static dev_proc_print_page(png_print_page);
71
static dev_proc_print_page(png_print_page_monod);
72
static dev_proc_open_device(pngalpha_open);
73
static dev_proc_encode_color(pngalpha_encode_color);
74
static dev_proc_decode_color(pngalpha_decode_color);
75
static dev_proc_copy_alpha(pngalpha_copy_alpha);
76
static dev_proc_fillpage(pngalpha_fillpage);
77
static dev_proc_put_image(pngalpha_put_image);
78
static dev_proc_get_params(pngalpha_get_params);
79
static dev_proc_put_params(pngalpha_put_params);
80
static dev_proc_create_buf_device(pngalpha_create_buf_device);
81
static dev_proc_get_params(png_get_params);
82
static dev_proc_put_params(png_put_params);
83
static dev_proc_get_params(png_get_params_mfs);
84
static dev_proc_put_params(png_put_params_mfs);
85
static dev_proc_dev_spec_op(pngalpha_spec_op);
86
87
typedef struct gx_device_png_s gx_device_png;
88
struct gx_device_png_s {
89
    gx_device_common;
90
    gx_prn_device_common;
91
    bool EmbedProfile;
92
    gx_downscaler_params downscale;
93
};
94
95
/* Monochrome. */
96
97
const gx_device_png gs_pngmono_device =
98
{ /* The print_page proc is compatible with allowing bg printing */
99
  prn_device_body(gx_device_png, gdev_prn_initialize_device_procs_mono_bg, "pngmono",
100
           DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
101
           X_DPI, Y_DPI,
102
           0, 0, 0, 0,    /* margins */
103
           1, 1, 1, 1, 2, 2, png_print_page),
104
    1, /* EmbedProfile */
105
    GX_DOWNSCALER_PARAMS_DEFAULTS
106
};
107
108
109
/* 4-bit planar (EGA/VGA-style) color. */
110
111
/* Since the print_page doesn't alter the device, this device can print in the background */
112
static void
113
png16_initialize_device_procs(gx_device *dev)
114
0
{
115
0
    gdev_prn_initialize_device_procs_bg(dev);
116
117
0
    set_dev_proc(dev, map_rgb_color, pc_4bit_map_rgb_color);
118
0
    set_dev_proc(dev, map_color_rgb, pc_4bit_map_color_rgb);
119
0
    set_dev_proc(dev, encode_color, pc_4bit_map_rgb_color);
120
0
    set_dev_proc(dev, decode_color, pc_4bit_map_color_rgb);
121
0
}
122
123
const gx_device_png gs_png16_device = {
124
  prn_device_body(gx_device_png, png16_initialize_device_procs, "png16",
125
           DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
126
           X_DPI, Y_DPI,
127
           0, 0, 0, 0,    /* margins */
128
           3, 4, 1, 1, 2, 2, png_print_page),
129
    1, /* EmbedProfile */
130
    GX_DOWNSCALER_PARAMS_DEFAULTS
131
};
132
133
/* 8-bit (SuperVGA-style) color. */
134
/* (Uses a fixed palette of 3,3,2 bits.) */
135
136
/* Since the print_page doesn't alter the device, this device can print in the background */
137
static void
138
png256_initialize_device_procs(gx_device *dev)
139
0
{
140
0
    gdev_prn_initialize_device_procs_bg(dev);
141
142
0
    set_dev_proc(dev, map_rgb_color, pc_8bit_map_rgb_color);
143
0
    set_dev_proc(dev, map_color_rgb, pc_8bit_map_color_rgb);
144
0
    set_dev_proc(dev, encode_color, pc_8bit_map_rgb_color);
145
0
    set_dev_proc(dev, decode_color, pc_8bit_map_color_rgb);
146
0
}
147
148
const gx_device_png gs_png256_device = {
149
  prn_device_body(gx_device_png, png256_initialize_device_procs, "png256",
150
           DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
151
           X_DPI, Y_DPI,
152
           0, 0, 0, 0,    /* margins */
153
           3, 8, 5, 5, 6, 6, png_print_page),
154
    1, /* EmbedProfile */
155
    GX_DOWNSCALER_PARAMS_DEFAULTS
156
};
157
158
/* 8-bit gray */
159
160
/* Since the print_page doesn't alter the device, this device can print in the background */
161
static void
162
pnggray_initialize_device_procs(gx_device *dev)
163
0
{
164
0
    gdev_prn_initialize_device_procs_gray_bg(dev);
165
166
0
    set_dev_proc(dev, get_params, png_get_params);
167
0
    set_dev_proc(dev, put_params, png_put_params);
168
0
    set_dev_proc(dev, encode_color, gx_default_8bit_map_gray_color);
169
0
    set_dev_proc(dev, decode_color, gx_default_8bit_map_color_gray);
170
0
}
171
172
const gx_device_png gs_pnggray_device =
173
{prn_device_body(gx_device_png, pnggray_initialize_device_procs, "pnggray",
174
                 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
175
                 X_DPI, Y_DPI,
176
                 0, 0, 0, 0,  /* margins */
177
                 1, 8, 255, 0, 256, 0, png_print_page),
178
    1, /* EmbedProfile */
179
    GX_DOWNSCALER_PARAMS_DEFAULTS
180
};
181
182
/* Monochrome (with error diffusion) */
183
184
/* Since the print_page doesn't alter the device, this device can print in the background */
185
static void
186
pngmonod_initialize_device_procs(gx_device *dev)
187
0
{
188
0
    gdev_prn_initialize_device_procs_gray_bg(dev);
189
190
0
    set_dev_proc(dev, get_params, png_get_params_mfs);
191
0
    set_dev_proc(dev, put_params, png_put_params_mfs);
192
0
    set_dev_proc(dev, encode_color, gx_default_8bit_map_gray_color);
193
0
    set_dev_proc(dev, decode_color, gx_default_8bit_map_color_gray);
194
0
}
195
196
const gx_device_png gs_pngmonod_device =
197
{prn_device_body(gx_device_png, pngmonod_initialize_device_procs, "pngmonod",
198
                 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
199
                 X_DPI, Y_DPI,
200
                 0, 0, 0, 0,  /* margins */
201
                 1, 8, 255, 0, 256, 0, png_print_page_monod),
202
    1, /* EmbedProfile */
203
    GX_DOWNSCALER_PARAMS_DEFAULTS
204
};
205
206
/* 24-bit color. */
207
208
/* Since the print_page doesn't alter the device, this device can print in the background */
209
static void
210
png16m_initialize_device_procs(gx_device *dev)
211
12.4k
{
212
12.4k
    gdev_prn_initialize_device_procs_rgb_bg(dev);
213
214
12.4k
    set_dev_proc(dev, get_params, png_get_params);
215
12.4k
    set_dev_proc(dev, put_params, png_put_params);
216
217
    /* The prn macros used in previous versions of the code leave
218
     * encode_color and decode_color set to NULL (which are then rewritten
219
     * by the system to the default. For compatibility we do the same. */
220
12.4k
    set_dev_proc(dev, encode_color, gx_default_rgb_map_rgb_color);
221
12.4k
    set_dev_proc(dev, decode_color, gx_default_rgb_map_color_rgb);
222
12.4k
}
223
224
const gx_device_png gs_png16m_device =
225
{prn_device_body(gx_device_png, png16m_initialize_device_procs, "png16m",
226
                 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
227
                 X_DPI, Y_DPI,
228
                 0, 0, 0, 0,  /* margins */
229
                 3, 24, 255, 255, 256, 256, png_print_page),
230
    1, /* EmbedProfile */
231
    GX_DOWNSCALER_PARAMS_DEFAULTS
232
};
233
234
/* 48 bit color. */
235
236
static void
237
png48_initialize_device_procs(gx_device *dev)
238
0
{
239
0
    gdev_prn_initialize_device_procs_rgb_bg(dev);
240
241
    /* The prn macros used in previous versions of the code leave
242
     * encode_color and decode_color set to NULL (which are then rewritten
243
     * by the system to the default. For compatibility we do the same. */
244
0
    set_dev_proc(dev, encode_color, gx_default_rgb_map_rgb_color);
245
0
    set_dev_proc(dev, decode_color, gx_default_rgb_map_color_rgb);
246
0
}
247
248
/* Since the print_page doesn't alter the device, this device can print in the background */
249
const gx_device_png gs_png48_device =
250
{prn_device_body(gx_device_png, png48_initialize_device_procs, "png48",
251
                 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
252
                 X_DPI, Y_DPI,
253
                 0, 0, 0, 0,  /* margins */
254
                 3, 48, 0, 65535, 1, 65536, png_print_page),
255
    1, /* EmbedProfile */
256
    GX_DOWNSCALER_PARAMS_DEFAULTS
257
};
258
259
/* 32-bit RGBA */
260
/* pngalpha device is 32-bit RGBA, with the alpha channel
261
 * indicating pixel coverage, not true transparency.
262
 * Anti-aliasing is enabled by default.
263
 * An erasepage will erase to transparent, not white.
264
 * It is intended to be used for creating web graphics with
265
 * a transparent background.
266
 */
267
typedef struct gx_device_pngalpha_s gx_device_pngalpha;
268
struct gx_device_pngalpha_s {
269
    gx_device_common;
270
    gx_prn_device_common;
271
    bool EmbedProfile;
272
    gx_downscaler_params downscale;
273
    int background;
274
};
275
276
static void
277
pngalpha_initialize_device_procs(gx_device *dev)
278
0
{
279
0
    gdev_prn_initialize_device_procs_bg(dev);
280
281
0
    set_dev_proc(dev, open_device, pngalpha_open);
282
0
    set_dev_proc(dev, map_rgb_color, pngalpha_encode_color);
283
0
    set_dev_proc(dev, map_color_rgb, pngalpha_decode_color);
284
0
    set_dev_proc(dev, encode_color, pngalpha_encode_color);
285
0
    set_dev_proc(dev, decode_color, pngalpha_decode_color);
286
0
    set_dev_proc(dev, get_params, pngalpha_get_params);
287
0
    set_dev_proc(dev, put_params, pngalpha_put_params);
288
0
    set_dev_proc(dev, copy_alpha, pngalpha_copy_alpha);
289
0
    set_dev_proc(dev, get_color_mapping_procs, gx_default_DevRGB_get_color_mapping_procs);
290
0
    set_dev_proc(dev, get_color_comp_index, gx_default_DevRGB_get_color_comp_index);
291
0
    set_dev_proc(dev, fillpage, pngalpha_fillpage);
292
0
    set_dev_proc(dev, put_image, pngalpha_put_image);
293
0
    set_dev_proc(dev, dev_spec_op, pngalpha_spec_op);
294
0
}
295
296
const gx_device_pngalpha gs_pngalpha_device = {
297
        std_device_part1_(gx_device_pngalpha,
298
                pngalpha_initialize_device_procs, "pngalpha",
299
                &st_device_printer, open_init_closed),
300
        /* color_info */
301
        {3 /* max components */,
302
         3 /* number components */,
303
         GX_CINFO_POLARITY_ADDITIVE /* polarity */,
304
         32 /* depth */,
305
         -1 /* gray index */,
306
         255 /* max gray */,
307
         255 /* max color */,
308
         256 /* dither grays */,
309
         256 /* dither colors */,
310
         { 4, 4 } /* antialias info text, graphics */,
311
         GX_CINFO_UNKNOWN_SEP_LIN /* separable_and_linear */,
312
         { 0 } /* component shift */,
313
         { 0 } /* component bits */,
314
         { 0 } /* component mask */,
315
         "DeviceRGB" /* process color name */,
316
         GX_CINFO_OPMSUPPORTED_UNKNOWN /* opmsupported */,
317
         0 /* process_cmps */,
318
         0 /* icc_locations */
319
        },
320
        std_device_part2_(
321
          (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),
322
          (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),
323
           X_DPI, Y_DPI),
324
        offset_margin_values(0, 0, 0, 0, 0, 0),
325
        std_device_part3_(),
326
        prn_device_body_rest_(png_print_page),
327
        1, /*EmbedProfile */
328
        GX_DOWNSCALER_PARAMS_DEFAULTS,
329
        0xffffff  /* white background */
330
};
331
332
const gx_device_pngalpha gs_png16malpha_device = {
333
        std_device_part1_(gx_device_pngalpha,
334
                pngalpha_initialize_device_procs, "png16malpha",
335
                &st_device_printer, open_init_closed),
336
        /* color_info */
337
        {3 /* max components */,
338
         3 /* number components */,
339
         GX_CINFO_POLARITY_ADDITIVE /* polarity */,
340
         32 /* depth */,
341
         -1 /* gray index */,
342
         255 /* max gray */,
343
         255 /* max color */,
344
         256 /* dither grays */,
345
         256 /* dither colors */,
346
         { 1, 1 } /* antialias info text, graphics */,
347
         GX_CINFO_UNKNOWN_SEP_LIN /* separable_and_linear */,
348
         { 0 } /* component shift */,
349
         { 0 } /* component bits */,
350
         { 0 } /* component mask */,
351
         "DeviceRGB" /* process color name */,
352
         GX_CINFO_OPMSUPPORTED_UNKNOWN /* opmsupported */,
353
         0 /* process_cmps */,
354
         0 /* icc_locations */
355
        },
356
        std_device_part2_(
357
          (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),
358
          (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),
359
           X_DPI, Y_DPI),
360
        offset_margin_values(0, 0, 0, 0, 0, 0),
361
        std_device_part3_(),
362
        prn_device_body_rest_(png_print_page),
363
        1, /*EmbedProfile */
364
        GX_DOWNSCALER_PARAMS_DEFAULTS,
365
        0xffffff  /* white background */
366
};
367
368
/* ------ Private definitions ------ */
369
370
static int
371
png_get_params(gx_device * dev, gs_param_list * plist)
372
268k
{
373
268k
    gx_device_png *pdev = (gx_device_png *)dev;
374
268k
    int code, ecode;
375
376
268k
    ecode = 0;
377
378
268k
    if ((code = param_write_bool(plist, "EmbedProfile", &pdev->EmbedProfile)) < 0)
379
0
        ecode = code;
380
381
268k
    if ((code = gx_downscaler_write_params(plist, &pdev->downscale, 0)) < 0)
382
0
        ecode = code;
383
384
268k
    code = gdev_prn_get_params(dev, plist);
385
268k
    if (code < 0)
386
0
        ecode = code;
387
388
268k
    return ecode;
389
268k
}
390
391
static int
392
png_put_params(gx_device *dev, gs_param_list *plist)
393
80.5k
{
394
80.5k
    gx_device_png *pdev = (gx_device_png *)dev;
395
80.5k
    int code, ecode;
396
80.5k
    gs_param_name param_name;
397
80.5k
    bool bparam = false;
398
399
80.5k
    code = param_read_bool(plist, (param_name = "EmbedProfile"), &bparam);
400
80.5k
    if ( code == 0 ) {
401
7.90k
        pdev->EmbedProfile = bparam;
402
7.90k
    }
403
72.6k
    else if ( code < 1 ) {
404
0
        param_signal_error(plist, param_name, code);
405
0
        return code;
406
0
    }
407
408
80.5k
    ecode = gx_downscaler_read_params(plist, &pdev->downscale, 0);
409
410
80.5k
    code = gdev_prn_put_params(dev, plist);
411
80.5k
    if (code < 0)
412
51
        ecode = code;
413
414
80.5k
    return ecode;
415
80.5k
}
416
417
static int
418
png_get_params_mfs(gx_device *dev, gs_param_list *plist)
419
0
{
420
0
    gx_device_png *pdev = (gx_device_png *)dev;
421
0
    int code, ecode;
422
423
0
    if ((code = param_write_bool(plist, "EmbedProfile", &pdev->EmbedProfile)) < 0)
424
0
        ecode = code;
425
426
0
    if ((code = gx_downscaler_write_params(plist, &pdev->downscale, GX_DOWNSCALER_PARAMS_MFS)) < 0)
427
0
        ecode = code;
428
429
0
    code = gdev_prn_get_params(dev, plist);
430
0
    if (code < 0)
431
0
        ecode = code;
432
433
0
    return ecode;
434
0
}
435
436
static int
437
png_put_params_mfs(gx_device *dev, gs_param_list *plist)
438
0
{
439
0
    gx_device_png *pdev = (gx_device_png *)dev;
440
0
    int code, ecode;
441
0
    gs_param_name param_name;
442
0
    bool bparam = false;
443
444
0
    code = param_read_bool(plist, (param_name = "EmbedProfile"), &bparam);
445
0
    if ( code == 0 ) {
446
0
        pdev->EmbedProfile = bparam;
447
0
    }
448
0
    else if ( code < 1 ) {
449
0
        param_signal_error(plist, param_name, code);
450
0
        return code;
451
0
    }
452
453
0
    ecode = gx_downscaler_read_params(plist, &pdev->downscale,
454
0
                                      GX_DOWNSCALER_PARAMS_MFS);
455
456
0
    code = gdev_prn_put_params(dev, plist);
457
0
    if (code < 0)
458
0
        ecode = code;
459
460
0
    return ecode;
461
0
}
462
463
166k
#define PNG_MEM_ALIGN 16
464
static png_voidp
465
gdevpng_malloc(png_structp png, png_size_t size)
466
83.0k
{
467
83.0k
    gs_memory_t *mem = png_get_mem_ptr(png);
468
83.0k
    uchar *unaligned;
469
83.0k
    uchar *aligned;
470
471
83.0k
    if (size == 0)
472
0
        return NULL;
473
83.0k
    unaligned = gs_alloc_bytes(mem, size + PNG_MEM_ALIGN, "libpng");
474
83.0k
    if (unaligned == NULL)
475
0
        return NULL;
476
477
83.0k
    aligned = (uchar *)((intptr_t)(unaligned + PNG_MEM_ALIGN) & ~(PNG_MEM_ALIGN - 1));
478
83.0k
    aligned[-1] = (uchar)(aligned - unaligned);
479
480
83.0k
    return aligned;
481
83.0k
}
482
483
static void
484
gdevpng_free(png_structp png, png_voidp ptr)
485
83.0k
{
486
83.0k
    gs_memory_t *mem = png_get_mem_ptr(png);
487
83.0k
    uchar *aligned = ptr;
488
83.0k
    if (aligned == NULL)
489
0
        return;
490
83.0k
    gs_free_object(mem, aligned - aligned[-1], "libpng");
491
83.0k
}
492
493
static void
494
my_png_write(png_struct *png, png_bytep buf, png_size_t size)
495
182k
{
496
182k
    gp_file *file = png_get_io_ptr(png);
497
498
182k
    (void)gp_fwrite(buf, 1, size, file);
499
182k
}
500
501
static void
502
my_png_flush(png_struct *png)
503
0
{
504
0
    gp_file *file = png_get_io_ptr(png);
505
506
0
    (void)gp_fflush(file);
507
0
}
508
509
/* Write out a page in PNG format. */
510
/* This routine is used for all formats. */
511
OPTIMIZE_SETJMP
512
static int
513
do_png_print_page(gx_device_png * pdev, gp_file * file, bool monod)
514
5.93k
{
515
5.93k
    gs_memory_t *mem = pdev->memory;
516
5.93k
    size_t raster = gdev_prn_raster(pdev);
517
5.93k
    gx_downscaler_t ds;
518
519
    /* PNG structures */
520
5.93k
    byte *row = gs_alloc_bytes(mem, raster, "png raster buffer");
521
5.93k
    png_struct *png_ptr =
522
5.93k
        png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, pdev->memory, gdevpng_malloc, gdevpng_free);
523
5.93k
    png_info *info_ptr = png_create_info_struct(png_ptr);
524
5.93k
    int depth = pdev->color_info.depth;
525
5.93k
    int y;
526
5.93k
    int code;     /* return code */
527
5.93k
    char software_key[80];
528
5.93k
    char software_text[256];
529
5.93k
    png_text text_png;
530
5.93k
    int dst_bpc, src_bpc;
531
5.93k
    bool errdiff = 0;
532
533
5.93k
    bool invert = false, endian_swap = false, bg_needed = false;
534
5.93k
    png_byte bit_depth = 0;
535
5.93k
    png_byte color_type = 0;
536
5.93k
    png_uint_32 x_pixels_per_unit;
537
5.93k
    png_uint_32 y_pixels_per_unit;
538
5.93k
    png_byte phys_unit_type;
539
5.93k
    png_color_16 background;
540
5.93k
    png_uint_32 width, height;
541
5.93k
#if PNG_LIBPNG_VER_MINOR >= 5
542
5.93k
    png_color palette[256];
543
5.93k
#endif
544
5.93k
    png_color *palettep;
545
5.93k
    png_uint_16 num_palette;
546
5.93k
    png_uint_32 valid = 0;
547
5.93k
    int upfactor, downfactor;
548
549
    /* Sanity check params */
550
5.93k
    if (pdev->downscale.downscale_factor < 1)
551
0
        pdev->downscale.downscale_factor = 1;
552
5.93k
    if (pdev->downscale.min_feature_size < 1)
553
5.38k
        pdev->downscale.min_feature_size = 1;
554
547
    else if (pdev->downscale.min_feature_size > 2)
555
0
        pdev->downscale.min_feature_size = 2;
556
557
    /* Slightly nasty, but it saves us duplicating this entire routine. */
558
5.93k
    if (monod) {
559
0
        errdiff = 1;
560
0
        depth = 1;
561
0
    }
562
563
5.93k
    if (row == 0 || png_ptr == 0 || info_ptr == 0) {
564
0
        code = gs_note_error(gs_error_VMerror);
565
0
        goto done;
566
0
    }
567
    /* set error handling */
568
5.93k
#if PNG_LIBPNG_VER_MINOR >= 5
569
5.93k
    code = setjmp(png_jmpbuf(png_ptr));
570
#else
571
    code = setjmp(png_ptr->jmpbuf);
572
#endif
573
5.93k
    if (code) {
574
        /* If we get here, we had a problem reading the file */
575
1
        code = gs_note_error(gs_error_VMerror);
576
1
        goto done;
577
1
    }
578
5.93k
    code = 0;     /* for normal path */
579
    /* set up the output control */
580
5.93k
    png_set_write_fn(png_ptr, file, my_png_write, my_png_flush);
581
582
    /* set the file information here */
583
5.93k
    gx_downscaler_decode_factor(pdev->downscale.downscale_factor,
584
5.93k
                                &upfactor, &downfactor);
585
    /* resolution is in pixels per meter vs. dpi */
586
5.93k
    x_pixels_per_unit =
587
5.93k
        (png_uint_32) (pdev->HWResolution[0] * upfactor * (100.0 / 2.54) / downfactor + 0.5);
588
5.93k
    y_pixels_per_unit =
589
5.93k
        (png_uint_32) (pdev->HWResolution[1] * upfactor * (100.0 / 2.54) / downfactor + 0.5);
590
591
5.93k
    phys_unit_type = PNG_RESOLUTION_METER;
592
5.93k
    valid |= PNG_INFO_pHYs;
593
594
5.93k
    switch (depth) {
595
0
        case 32:
596
0
            bit_depth = 8;
597
0
            color_type = PNG_COLOR_TYPE_RGB_ALPHA;
598
0
            invert = true;
599
600
0
            {   gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
601
0
                background.index = 0;
602
0
                background.red =   (ppdev->background >> 16) & 0xff;
603
0
                background.green = (ppdev->background >> 8)  & 0xff;
604
0
                background.blue =  (ppdev->background)       & 0xff;
605
0
                background.gray = 0;
606
0
                bg_needed = true;
607
0
            }
608
0
            errdiff = 1;
609
0
            break;
610
0
        case 48:
611
0
            bit_depth = 16;
612
0
            color_type = PNG_COLOR_TYPE_RGB;
613
0
#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
614
0
            endian_swap = true;
615
0
#endif
616
0
            break;
617
5.93k
        case 24:
618
5.93k
            bit_depth = 8;
619
5.93k
            color_type = PNG_COLOR_TYPE_RGB;
620
5.93k
            errdiff = 1;
621
5.93k
            break;
622
0
        case 8:
623
0
            bit_depth = 8;
624
0
            if (gx_device_has_color(pdev)) {
625
0
                color_type = PNG_COLOR_TYPE_PALETTE;
626
0
                errdiff = 0;
627
0
            } else {
628
0
                color_type = PNG_COLOR_TYPE_GRAY;
629
0
                errdiff = 1;
630
0
            }
631
0
            break;
632
0
        case 4:
633
0
            bit_depth = 4;
634
0
            color_type = PNG_COLOR_TYPE_PALETTE;
635
0
            break;
636
0
        case 1:
637
0
            bit_depth = 1;
638
0
            color_type = PNG_COLOR_TYPE_GRAY;
639
            /* invert monochrome pixels */
640
0
            if (!monod) {
641
0
                invert = true;
642
0
            }
643
0
            break;
644
5.93k
    }
645
646
    /* set the palette if there is one */
647
5.93k
    if (color_type == PNG_COLOR_TYPE_PALETTE) {
648
0
        int i;
649
0
        int num_colors = 1 << depth;
650
0
        gx_color_value rgb[3];
651
652
0
#if PNG_LIBPNG_VER_MINOR >= 5
653
0
        palettep = palette;
654
#else
655
        palettep =
656
            (void *)gs_alloc_bytes(mem, (size_t)256 * sizeof(png_color),
657
                                   "png palette");
658
        if (palettep == 0) {
659
            code = gs_note_error(gs_error_VMerror);
660
            goto done;
661
        }
662
#endif
663
0
        num_palette = num_colors;
664
0
        valid |= PNG_INFO_PLTE;
665
0
        for (i = 0; i < num_colors; i++) {
666
0
            (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
667
0
                                              (gx_color_index) i, rgb);
668
0
            palettep[i].red = gx_color_value_to_byte(rgb[0]);
669
0
            palettep[i].green = gx_color_value_to_byte(rgb[1]);
670
0
            palettep[i].blue = gx_color_value_to_byte(rgb[2]);
671
0
        }
672
0
    }
673
5.93k
    else {
674
5.93k
        palettep = NULL;
675
5.93k
        num_palette = 0;
676
5.93k
    }
677
    /* add comment */
678
#ifdef CLUSTER
679
    strncpy(software_key, "GPL Ghostscript", sizeof(software_key));
680
    strncpy(software_text, "GPL Ghostscript", sizeof(software_text));
681
#else
682
5.93k
    strncpy(software_key, "Software", sizeof(software_key));
683
5.93k
    {
684
5.93k
        int major = (int)(gs_revision / 1000);
685
5.93k
        int minor = (int)(gs_revision - (major * 1000)) / 10;
686
5.93k
        int patch = gs_revision % 10;
687
688
5.93k
        gs_snprintf(software_text, sizeof(software_text), "%s %d.%02d.%d", gs_product, major, minor, patch);
689
5.93k
    }
690
5.93k
#endif
691
5.93k
    text_png.compression = -1;  /* uncompressed */
692
5.93k
    text_png.key = software_key;
693
5.93k
    text_png.text = software_text;
694
5.93k
    text_png.text_length = strlen(software_text);
695
696
5.93k
    dst_bpc = bit_depth;
697
5.93k
    src_bpc = dst_bpc;
698
5.93k
    if (errdiff)
699
5.93k
        src_bpc = 8;
700
0
    else
701
0
        pdev->downscale.downscale_factor = upfactor = downfactor = 1;
702
5.93k
    width = pdev->width * upfactor / downfactor;
703
5.93k
    height = pdev->height * upfactor / downfactor;
704
705
5.93k
#if PNG_LIBPNG_VER_MINOR >= 5
706
5.93k
    png_set_pHYs(png_ptr, info_ptr,
707
5.93k
                 x_pixels_per_unit, y_pixels_per_unit, phys_unit_type);
708
709
5.93k
    png_set_IHDR(png_ptr, info_ptr,
710
5.93k
                 width, height, bit_depth,
711
5.93k
                 color_type, PNG_INTERLACE_NONE,
712
5.93k
                 PNG_COMPRESSION_TYPE_DEFAULT,
713
5.93k
                 PNG_FILTER_TYPE_DEFAULT);
714
5.93k
    if (palettep)
715
0
        png_set_PLTE(png_ptr, info_ptr, palettep, num_palette);
716
717
5.93k
    png_set_text(png_ptr, info_ptr, &text_png, 1);
718
719
5.93k
    if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE] != NULL) {
720
5.93k
        cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE];
721
5.93k
        if (icc_profile->hash_is_valid && icc_profile->hashcode == ARTIFEX_sRGB_HASH) {
722
            /* sRGB case. Just use the tag */
723
5.93k
            png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE);
724
5.93k
        } else {
725
            /* PNG can only be RGB or gray.  No CIELAB :(  */
726
0
            if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) {
727
0
                if (icc_profile->num_comps == pdev->color_info.num_components &&
728
0
                    !(pdev->icc_struct->usefastcolor && pdev->EmbedProfile)) {
729
0
                    png_set_iCCP(png_ptr, info_ptr, icc_profile->name,
730
0
                        PNG_COMPRESSION_TYPE_DEFAULT, icc_profile->buffer,
731
0
                        icc_profile->buffer_size);
732
0
                }
733
0
            }
734
0
        }
735
5.93k
    }
736
#else
737
    info_ptr->bit_depth = bit_depth;
738
    info_ptr->color_type = color_type;
739
    info_ptr->width = width;
740
    info_ptr->height = height;
741
    info_ptr->x_pixels_per_unit = x_pixels_per_unit;
742
    info_ptr->y_pixels_per_unit = y_pixels_per_unit;
743
    info_ptr->phys_unit_type = phys_unit_type;
744
    info_ptr->palette = palettep;
745
    info_ptr->num_palette = num_palette;
746
    info_ptr->valid |= valid;
747
    info_ptr->text = &text_png;
748
    info_ptr->num_text = 1;
749
    /* Set up the ICC information */
750
    if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE] != NULL) {
751
        cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE];
752
        if (icc_profile->hash_is_valid && icc_profile->hashcode == ARTIFEX_sRGB_HASH) {
753
            /* sRGB case. Just use the tag */
754
            png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE);
755
        } else {
756
            /* PNG can only be RGB or gray.  No CIELAB :(  */
757
            if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) {
758
                if (icc_profile->num_comps == pdev->color_info.num_components &&
759
                    !(pdev->icc_struct->usefastcolor) && pdev->EmbedProfile) {
760
                    info_ptr->iccp_name = icc_profile->name;
761
                    info_ptr->iccp_profile = icc_profile->buffer;
762
                    info_ptr->iccp_proflen = icc_profile->buffer_size;
763
                    info_ptr->valid |= PNG_INFO_iCCP;
764
                }
765
            }
766
        }
767
    }
768
#endif
769
5.93k
    if (invert) {
770
0
        if (depth == 32)
771
0
            png_set_invert_alpha(png_ptr);
772
0
        else
773
0
            png_set_invert_mono(png_ptr);
774
0
    }
775
5.93k
    if (bg_needed) {
776
0
        png_set_bKGD(png_ptr, info_ptr, &background);
777
0
    }
778
5.93k
#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
779
5.93k
    if (endian_swap) {
780
0
        png_set_swap(png_ptr);
781
0
    }
782
5.93k
#endif
783
784
    /* write the file information */
785
5.93k
    png_write_info(png_ptr, info_ptr);
786
787
5.93k
#if PNG_LIBPNG_VER_MINOR >= 5
788
#else
789
    /* don't write the comments twice */
790
    info_ptr->num_text = 0;
791
    info_ptr->text = NULL;
792
#endif
793
794
    /* For simplicity of code, we always go through the downscaler. For
795
     * non-supported depths, it will pass through with minimal performance
796
     * hit. So ensure that we only trigger downscales when we need them.
797
     */
798
5.93k
    code = gx_downscaler_init(&ds, (gx_device *)pdev, src_bpc, dst_bpc,
799
5.93k
                              depth/dst_bpc, &pdev->downscale, NULL, 0);
800
5.93k
    if (code >= 0)
801
5.93k
    {
802
#ifdef CLUSTER
803
        int bitlen = width*dst_bpc;
804
        int end = bitlen>>3;
805
        int mask = 255>>(bitlen&7);
806
        if (bitlen & 7)
807
            mask = ~mask;
808
        else
809
            end--;
810
#endif
811
        /* Write the contents of the image. */
812
11.1M
        for (y = 0; y < height; y++) {
813
11.1M
            gx_downscaler_getbits(&ds, row, y);
814
#ifdef CLUSTER
815
            row[end] &= mask;
816
#endif
817
11.1M
            png_write_rows(png_ptr, &row, 1);
818
11.1M
        }
819
5.93k
        gx_downscaler_fin(&ds);
820
5.93k
    }
821
822
    /* write the rest of the file */
823
5.93k
    png_write_end(png_ptr, info_ptr);
824
825
5.93k
#if PNG_LIBPNG_VER_MINOR >= 5
826
#else
827
    /* if you alloced the palette, free it here */
828
    gs_free_object(mem, palettep, "png palette");
829
#endif
830
831
5.93k
  done:
832
    /* free the structures */
833
5.93k
    png_destroy_write_struct(&png_ptr, &info_ptr);
834
5.93k
    gs_free_object(mem, row, "png raster buffer");
835
836
5.93k
    return code;
837
5.93k
}
838
839
static int
840
png_print_page(gx_device_printer * pdev, gp_file * file)
841
5.93k
{
842
5.93k
    return do_png_print_page((gx_device_png *)pdev, file, 0);
843
5.93k
}
844
845
static int
846
png_print_page_monod(gx_device_printer * pdev, gp_file * file)
847
0
{
848
0
    return do_png_print_page((gx_device_png *)pdev, file, 1);
849
0
}
850
851
#if PNG_LIBPNG_VER_MINOR < 5
852
853
/*
854
 * Patch around a static reference to a never-used procedure.
855
 * This could be avoided if we were willing to edit pngconf.h to
856
 *      #undef PNG_PROGRESSIVE_READ_SUPPORTED
857
 */
858
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
859
#  if PNG_LIBPNG_VER >= 95
860
#    define PPFB_LENGTH_T png_size_t
861
#  else
862
#    define PPFB_LENGTH_T png_uint_32
863
#  endif
864
void
865
png_push_fill_buffer(png_structp, png_bytep, PPFB_LENGTH_T);
866
void
867
png_push_fill_buffer(png_structp png_ptr, png_bytep buffer,
868
                     PPFB_LENGTH_T length)
869
{
870
}
871
#endif
872
#endif
873
874
static int
875
pngalpha_open(gx_device * pdev)
876
0
{
877
0
    gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
878
0
    int code;
879
    /* We replace create_buf_device so we can replace copy_alpha
880
     * for memory device, but not clist. We also replace the fillpage
881
     * proc with our own to fill with transparent.
882
     */
883
0
    ppdev->printer_procs.buf_procs.create_buf_device =
884
0
        pngalpha_create_buf_device;
885
0
    code = gdev_prn_open(pdev);
886
0
    return code;
887
0
}
888
889
static int
890
pngalpha_create_buf_device(gx_device **pbdev, gx_device *target, int y,
891
   const gx_render_plane_t *render_plane, gs_memory_t *mem,
892
   gx_color_usage_t *color_usage)
893
0
{
894
0
    gx_device_printer *ptarget;
895
0
    int code = gx_default_create_buf_device(pbdev, target, y,
896
0
        render_plane, mem, color_usage);
897
    /* Now set copy_alpha to one that handles RGBA */
898
899
    /* this is really pretty nasty. The pngalpha device is going to replace
900
     * the device methods in the memory rendering device with some of its own.
901
     * To me this seems fraught with peril, its making a lot of assumptions
902
     * about the compatibility of the devices!
903
     * This, of course, totally breaks device chaining, but since the memory
904
     * device wasn't going to pass on the intermediate method calls to the
905
     * 'terminating' device, we can work around it here. We simply descend
906
     * the chain of devices to the terminating device, and pull the methods
907
     * we need directly from that device. I don't know why we are using
908
     * 'orig_procs' either, but its safe to do so because this is only
909
     * done here for the PNG device, and we know that this is a gx_device_prn
910
     * based device.
911
     */
912
0
    while (target->child != NULL)
913
0
        target = target->child;
914
915
0
    ptarget= (gx_device_printer *)target;
916
0
    set_dev_proc(*pbdev, copy_alpha, ptarget->orig_procs.copy_alpha);
917
0
    set_dev_proc(*pbdev, dev_spec_op, ptarget->orig_procs.dev_spec_op);
918
0
    set_dev_proc(*pbdev, fillpage, pngalpha_fillpage);
919
0
    return code;
920
0
}
921
922
static int
923
pngalpha_put_params(gx_device * pdev, gs_param_list * plist)
924
0
{
925
0
    gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
926
0
    int background;
927
0
    int code, ecode;
928
0
    gs_param_name param_name;
929
0
    bool bparam = false;
930
931
    /* BackgroundColor in format 16#RRGGBB is used for bKGD chunk */
932
0
    switch(code = param_read_int(plist, "BackgroundColor", &background)) {
933
0
        case 0:
934
0
            ppdev->background = background & 0xffffff;
935
0
            break;
936
0
        case 1:   /* not found */
937
0
            code = 0;
938
0
            break;
939
0
        default:
940
0
            param_signal_error(plist, "BackgroundColor", code);
941
0
            break;
942
0
    }
943
944
0
    ecode = param_read_bool(plist, (param_name = "EmbedProfile"), &bparam);
945
0
    if ( ecode == 0 ) {
946
0
        ppdev->EmbedProfile = bparam;
947
0
    }
948
0
    else if ( ecode < 1 ) {
949
0
        code = ecode;
950
0
        param_signal_error(plist, param_name, code);
951
0
    }
952
953
0
    if ((ecode = gx_downscaler_read_params(plist, &ppdev->downscale, 0)) < 0)
954
0
        code = ecode;
955
956
0
    if (code == 0) {
957
0
        code = gdev_prn_put_params(pdev, plist);
958
0
    }
959
960
0
    return code;
961
0
}
962
963
/* Get device parameters */
964
static int
965
pngalpha_get_params(gx_device * pdev, gs_param_list * plist)
966
0
{
967
0
    gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
968
0
    int code = gdev_prn_get_params(pdev, plist);
969
0
    int ecode = 0;
970
971
0
    if (code >= 0) {
972
0
        code = param_write_int(plist, "BackgroundColor",
973
0
                                &(ppdev->background));
974
0
        if ((ecode = param_write_bool(plist, "EmbedProfile", &ppdev->EmbedProfile)) < 0)
975
0
            code = ecode;
976
0
        if ((ecode = gx_downscaler_write_params(plist, &ppdev->downscale, 0)) < 0)
977
0
            code = ecode;
978
0
    }
979
980
0
    return code;
981
0
}
982
983
/* RGB mapping for 32-bit RGBA color devices */
984
985
static gx_color_index
986
pngalpha_encode_color(gx_device * dev, const gx_color_value cv[])
987
0
{
988
    /* low 7 are alpha, stored inverted to avoid white/opaque
989
     * being 0xffffffff which is also gx_no_color_index.
990
     * So 0xff is transparent and 0x00 is opaque.
991
     * We always return opaque colors (bits 0-7 = 0).
992
     * Return value is 0xRRGGBB00.
993
     */
994
0
    return
995
0
        ((uint) gx_color_value_to_byte(cv[2]) << 8) +
996
0
        ((ulong) gx_color_value_to_byte(cv[1]) << 16) +
997
0
        ((ulong) gx_color_value_to_byte(cv[0]) << 24);
998
0
}
999
1000
/* Map a color index to a r-g-b color. */
1001
static int
1002
pngalpha_decode_color(gx_device * dev, gx_color_index color,
1003
                             gx_color_value prgb[])
1004
0
{
1005
0
    prgb[0] = gx_color_value_from_byte((color >> 24) & 0xff);
1006
0
    prgb[1] = gx_color_value_from_byte((color >> 16) & 0xff);
1007
0
    prgb[2] = gx_color_value_from_byte((color >> 8)  & 0xff);
1008
0
    return 0;
1009
0
}
1010
1011
/* fill the page fills with transparent */
1012
static int
1013
pngalpha_fillpage(gx_device *dev, gs_gstate * pgs, gx_device_color *pdevc)
1014
0
{
1015
0
    return (*dev_proc(dev, fill_rectangle))(dev, 0, 0, dev->width, dev->height,  0xffffffff);
1016
0
}
1017
1018
/* Handle the RGBA planes from the PDF 1.4 compositor */
1019
static int
1020
pngalpha_put_image (gx_device *pdev, gx_device *mdev, const byte **buffers, int num_chan, int xstart,
1021
              int ystart, int width, int height, int row_stride,
1022
              int alpha_plane_index, int tag_plane_index)
1023
0
{
1024
0
    gx_device_memory *pmemdev = (gx_device_memory *)mdev;
1025
0
    byte *buffer_prn;
1026
0
    int yend = ystart + height;
1027
0
    int xend = xstart + width;
1028
0
    int x, y;
1029
0
    int src_position, des_position;
1030
1031
0
    if (num_chan != 3 || alpha_plane_index <= 0)
1032
0
            return_error(gs_error_unknownerror);        /* can't handle these cases */
1033
1034
    /* Now we need to convert the 4 channels (RGBA) planar into what   */
1035
    /* the do_png_print_page expects -- chunky inverted data. For that */
1036
    /* we need to find the underlying gx_device_memory buffer for the  */
1037
    /* data (similar to bit_put_image, and borrwed from there).        */
1038
    /* Drill down to get the appropriate memory buffer pointer */
1039
0
    buffer_prn = pmemdev->base;
1040
    /* Now go ahead and process the planes into chunky as the memory device needs */
1041
0
    for ( y = ystart; y < yend; y++ ) {
1042
0
        src_position = (y - ystart) * row_stride;
1043
0
        des_position = y * pmemdev->raster + xstart * 4;
1044
0
        for ( x = xstart; x < xend; x++ ) {
1045
0
            buffer_prn[des_position++] =  buffers[0][src_position];
1046
0
            buffer_prn[des_position++] =  buffers[1][src_position];
1047
0
            buffer_prn[des_position++] =  buffers[2][src_position];
1048
            /* Alpha data in low bits. Note that Alpha is inverted. */
1049
0
            buffer_prn[des_position++] = (255 - buffers[alpha_plane_index][src_position]);
1050
0
            src_position += 1;
1051
0
        }
1052
0
    }
1053
0
    return height;        /* we used all of the data */
1054
0
}
1055
1056
/* Implementation for 32-bit RGBA in a memory buffer */
1057
/* Derived from gx_default_copy_alpha, but now maintains alpha channel. */
1058
static int
1059
pngalpha_copy_alpha(gx_device * dev, const byte * data, int data_x,
1060
           int raster, gx_bitmap_id id, int x, int y, int width, int height,
1061
                      gx_color_index color, int depth)
1062
0
{       /* This might be called with depth = 1.... */
1063
0
    if (depth == 1)
1064
0
        return (*dev_proc(dev, copy_mono)) (dev, data, data_x, raster, id,
1065
0
                                            x, y, width, height,
1066
0
                                            gx_no_color_index, color);
1067
    /*
1068
     * Simulate alpha by weighted averaging of RGB values.
1069
     * This is very slow, but functionally correct.
1070
     */
1071
0
    {
1072
0
        const byte *row;
1073
0
        gs_memory_t *mem = dev->memory;
1074
0
        int bpp = dev->color_info.depth;
1075
0
        int ncomps = dev->color_info.num_components;
1076
0
        size_t in_size = gx_device_raster(dev, false);
1077
0
        byte *lin;
1078
0
        size_t out_size;
1079
0
        byte *lout;
1080
0
        int code = 0;
1081
0
        gx_color_value color_cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
1082
0
        int ry;
1083
0
        gs_int_rect rect;
1084
0
        gs_get_bits_params_t params;
1085
1086
0
        fit_copy(dev, data, data_x, raster, id, x, y, width, height);
1087
0
        row = data;
1088
0
        out_size = bitmap_raster(width * bpp);
1089
0
        lin = gs_alloc_bytes(mem, in_size, "copy_alpha(lin)");
1090
0
        lout = gs_alloc_bytes(mem, out_size, "copy_alpha(lout)");
1091
0
        if (lin == 0 || lout == 0) {
1092
0
            code = gs_note_error(gs_error_VMerror);
1093
0
            goto out;
1094
0
        }
1095
0
        (*dev_proc(dev, decode_color)) (dev, color, color_cv);
1096
0
        rect.p.x = 0;
1097
0
        rect.q.x = dev->width;
1098
0
        params.x_offset = 0;
1099
0
        params.raster = bitmap_raster(dev->width * dev->color_info.depth);
1100
0
        for (ry = y; ry < y + height; row += raster, ++ry) {
1101
0
            byte *line;
1102
0
            int sx, rx;
1103
1104
0
            byte *l_dptr = lout;
1105
0
            int l_dbit = 0;
1106
0
            byte l_dbyte = ((l_dbit) ? (byte)(*(l_dptr) & (0xff00 >> (l_dbit))) : 0);
1107
0
            int l_xprev = x;
1108
1109
0
            rect.p.y = ry;
1110
0
            rect.q.y = ry+1;
1111
1112
0
            params.options = (GB_ALIGN_ANY |
1113
0
                              (GB_RETURN_COPY | GB_RETURN_POINTER) |
1114
0
                              GB_OFFSET_0 |
1115
0
                              GB_RASTER_STANDARD | GB_PACKING_CHUNKY |
1116
0
                              GB_COLORS_NATIVE | GB_ALPHA_NONE);
1117
0
            params.data[0] = lin;
1118
0
            code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, &params);
1119
0
            if (code < 0)
1120
0
                break;
1121
0
            line = params.data[0];
1122
0
            for (sx = data_x, rx = x; sx < data_x + width; ++sx, ++rx) {
1123
0
                gx_color_index previous = gx_no_color_index;
1124
0
                gx_color_index composite;
1125
0
                uint32_t alpha2, alpha;
1126
1127
0
                switch(depth)
1128
0
                {
1129
0
                case 2: /* map 0 - 3 to 0 - 255 */
1130
0
                    alpha = ((row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 85;
1131
0
                    break;
1132
0
                case 4:
1133
0
                    alpha2 = row[sx >> 1];
1134
0
                    alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4) * 17;
1135
0
                    break;
1136
0
                case 8:
1137
0
                    alpha = row[sx];
1138
0
                    break;
1139
0
                default:
1140
0
      return_error(gs_error_rangecheck);
1141
0
                }
1142
0
                if (alpha == 255) { /* Just write the new color. */
1143
0
                    composite = color;
1144
0
                } else {
1145
0
                    if (previous == gx_no_color_index) { /* Extract the old color. */
1146
0
                        const byte *src = line + (rx * (bpp >> 3));
1147
0
                        previous = 0;
1148
0
                        previous += (gx_color_index) * src++ << 24;
1149
0
                        previous += (gx_color_index) * src++ << 16;
1150
0
                        previous += (gx_color_index) * src++ << 8;
1151
0
                        previous += *src++;
1152
0
                    }
1153
0
                    if (alpha == 0) { /* Just write the old color. */
1154
0
                        composite = previous;
1155
0
                    } else { /* Blend values. */
1156
0
                        gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
1157
0
                        int i;
1158
0
                        uint32_t old_coverage;
1159
0
                        uint32_t new_coverage;
1160
1161
0
                        (*dev_proc(dev, decode_color)) (dev, previous, cv);
1162
                        /* decode color doesn't give us coverage */
1163
0
                        cv[3] = previous & 0xff;
1164
0
                        old_coverage = 255 - cv[3];
1165
0
                        new_coverage =
1166
0
                            (255 * alpha + old_coverage * (255 - alpha)) / 255;
1167
0
                        for (i=0; i<ncomps; i++)
1168
0
                            cv[i] = min(((255 * alpha * color_cv[i]) +
1169
0
                                         (old_coverage * (255 - alpha ) * cv[i]))
1170
0
                                        / (new_coverage * 255), gx_max_color_value);
1171
0
                        composite =
1172
0
                            (*dev_proc(dev, encode_color)) (dev, cv);
1173
                        /* encode color doesn't include coverage */
1174
0
                        composite |= (255 - new_coverage) & 0xff;
1175
1176
                        /* composite can never be gx_no_color_index
1177
                         * because pixel is never completely transparent
1178
                         * (low byte != 0xff).
1179
                         */
1180
0
                    }
1181
0
                }
1182
0
                if (sizeof(composite) > 4) {
1183
0
                    if (sample_store_next64(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0)
1184
0
                        return_error(gs_error_rangecheck);
1185
0
                }
1186
0
                else {
1187
0
                    if (sample_store_next32(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0)
1188
0
                        return_error(gs_error_rangecheck);
1189
0
                }
1190
0
            }
1191
0
            if ( rx > l_xprev ) {
1192
0
                sample_store_flush(l_dptr, l_dbit, l_dbyte);
1193
0
                code = (*dev_proc(dev, copy_color))
1194
0
                  (dev, lout, l_xprev - x, raster,
1195
0
                   gx_no_bitmap_id, l_xprev, ry, rx - l_xprev, 1);
1196
0
                if (code < 0)
1197
0
                    return code;
1198
0
            }
1199
0
        }
1200
0
      out:gs_free_object(mem, lout, "copy_alpha(lout)");
1201
0
        gs_free_object(mem, lin, "copy_alpha(lin)");
1202
0
        return code;
1203
0
    }
1204
0
}
1205
1206
static int
1207
pngalpha_spec_op(gx_device* pdev, int dso, void* ptr, int size)
1208
0
{
1209
0
    switch (dso)
1210
0
    {
1211
0
    case gxdso_supports_alpha:
1212
0
        return 1;
1213
0
    }
1214
1215
0
    return gdev_prn_dev_spec_op(pdev, dso, ptr, size);
1216
0
}