summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'devices/gdevpng.c')
-rw-r--r--devices/gdevpng.c1062
1 files changed, 1062 insertions, 0 deletions
diff --git a/devices/gdevpng.c b/devices/gdevpng.c
new file mode 100644
index 00000000..10962516
--- /dev/null
+++ b/devices/gdevpng.c
@@ -0,0 +1,1062 @@
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PNG (Portable Network Graphics) Format. Pronounced "ping". */
+/* lpd 1999-09-24: changes PNG_NO_STDIO to PNG_NO_CONSOLE_IO for libpng
+ versions 1.0.3 and later. */
+/* lpd 1999-07-01: replaced remaining uses of gs_malloc and gs_free with
+ gs_alloc_bytes and gs_free_object. */
+/* lpd 1999-03-08: changed png.h to png_.h to allow compiling with only
+ headers in /usr/include, no source code. */
+/* lpd 1997-07-20: changed from using gs_malloc/png_xxx_int to png_create_xxx
+ * for allocating structures, and from gs_free to png_write_destroy for
+ * freeing them. */
+/* lpd 1997-5-7: added PNG_LIBPNG_VER conditional for operand types of
+ * dummy png_push_fill_buffer. */
+/* lpd 1997-4-13: Added PNG_NO_STDIO to remove library access to stderr. */
+/* lpd 1997-3-14: Added resolution (pHYs) to output. */
+/* lpd 1996-6-24: Added #ifdef for compatibility with old libpng versions. */
+/* lpd 1996-6-11: Edited to remove unnecessary color mapping code. */
+/* lpd (L. Peter Deutsch) 1996-4-7: Modified for libpng 0.88. */
+/* Original version by Russell Lang 1995-07-04 */
+
+/* RJW: Include png header BEFORE the gs ones to avoid warnings. */
+/*
+ * libpng versions 1.0.3 and later allow disabling access to the stdxxx
+ * files while retaining support for FILE * I/O.
+ */
+#define PNG_NO_CONSOLE_IO
+/*
+ * Earlier libpng versions require disabling FILE * I/O altogether.
+ * This produces a compiler warning about no prototype for png_init_io.
+ * The right thing will happen at link time, since the library itself
+ * is compiled with stdio support. Unfortunately, we can't do this
+ * conditionally depending on PNG_LIBPNG_VER, because this is defined
+ * in png.h.
+ */
+/*#define PNG_NO_STDIO*/
+#include "png_.h"
+
+#include "gdevprn.h"
+#include "gdevmem.h"
+#include "gdevpccm.h"
+#include "gscdefs.h"
+#include "gxdownscale.h"
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+static dev_proc_print_page(png_print_page);
+static dev_proc_print_page(png_print_page_monod);
+static dev_proc_open_device(pngalpha_open);
+static dev_proc_encode_color(pngalpha_encode_color);
+static dev_proc_decode_color(pngalpha_decode_color);
+static dev_proc_copy_alpha(pngalpha_copy_alpha);
+static dev_proc_fillpage(pngalpha_fillpage);
+static dev_proc_put_image(pngalpha_put_image);
+static dev_proc_get_params(pngalpha_get_params);
+static dev_proc_put_params(pngalpha_put_params);
+static dev_proc_create_buf_device(pngalpha_create_buf_device);
+static dev_proc_get_params(png_get_params_downscale);
+static dev_proc_put_params(png_put_params_downscale);
+static dev_proc_get_params(png_get_params_downscale_mfs);
+static dev_proc_put_params(png_put_params_downscale_mfs);
+
+typedef struct gx_device_png_s gx_device_png;
+struct gx_device_png_s {
+ gx_device_common;
+ gx_prn_device_common;
+ gx_downscaler_params downscale;
+};
+
+/* Monochrome. */
+
+const gx_device_png gs_pngmono_device =
+{ /* The print_page proc is compatible with allowing bg printing */
+ prn_device_body(gx_device_png, prn_bg_procs, "pngmono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 1, 1, 1, 2, 2, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+
+/* 4-bit planar (EGA/VGA-style) color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png16_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_png gs_png16_device = {
+ prn_device_body(gx_device_png, png16_procs, "png16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png256_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_png gs_png256_device = {
+ prn_device_body(gx_device_png, png256_procs, "png256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* 8-bit gray */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pnggray_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ png_get_params_downscale, png_put_params_downscale);
+const gx_device_png gs_pnggray_device =
+{prn_device_body(gx_device_png, pnggray_procs, "pnggray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* Monochrome (with error diffusion) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pngmonod_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ png_get_params_downscale_mfs,
+ png_put_params_downscale_mfs);
+const gx_device_png gs_pngmonod_device =
+{prn_device_body(gx_device_png, pngmonod_procs, "pngmonod",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, png_print_page_monod),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* 24-bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png16m_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ png_get_params_downscale, png_put_params_downscale);
+const gx_device_png gs_png16m_device =
+{prn_device_body(gx_device_png, png16m_procs, "png16m",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 24, 255, 255, 256, 256, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* 48 bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png48_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+const gx_device_png gs_png48_device =
+{prn_device_body(gx_device_png, png48_procs, "png48",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 48, 0, 65535, 1, 65536, png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS
+};
+
+/* 32-bit RGBA */
+/* pngalpha device is 32-bit RGBA, with the alpha channel
+ * indicating pixel coverage, not true transparency.
+ * Anti-aliasing is enabled by default.
+ * An erasepage will erase to transparent, not white.
+ * It is intended to be used for creating web graphics with
+ * a transparent background.
+ */
+typedef struct gx_device_pngalpha_s gx_device_pngalpha;
+struct gx_device_pngalpha_s {
+ gx_device_common;
+ gx_prn_device_common;
+ gx_downscaler_params downscale;
+ int background;
+};
+static const gx_device_procs pngalpha_procs =
+{
+ pngalpha_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ /* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ pngalpha_encode_color, /* map_rgb_color */
+ pngalpha_decode_color, /* map_color_rgb */
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ pngalpha_get_params,
+ pngalpha_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ pngalpha_copy_alpha,
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop, */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ gx_default_DevRGB_get_color_mapping_procs,
+ gx_default_DevRGB_get_color_comp_index,
+ pngalpha_encode_color,
+ pngalpha_decode_color,
+ NULL, /* pattern_manage */
+ NULL, /* fill_rectangle_hl_color */
+ NULL, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ pngalpha_fillpage,
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ pngalpha_put_image
+};
+
+const gx_device_pngalpha gs_pngalpha_device = {
+ std_device_part1_(gx_device_pngalpha, &pngalpha_procs, "pngalpha",
+ &st_device_printer, open_init_closed),
+ /* color_info */
+ {3 /* max components */,
+ 3 /* number components */,
+ GX_CINFO_POLARITY_ADDITIVE /* polarity */,
+ 32 /* depth */,
+ -1 /* gray index */,
+ 255 /* max gray */,
+ 255 /* max color */,
+ 256 /* dither grays */,
+ 256 /* dither colors */,
+ { 4, 4 } /* antialias info text, graphics */,
+ GX_CINFO_UNKNOWN_SEP_LIN /* separable_and_linear */,
+ { 0 } /* component shift */,
+ { 0 } /* component bits */,
+ { 0 } /* component mask */,
+ "DeviceRGB" /* process color name */,
+ GX_CINFO_OPMODE_UNKNOWN /* opmode */,
+ 0 /* process_cmps */,
+ 0 /* icc_locations */
+ },
+ std_device_part2_(
+ (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),
+ (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),
+ X_DPI, Y_DPI),
+ offset_margin_values(0, 0, 0, 0, 0, 0),
+ std_device_part3_(),
+ prn_device_body_rest_(png_print_page),
+ GX_DOWNSCALER_PARAMS_DEFAULTS,
+ 0xffffff /* white background */
+};
+
+/* ------ Private definitions ------ */
+
+static int
+png_get_params_downscale(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = 0;
+ if ((code = gx_downscaler_write_params(plist, &pdev->downscale, 0)) < 0)
+ ecode = code;
+
+ code = gdev_prn_get_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+png_put_params_downscale(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = gx_downscaler_read_params(plist, &pdev->downscale, 0);
+
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+png_get_params_downscale_mfs(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = gx_downscaler_write_params(plist, &pdev->downscale,
+ GX_DOWNSCALER_PARAMS_MFS);
+
+ code = gdev_prn_get_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+png_put_params_downscale_mfs(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = gx_downscaler_read_params(plist, &pdev->downscale,
+ GX_DOWNSCALER_PARAMS_MFS);
+
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+#define PNG_MEM_ALIGN 16
+static png_voidp
+gdevpng_malloc(png_structp png, png_size_t size)
+{
+ gs_memory_t *mem = png_get_mem_ptr(png);
+ uchar *unaligned;
+ uchar *aligned;
+
+ if (size == 0)
+ return NULL;
+ unaligned = gs_alloc_bytes(mem, size + PNG_MEM_ALIGN, "libpng");
+ if (unaligned == NULL)
+ return NULL;
+
+ aligned = (uchar *)((intptr_t)(unaligned + PNG_MEM_ALIGN) & ~(PNG_MEM_ALIGN - 1));
+ aligned[-1] = (uchar)(aligned - unaligned);
+
+ return aligned;
+}
+
+static void
+gdevpng_free(png_structp png, png_voidp ptr)
+{
+ gs_memory_t *mem = png_get_mem_ptr(png);
+ uchar *aligned = ptr;
+ if (aligned == NULL)
+ return;
+ gs_free_object(mem, aligned - aligned[-1], "libpng");
+}
+
+static void
+my_png_write(png_struct *png, png_bytep buf, png_size_t size)
+{
+ gp_file *file = png_get_io_ptr(png);
+
+ (void)gp_fwrite(buf, 1, size, file);
+}
+
+static void
+my_png_flush(png_struct *png)
+{
+ gp_file *file = png_get_io_ptr(png);
+
+ (void)gp_fflush(file);
+}
+
+/* Write out a page in PNG format. */
+/* This routine is used for all formats. */
+static int
+do_png_print_page(gx_device_png * pdev, gp_file * file, bool monod)
+{
+ gs_memory_t *mem = pdev->memory;
+ int raster = gdev_prn_raster(pdev);
+ gx_downscaler_t ds;
+
+ /* PNG structures */
+ byte *row = gs_alloc_bytes(mem, raster, "png raster buffer");
+ png_struct *png_ptr =
+ png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, pdev->memory, gdevpng_malloc, gdevpng_free);
+ png_info *info_ptr = png_create_info_struct(png_ptr);
+ int depth = pdev->color_info.depth;
+ int y;
+ int code; /* return code */
+ char software_key[80];
+ char software_text[256];
+ png_text text_png;
+ int dst_bpc, src_bpc;
+ bool errdiff = 0;
+ int factor = pdev->downscale.downscale_factor;
+ int mfs = pdev->downscale.min_feature_size;
+
+ bool invert = false, endian_swap = false, bg_needed = false;
+ png_byte bit_depth = 0;
+ png_byte color_type = 0;
+ png_uint_32 x_pixels_per_unit;
+ png_uint_32 y_pixels_per_unit;
+ png_byte phys_unit_type;
+ png_color_16 background;
+ png_uint_32 width, height;
+#if PNG_LIBPNG_VER_MINOR >= 5
+ png_color palette[256];
+#endif
+ png_color *palettep;
+ png_uint_16 num_palette;
+ png_uint_32 valid = 0;
+
+ /* Sanity check params */
+ if (factor < 1)
+ factor = 1;
+ if (mfs < 1)
+ mfs = 1;
+ else if (mfs > 2)
+ mfs = 2;
+
+ /* Slightly nasty, but it saves us duplicating this entire routine. */
+ if (monod) {
+ errdiff = 1;
+ depth = 1;
+ }
+
+ if (row == 0 || png_ptr == 0 || info_ptr == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ /* set error handling */
+#if PNG_LIBPNG_VER_MINOR >= 5
+ code = setjmp(png_jmpbuf(png_ptr));
+#else
+ code = setjmp(png_ptr->jmpbuf);
+#endif
+ if (code) {
+ /* If we get here, we had a problem reading the file */
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ code = 0; /* for normal path */
+ /* set up the output control */
+ png_set_write_fn(png_ptr, file, my_png_write, my_png_flush);
+
+ /* set the file information here */
+ /* resolution is in pixels per meter vs. dpi */
+ x_pixels_per_unit =
+ (png_uint_32) (pdev->HWResolution[0] * (100.0 / 2.54) / factor + 0.5);
+ y_pixels_per_unit =
+ (png_uint_32) (pdev->HWResolution[1] * (100.0 / 2.54) / factor + 0.5);
+
+ phys_unit_type = PNG_RESOLUTION_METER;
+ valid |= PNG_INFO_pHYs;
+
+ switch (depth) {
+ case 32:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ invert = true;
+
+ { gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ background.index = 0;
+ background.red = (ppdev->background >> 16) & 0xff;
+ background.green = (ppdev->background >> 8) & 0xff;
+ background.blue = (ppdev->background) & 0xff;
+ background.gray = 0;
+ bg_needed = true;
+ }
+ errdiff = 1;
+ break;
+ case 48:
+ bit_depth = 16;
+ color_type = PNG_COLOR_TYPE_RGB;
+#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
+ endian_swap = true;
+#endif
+ break;
+ case 24:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB;
+ errdiff = 1;
+ break;
+ case 8:
+ bit_depth = 8;
+ if (gx_device_has_color(pdev)) {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ errdiff = 0;
+ } else {
+ color_type = PNG_COLOR_TYPE_GRAY;
+ errdiff = 1;
+ }
+ break;
+ case 4:
+ bit_depth = 4;
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ break;
+ case 1:
+ bit_depth = 1;
+ color_type = PNG_COLOR_TYPE_GRAY;
+ /* invert monocrome pixels */
+ if (!monod) {
+ invert = true;
+ }
+ break;
+ }
+
+ /* set the palette if there is one */
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ int i;
+ int num_colors = 1 << depth;
+ gx_color_value rgb[3];
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+ palettep = palette;
+#else
+ palettep =
+ (void *)gs_alloc_bytes(mem, 256 * sizeof(png_color),
+ "png palette");
+ if (palettep == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+#endif
+ num_palette = num_colors;
+ valid |= PNG_INFO_PLTE;
+ for (i = 0; i < num_colors; i++) {
+ (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
+ (gx_color_index) i, rgb);
+ palettep[i].red = gx_color_value_to_byte(rgb[0]);
+ palettep[i].green = gx_color_value_to_byte(rgb[1]);
+ palettep[i].blue = gx_color_value_to_byte(rgb[2]);
+ }
+ }
+ else {
+ palettep = NULL;
+ num_palette = 0;
+ }
+ /* add comment */
+ strncpy(software_key, "Software", sizeof(software_key));
+ gs_sprintf(software_text, "%s %d.%02d", gs_product,
+ (int)(gs_revision / 100), (int)(gs_revision % 100));
+ text_png.compression = -1; /* uncompressed */
+ text_png.key = software_key;
+ text_png.text = software_text;
+ text_png.text_length = strlen(software_text);
+
+ dst_bpc = bit_depth;
+ src_bpc = dst_bpc;
+ if (errdiff)
+ src_bpc = 8;
+ else
+ factor = 1;
+ width = pdev->width/factor;
+ height = pdev->height/factor;
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+ png_set_pHYs(png_ptr, info_ptr,
+ x_pixels_per_unit, y_pixels_per_unit, phys_unit_type);
+
+ png_set_IHDR(png_ptr, info_ptr,
+ width, height, bit_depth,
+ color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ if (palettep)
+ png_set_PLTE(png_ptr, info_ptr, palettep, num_palette);
+
+ png_set_text(png_ptr, info_ptr, &text_png, 1);
+
+ if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[0] != NULL) {
+ cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[0];
+ /* PNG can only be RGB or gray. No CIELAB :( */
+ if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) {
+ if (icc_profile->num_comps == pdev->color_info.num_components &&
+ !(pdev->icc_struct->usefastcolor)) {
+ png_set_iCCP(png_ptr, info_ptr, icc_profile->name,
+ PNG_COMPRESSION_TYPE_DEFAULT, icc_profile->buffer,
+ icc_profile->buffer_size);
+ }
+ }
+ }
+#else
+ info_ptr->bit_depth = bit_depth;
+ info_ptr->color_type = color_type;
+ info_ptr->width = width;
+ info_ptr->height = height;
+ info_ptr->x_pixels_per_unit = x_pixels_per_unit;
+ info_ptr->y_pixels_per_unit = y_pixels_per_unit;
+ info_ptr->phys_unit_type = phys_unit_type;
+ info_ptr->palette = palettep;
+ info_ptr->num_palette = num_palette;
+ info_ptr->valid |= valid;
+ info_ptr->text = &text_png;
+ info_ptr->num_text = 1;
+ /* Set up the ICC information */
+ if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[0] != NULL) {
+ cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[0];
+ /* PNG can only be RGB or gray. No CIELAB :( */
+ if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) {
+ if (icc_profile->num_comps == pdev->color_info.num_components &&
+ !(pdev->icc_struct->usefastcolor)) {
+ info_ptr->iccp_name = icc_profile->name;
+ info_ptr->iccp_profile = icc_profile->buffer;
+ info_ptr->iccp_proflen = icc_profile->buffer_size;
+ info_ptr->valid |= PNG_INFO_iCCP;
+ }
+ }
+ }
+#endif
+ if (invert) {
+ if (depth == 32)
+ png_set_invert_alpha(png_ptr);
+ else
+ png_set_invert_mono(png_ptr);
+ }
+ if (bg_needed) {
+ png_set_bKGD(png_ptr, info_ptr, &background);
+ }
+#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
+ if (endian_swap) {
+ png_set_swap(png_ptr);
+ }
+#endif
+
+ /* write the file information */
+ png_write_info(png_ptr, info_ptr);
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+#else
+ /* don't write the comments twice */
+ info_ptr->num_text = 0;
+ info_ptr->text = NULL;
+#endif
+
+ /* For simplicity of code, we always go through the downscaler. For
+ * non-supported depths, it will pass through with minimal performance
+ * hit. So ensure that we only trigger downscales when we need them.
+ */
+ code = gx_downscaler_init(&ds, (gx_device *)pdev, src_bpc, dst_bpc,
+ depth/dst_bpc, factor, mfs, NULL, 0);
+ if (code >= 0)
+ {
+ /* Write the contents of the image. */
+ for (y = 0; y < height; y++) {
+ gx_downscaler_getbits(&ds, row, y);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ gx_downscaler_fin(&ds);
+ }
+
+ /* write the rest of the file */
+ png_write_end(png_ptr, info_ptr);
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+#else
+ /* if you alloced the palette, free it here */
+ gs_free_object(mem, palettep, "png palette");
+#endif
+
+ done:
+ /* free the structures */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ gs_free_object(mem, row, "png raster buffer");
+
+ return code;
+}
+
+static int
+png_print_page(gx_device_printer * pdev, gp_file * file)
+{
+ return do_png_print_page((gx_device_png *)pdev, file, 0);
+}
+
+static int
+png_print_page_monod(gx_device_printer * pdev, gp_file * file)
+{
+ return do_png_print_page((gx_device_png *)pdev, file, 1);
+}
+
+#if PNG_LIBPNG_VER_MINOR < 5
+
+/*
+ * Patch around a static reference to a never-used procedure.
+ * This could be avoided if we were willing to edit pngconf.h to
+ * #undef PNG_PROGRESSIVE_READ_SUPPORTED
+ */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+# if PNG_LIBPNG_VER >= 95
+# define PPFB_LENGTH_T png_size_t
+# else
+# define PPFB_LENGTH_T png_uint_32
+# endif
+void
+png_push_fill_buffer(png_structp, png_bytep, PPFB_LENGTH_T);
+void
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer,
+ PPFB_LENGTH_T length)
+{
+}
+#endif
+#endif
+
+static int
+pngalpha_open(gx_device * pdev)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int code;
+ /* We replace create_buf_device so we can replace copy_alpha
+ * for memory device, but not clist. We also replace the fillpage
+ * proc with our own to fill with transparent.
+ */
+ ppdev->printer_procs.buf_procs.create_buf_device =
+ pngalpha_create_buf_device;
+ code = gdev_prn_open(pdev);
+ return code;
+}
+
+static int
+pngalpha_create_buf_device(gx_device **pbdev, gx_device *target, int y,
+ const gx_render_plane_t *render_plane, gs_memory_t *mem,
+ gx_color_usage_t *color_usage)
+{
+ gx_device_printer *ptarget;
+ int code = gx_default_create_buf_device(pbdev, target, y,
+ render_plane, mem, color_usage);
+ /* Now set copy_alpha to one that handles RGBA */
+
+ /* this is really pretty nasty. The pngalpha device is going to replace
+ * the device methods in the memory rendering device with some of its own.
+ * To me this seems fraught with peril, its making a lot of assumptions
+ * about the compatibility of the devices!
+ * This, of course, totally breaks device chaining, but since the memory
+ * device wasn't going to pass on the intermediate method calls to the
+ * 'terminating' device, we can work around it here. We simply descend
+ * the chain of devices to the terminating device, and pull the methods
+ * we need directly from that device. I don't know why we are using
+ * 'orig_procs' either, but its safe to do so because this is only
+ * done here for the PNG device, and we know that this is a gx_device_prn
+ * based device.
+ */
+ while (target->child != NULL)
+ target = target->child;
+
+ ptarget= (gx_device_printer *)target;
+ set_dev_proc(*pbdev, copy_alpha, ptarget->orig_procs.copy_alpha);
+ set_dev_proc(*pbdev, fillpage, pngalpha_fillpage);
+ return code;
+}
+
+static int
+pngalpha_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int background;
+ int code, ecode;
+
+ /* BackgroundColor in format 16#RRGGBB is used for bKGD chunk */
+ switch(code = param_read_int(plist, "BackgroundColor", &background)) {
+ case 0:
+ ppdev->background = background & 0xffffff;
+ break;
+ case 1: /* not found */
+ code = 0;
+ break;
+ default:
+ param_signal_error(plist, "BackgroundColor", code);
+ break;
+ }
+
+ if ((ecode = gx_downscaler_read_params(plist, &ppdev->downscale, 0)) < 0)
+ code = ecode;
+
+ if (code == 0) {
+ code = gdev_prn_put_params(pdev, plist);
+ }
+
+ return code;
+}
+
+/* Get device parameters */
+static int
+pngalpha_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int code = gdev_prn_get_params(pdev, plist);
+ int ecode;
+ if (code >= 0)
+ code = param_write_int(plist, "BackgroundColor",
+ &(ppdev->background));
+ ecode = 0;
+ if ((ecode = gx_downscaler_write_params(plist, &ppdev->downscale, 0)) < 0)
+ code = ecode;
+
+ return code;
+}
+
+/* RGB mapping for 32-bit RGBA color devices */
+
+static gx_color_index
+pngalpha_encode_color(gx_device * dev, const gx_color_value cv[])
+{
+ /* low 7 are alpha, stored inverted to avoid white/opaque
+ * being 0xffffffff which is also gx_no_color_index.
+ * So 0xff is transparent and 0x00 is opaque.
+ * We always return opaque colors (bits 0-7 = 0).
+ * Return value is 0xRRGGBB00.
+ */
+ return
+ ((uint) gx_color_value_to_byte(cv[2]) << 8) +
+ ((ulong) gx_color_value_to_byte(cv[1]) << 16) +
+ ((ulong) gx_color_value_to_byte(cv[0]) << 24);
+}
+
+/* Map a color index to a r-g-b color. */
+static int
+pngalpha_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ prgb[0] = gx_color_value_from_byte((color >> 24) & 0xff);
+ prgb[1] = gx_color_value_from_byte((color >> 16) & 0xff);
+ prgb[2] = gx_color_value_from_byte((color >> 8) & 0xff);
+ return 0;
+}
+
+/* fill the page fills with transparent */
+static int
+pngalpha_fillpage(gx_device *dev, gs_gstate * pgs, gx_device_color *pdevc)
+{
+ return (*dev_proc(dev, fill_rectangle))(dev, 0, 0, dev->width, dev->height, 0xffffffff);
+}
+
+/* Handle the RGBA planes from the PDF 1.4 compositor */
+static int
+pngalpha_put_image (gx_device *pdev, gx_device *mdev, const byte **buffers, int num_chan, int xstart,
+ int ystart, int width, int height, int row_stride,
+ int alpha_plane_index, int tag_plane_index)
+{
+ gx_device_memory *pmemdev = (gx_device_memory *)mdev;
+ byte *buffer_prn;
+ int yend = ystart + height;
+ int xend = xstart + width;
+ int x, y;
+ int src_position, des_position;
+
+ if (num_chan != 3 || alpha_plane_index <= 0)
+ return_error(gs_error_unknownerror); /* can't handle these cases */
+
+ /* Now we need to convert the 4 channels (RGBA) planar into what */
+ /* the do_png_print_page expects -- chunky inverted data. For that */
+ /* we need to find the underlying gx_device_memory buffer for the */
+ /* data (similar to bit_put_image, and borrwed from there). */
+ /* Drill down to get the appropriate memory buffer pointer */
+ buffer_prn = pmemdev->base;
+ /* Now go ahead and process the planes into chunky as the memory device needs */
+ for ( y = ystart; y < yend; y++ ) {
+ src_position = (y - ystart) * row_stride;
+ des_position = y * pmemdev->raster + xstart * 4;
+ for ( x = xstart; x < xend; x++ ) {
+ buffer_prn[des_position++] = buffers[0][src_position];
+ buffer_prn[des_position++] = buffers[1][src_position];
+ buffer_prn[des_position++] = buffers[2][src_position];
+ /* Alpha data in low bits. Note that Alpha is inverted. */
+ buffer_prn[des_position++] = (255 - buffers[alpha_plane_index][src_position]);
+ src_position += 1;
+ }
+ }
+ return height; /* we used all of the data */
+}
+
+/* Implementation for 32-bit RGBA in a memory buffer */
+/* Derived from gx_default_copy_alpha, but now maintains alpha channel. */
+static int
+pngalpha_copy_alpha(gx_device * dev, const byte * data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int width, int height,
+ gx_color_index color, int depth)
+{ /* This might be called with depth = 1.... */
+ if (depth == 1)
+ return (*dev_proc(dev, copy_mono)) (dev, data, data_x, raster, id,
+ x, y, width, height,
+ gx_no_color_index, color);
+ /*
+ * Simulate alpha by weighted averaging of RGB values.
+ * This is very slow, but functionally correct.
+ */
+ {
+ const byte *row;
+ gs_memory_t *mem = dev->memory;
+ int bpp = dev->color_info.depth;
+ int ncomps = dev->color_info.num_components;
+ uint in_size = gx_device_raster(dev, false);
+ byte *lin;
+ uint out_size;
+ byte *lout;
+ int code = 0;
+ gx_color_value color_cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int ry;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ row = data;
+ out_size = bitmap_raster(width * bpp);
+ lin = gs_alloc_bytes(mem, in_size, "copy_alpha(lin)");
+ lout = gs_alloc_bytes(mem, out_size, "copy_alpha(lout)");
+ if (lin == 0 || lout == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ (*dev_proc(dev, decode_color)) (dev, color, color_cv);
+ for (ry = y; ry < y + height; row += raster, ++ry) {
+ byte *line;
+ int sx, rx;
+
+ byte *l_dptr = lout;
+ int l_dbit = 0;
+ byte l_dbyte = ((l_dbit) ? (byte)(*(l_dptr) & (0xff00 >> (l_dbit))) : 0);
+ int l_xprev = x;
+
+ code = (*dev_proc(dev, get_bits)) (dev, ry, lin, &line);
+ if (code < 0)
+ break;
+ for (sx = data_x, rx = x; sx < data_x + width; ++sx, ++rx) {
+ gx_color_index previous = gx_no_color_index;
+ gx_color_index composite;
+ uint32_t alpha2, alpha;
+
+ switch(depth)
+ {
+ case 2: /* map 0 - 3 to 0 - 255 */
+ alpha = ((row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 85;
+ break;
+ case 4:
+ alpha2 = row[sx >> 1];
+ alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4) * 17;
+ break;
+ case 8:
+ alpha = row[sx];
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (alpha == 255) { /* Just write the new color. */
+ composite = color;
+ } else {
+ if (previous == gx_no_color_index) { /* Extract the old color. */
+ const byte *src = line + (rx * (bpp >> 3));
+ previous = 0;
+ previous += (gx_color_index) * src++ << 24;
+ previous += (gx_color_index) * src++ << 16;
+ previous += (gx_color_index) * src++ << 8;
+ previous += *src++;
+ }
+ if (alpha == 0) { /* Just write the old color. */
+ composite = previous;
+ } else { /* Blend values. */
+ gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int i;
+ uint32_t old_coverage;
+ uint32_t new_coverage;
+
+ (*dev_proc(dev, decode_color)) (dev, previous, cv);
+ /* decode color doesn't give us coverage */
+ cv[3] = previous & 0xff;
+ old_coverage = 255 - cv[3];
+ new_coverage =
+ (255 * alpha + old_coverage * (255 - alpha)) / 255;
+ for (i=0; i<ncomps; i++)
+ cv[i] = min(((255 * alpha * color_cv[i]) +
+ (old_coverage * (255 - alpha ) * cv[i]))
+ / (new_coverage * 255), gx_max_color_value);
+ composite =
+ (*dev_proc(dev, encode_color)) (dev, cv);
+ /* encode color doesn't include coverage */
+ composite |= (255 - new_coverage) & 0xff;
+
+ /* composite can never be gx_no_color_index
+ * because pixel is never completely transparent
+ * (low byte != 0xff).
+ */
+ }
+ }
+ if (sizeof(composite) > 4) {
+ if (sample_store_next64(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0)
+ return_error(gs_error_rangecheck);
+ }
+ else {
+ if (sample_store_next32(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0)
+ return_error(gs_error_rangecheck);
+ }
+ }
+ if ( rx > l_xprev ) {
+ sample_store_flush(l_dptr, l_dbit, l_dbyte);
+ code = (*dev_proc(dev, copy_color))
+ (dev, lout, l_xprev - x, raster,
+ gx_no_bitmap_id, l_xprev, ry, rx - l_xprev, 1);
+ if (code < 0)
+ return code;
+ }
+ }
+ out:gs_free_object(mem, lout, "copy_alpha(lout)");
+ gs_free_object(mem, lin, "copy_alpha(lin)");
+ return code;
+ }
+}