Implementation of 687496 transparency and CMYK support

"Dan Coby" <[email protected]> Sat, 31 Jul 2004 00:09:09 -0700
Newsgroups gmane.comp.printing.ghostscript.patches
Message-ID <[email protected]>
A note to Raph:  I had to remove the color management from the
test files that you sent.  It was causing problems.  However I think
that it is really a separate problem related to our handling of
color management.


Log message:

Implementation of 687496 transparency and CMYK support.

DETAILS:

This change extends our current implementation of the PDF 1.4
transparency to include blending in a DeviceCMYK color space.
There is also a partial implementation of a DeviceGray blending
space.  However I do not have a method of generating test data
for a DeviceGray blending space.  Currently the choice of a
blending color space is based upon the process color model of
the output device.


Dan

_______________________________________________
gs-code-review mailing list
[email protected]
http://www.ghostscript.com/mailman/listinfo/gs-code-review
687496.txt (text/plain, 31.1 KB)
Index: src/gdevp14.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gdevp14.c,v
retrieving revision 1.23
diff -u -r1.23 gdevp14.c
--- src/gdevp14.c	26 May 2004 04:10:58 -0000	1.23
+++ src/gdevp14.c	31 Jul 2004 05:30:18 -0000
@@ -39,6 +39,8 @@
 #include "gzstate.h"
 #include "gdevp14.h"
 #include "gsovrc.h"
+#include "gxcmap.h"
+#include "gscolor1.h"
 
 /* #define DUMP_TO_PNG */
 
@@ -52,6 +54,12 @@
 
 #define PDF14_MAX_PLANES 16
 
+typedef enum {
+    DeviceGray = 0,
+    DeviceRGB = 1,
+    DeviceCMYK = 2
+} pdf14_default_colorspace_t;
+
 typedef struct pdf14_buf_s pdf14_buf;
 typedef struct pdf14_ctx_s pdf14_ctx;
 
@@ -89,6 +97,7 @@
     pdf14_buf *maskbuf;
     gs_memory_t *memory;
     gs_int_rect rect;
+    bool additive;
     int n_chan;
 };
 
@@ -129,6 +138,8 @@
 private int pdf14_open(gx_device * pdev);
 private dev_proc_close_device(pdf14_close);
 private int pdf14_output_page(gx_device * pdev, int num_copies, int flush);
+private dev_proc_encode_color(pdf14_encode_color);
+private dev_proc_decode_color(pdf14_decode_color);
 private dev_proc_fill_rectangle(pdf14_fill_rectangle);
 private dev_proc_fill_path(pdf14_fill_path);
 private dev_proc_stroke_path(pdf14_stroke_path);
@@ -145,61 +156,74 @@
 
 /* 24-bit color. */
 
-private const gx_device_procs pdf14_procs =
-{
-	pdf14_open,			/* open */
-	NULL,	/* get_initial_matrix */
-	NULL,	/* sync_output */
-	pdf14_output_page,		/* output_page */
-	pdf14_close,			/* close */
-	gx_default_rgb_map_rgb_color,
-	gx_default_rgb_map_color_rgb,
-	pdf14_fill_rectangle,	/* fill_rectangle */
-	NULL,	/* tile_rectangle */
-	NULL,	/* copy_mono */
-	NULL,	/* copy_color */
-	NULL,	/* draw_line */
-	NULL,	/* get_bits */
-	NULL,   /* get_params */
-	NULL,   /* put_params */
-	NULL,	/* map_cmyk_color */
-	NULL,	/* get_xfont_procs */
-	NULL,	/* get_xfont_device */
-	NULL,	/* map_rgb_alpha_color */
-#if 0
-	gx_page_device_get_page_device,	/* get_page_device */
-#else
-	NULL,   /* get_page_device */
-#endif
-	NULL,	/* get_alpha_bits */
-	NULL,	/* copy_alpha */
-	NULL,	/* get_band */
-	NULL,	/* copy_rop */
-	pdf14_fill_path,		/* fill_path */
-	pdf14_stroke_path,	/* 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 */
-	pdf14_begin_typed_image,	/* begin_typed_image */
-	NULL,	/* get_bits_rectangle */
-	NULL,	/* map_color_rgb_alpha */
-	pdf14_create_compositor,	/* create_compositor */
-	NULL,	/* get_hardware_params */
-	pdf14_text_begin,	/* text_begin */
-	NULL,	/* finish_copydevice */
-	pdf14_begin_transparency_group,
-	pdf14_end_transparency_group,
-	pdf14_begin_transparency_mask,
-	pdf14_end_transparency_mask
-};
+#define pdf14_procs(get_color_mapping_procs, get_color_comp_index) \
+{\
+	pdf14_open,			/* open */\
+	NULL,				/* get_initial_matrix */\
+	NULL,				/* sync_output */\
+	pdf14_output_page,		/* output_page */\
+	pdf14_close,			/* close */\
+	pdf14_encode_color,		/* rgb_map_rgb_color */\
+	pdf14_decode_color,		/* gx_default_rgb_map_color_rgb */\
+	pdf14_fill_rectangle,		/* fill_rectangle */\
+	NULL,				/* tile_rectangle */\
+	NULL,				/* copy_mono */\
+	NULL,				/* copy_color */\
+	NULL,				/* draw_line */\
+	NULL,				/* get_bits */\
+	NULL,				/* get_params */\
+	NULL,				/* put_params */\
+	NULL,				/* map_cmyk_color */\
+	NULL,				/* get_xfont_procs */\
+	NULL,				/* get_xfont_device */\
+	NULL,				/* map_rgb_alpha_color */\
+	NULL,				/* get_page_device */\
+	NULL,				/* get_alpha_bits */\
+	NULL,				/* copy_alpha */\
+	NULL,				/* get_band */\
+	NULL,				/* copy_rop */\
+	pdf14_fill_path,		/* fill_path */\
+	pdf14_stroke_path,		/* 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 */\
+	pdf14_begin_typed_image,	/* begin_typed_image */\
+	NULL,				/* get_bits_rectangle */\
+	NULL,				/* map_color_rgb_alpha */\
+	pdf14_create_compositor,	/* create_compositor */\
+	NULL,				/* get_hardware_params */\
+	pdf14_text_begin,		/* text_begin */\
+	NULL,				/* finish_copydevice */\
+	pdf14_begin_transparency_group,\
+	pdf14_end_transparency_group,\
+	pdf14_begin_transparency_mask,\
+	pdf14_end_transparency_mask,\
+	NULL,				/* discard_transparency_layer */\
+	get_color_mapping_procs,	/* get_color_mapping_procs */\
+	get_color_comp_index,		/* get_color_comp_index */\
+	pdf14_encode_color,		/* encode_color */\
+	pdf14_decode_color		/* decode_color */\
+}
+
+private const gx_device_procs pdf14_Gray_procs =
+	pdf14_procs(gx_default_DevGray_get_color_mapping_procs,
+			gx_default_DevGray_get_color_comp_index);
+
+private const gx_device_procs pdf14_RGB_procs =
+	pdf14_procs(gx_default_DevRGB_get_color_mapping_procs,
+			gx_default_DevRGB_get_color_comp_index);
+
+private const gx_device_procs pdf14_CMYK_procs =
+	pdf14_procs(gx_default_DevCMYK_get_color_mapping_procs,
+			gx_default_DevCMYK_get_color_comp_index);
 
 typedef struct pdf14_device_s {
     gx_device_common;
@@ -215,10 +239,24 @@
 				  pdf14_device_enum_ptrs, pdf14_device_reloc_ptrs,
 			  gx_device_finalize);
 
-const pdf14_device gs_pdf14_device = {
-    std_device_color_stype_body(pdf14_device, &pdf14_procs, "pdf14",
+const pdf14_device gs_pdf14_Gray_device = {
+    std_device_color_stype_body(pdf14_device, &pdf14_Gray_procs, "pdf14gray",
+				&st_pdf14_device,
+				XSIZE, YSIZE, X_DPI, Y_DPI, 8, 255, 255),
+    { 0 }
+};
+
+const pdf14_device gs_pdf14_RGB_device = {
+    std_device_color_stype_body(pdf14_device, &pdf14_RGB_procs, "pdf14RGB",
 				&st_pdf14_device,
-				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
+				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 255),
+    { 0 }
+};
+
+const pdf14_device gs_pdf14_CMYK_device = {
+    std_device_std_color_full_body_type(pdf14_device, &pdf14_CMYK_procs,
+	"PDF14cmyk", &st_pdf14_device, XSIZE, YSIZE, X_DPI, Y_DPI, 32,
+	0, 0, 0, 0, 0, 0),
     { 0 }
 };
 
@@ -240,57 +278,74 @@
 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle);
 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle_ko_simple);
 
-private const gx_device_procs pdf14_mark_procs =
-{
-	NULL,	/* open */
-	NULL,	/* get_initial_matrix */
-	NULL,	/* sync_output */
-	NULL,	/* output_page */
-	NULL,	/* close */
-	gx_default_rgb_map_rgb_color,
-	gx_default_rgb_map_color_rgb,
-	NULL,	/* fill_rectangle */
-	NULL,	/* tile_rectangle */
-	NULL,	/* copy_mono */
-	NULL,	/* copy_color */
-	NULL,	/* draw_line */
-	NULL,	/* get_bits */
-	NULL,   /* get_params */
-	NULL,   /* put_params */
-	NULL,	/* map_cmyk_color */
-	NULL,	/* get_xfont_procs */
-	NULL,	/* get_xfont_device */
-	NULL,	/* map_rgb_alpha_color */
-#if 0
-	gx_page_device_get_page_device,	/* get_page_device */
-#else
-	NULL,   /* get_page_device */
-#endif
-	NULL,	/* get_alpha_bits */
-	NULL,	/* 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 */
-};
+#define pdf14_mark_procs(get_color_mapping_procs, get_color_comp_index) \
+{\
+	NULL,				/* open */\
+	NULL,				/* get_initial_matrix */\
+	NULL,				/* sync_output */\
+	NULL,				/* output_page */\
+	NULL,				/* close */\
+	pdf14_encode_color,\
+	pdf14_decode_color,\
+	NULL,				/* fill_rectangle */\
+	NULL,				/* tile_rectangle */\
+	NULL,				/* copy_mono */\
+	NULL,				/* copy_color */\
+	NULL,				/* draw_line */\
+	NULL,				/* get_bits */\
+	NULL,				/* get_params */\
+	NULL,				/* put_params */\
+	NULL,				/* map_cmyk_color */\
+	NULL,				/* get_xfont_procs */\
+	NULL,				/* get_xfont_device */\
+	NULL,				/* map_rgb_alpha_color */\
+	NULL,				/* get_page_device */\
+	NULL,				/* get_alpha_bits */\
+	NULL,				/* 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 */\
+	get_color_mapping_procs,	/* get_color_mapping_procs */\
+	get_color_comp_index,		/* get_color_comp_index */\
+	pdf14_encode_color,		/* encode_color */\
+	pdf14_decode_color		/* decode_color */\
+}
+
+private const gx_device_procs pdf14_mark_Gray_procs =
+	pdf14_mark_procs(gx_default_DevGray_get_color_mapping_procs,
+			gx_default_DevGray_get_color_comp_index);
+
+private const gx_device_procs pdf14_mark_RGB_procs =
+	pdf14_mark_procs(gx_default_DevRGB_get_color_mapping_procs,
+			gx_default_DevRGB_get_color_comp_index);
+
+private const gx_device_procs pdf14_mark_CMYK_procs =
+	pdf14_mark_procs(gx_default_DevCMYK_get_color_mapping_procs,
+			gx_default_DevCMYK_get_color_comp_index);
 
 typedef struct pdf14_mark_device_s {
     gx_device_common;
@@ -305,11 +360,24 @@
 gs_private_st_simple_final(st_pdf14_mark_device, pdf14_mark_device,
 			   "pdf14_mark_device", gx_device_finalize);
 
-const pdf14_mark_device gs_pdf14_mark_device = {
-    std_device_color_stype_body(pdf14_mark_device, &pdf14_mark_procs,
-				"pdf14_mark",
-				&st_pdf14_mark_device,
-				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
+const pdf14_mark_device gs_pdf14_mark_Gray_device = {
+    std_device_color_stype_body(pdf14_mark_device, &pdf14_mark_Gray_procs,
+		    		"pd14_mark_gray", &st_pdf14_mark_device,
+				XSIZE, YSIZE, X_DPI, Y_DPI, 8, 255, 255),
+    { 0 }
+};
+
+const pdf14_mark_device gs_pdf14_mark_RGB_device = {
+    std_device_color_stype_body(pdf14_mark_device, &pdf14_mark_RGB_procs,
+		    		"pd14_mark_RGB", &st_pdf14_mark_device,
+				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 255),
+    { 0 }
+};
+
+const pdf14_mark_device gs_pdf14_mark_CMYK_device = {
+    std_device_std_color_full_body_type(pdf14_mark_device, &pdf14_mark_CMYK_procs,
+	"PDF14_mark_cmyk", &st_pdf14_mark_device, XSIZE, YSIZE, X_DPI, Y_DPI, 32,
+	0, 0, 0, 0, 0, 0),
     { 0 }
 };
 
@@ -381,7 +449,7 @@
 }
 
 private pdf14_ctx *
-pdf14_ctx_new(gs_int_rect *rect, int n_chan, gs_memory_t *memory)
+pdf14_ctx_new(gs_int_rect *rect, int n_chan, bool additive, gs_memory_t *memory)
 {
     pdf14_ctx *result;
     pdf14_buf *buf;
@@ -405,6 +473,7 @@
     result->n_chan = n_chan;
     result->memory = memory;
     result->rect = *rect;
+    result->additive = additive;
     return result;
 }
 
@@ -515,6 +584,7 @@
     pdf14_buf *nos = tos->saved;
     pdf14_buf *maskbuf = ctx->maskbuf;
     int n_chan = ctx->n_chan;
+    int num_comp = n_chan - 1;
     byte alpha = tos->alpha;
     byte shape = tos->shape;
     byte blend_mode = tos->blend_mode;
@@ -544,6 +614,7 @@
     int nos_shape_offset = n_chan * nos_planestride;
     bool nos_has_shape = nos->has_shape;
     byte *mask_tr_fn = NULL; /* Quiet compiler. */
+    bool additive = ctx->additive;
 
     if (nos == NULL)
 	return_error(gs_error_rangecheck);
@@ -566,13 +637,25 @@
     for (y = y0; y < y1; ++y) {
 	for (x = 0; x < width; ++x) {
 	    byte pix_alpha = alpha;
-	    for (i = 0; i < n_chan; ++i) {
-		tos_pixel[i] = tos_ptr[x + i * tos_planestride];
-		nos_pixel[i] = nos_ptr[x + i * nos_planestride];
+
+	    /* Complement the components for subtractive color spaces */
+	    if (additive) {
+	        for (i = 0; i < n_chan; ++i) {
+		    tos_pixel[i] = tos_ptr[x + i * tos_planestride];
+		    nos_pixel[i] = nos_ptr[x + i * nos_planestride];
+	        }
+	    }
+	    else {
+	        for (i = 0; i < num_comp; ++i) {
+		    tos_pixel[i] = 255 - tos_ptr[x + i * tos_planestride];
+		    nos_pixel[i] = 255 - nos_ptr[x + i * nos_planestride];
+	        }
+		tos_pixel[num_comp] = tos_ptr[x + num_comp * tos_planestride];
+		nos_pixel[num_comp] = nos_ptr[x + num_comp * nos_planestride];
 	    }
 
 	    if (mask_ptr != NULL) {
-		int mask_alpha = mask_ptr[x + 3 * mask_planestride];
+		int mask_alpha = mask_ptr[x + num_comp * mask_planestride];
 		int tmp;
 		byte mask;
 
@@ -584,7 +667,7 @@
 		    int t2 = (mask_ptr[x] - mask_bg_alpha) * mask_alpha + 0x80;
 		    mask = mask_bg_alpha + ((t2 + (t2 >> 8)) >> 8);
 		}
-		mask = mask_tr_fn[mask];
+	 	mask = mask_tr_fn[mask];
 		tmp = pix_alpha * mask + 0x80;
 		pix_alpha = (tmp + (tmp >> 8)) >> 8;
 	    }
@@ -619,8 +702,16 @@
 					 shape);
 	    }
 	    
-	    for (i = 0; i < n_chan; ++i) {
-		nos_ptr[x + i * nos_planestride] = nos_pixel[i];
+	    /* Complement the results for subtractive color spaces */
+	    if (additive) {
+	        for (i = 0; i < n_chan; ++i) {
+		    nos_ptr[x + i * nos_planestride] = nos_pixel[i];
+	        }
+	    }
+	    else {
+	        for (i = 0; i < num_comp; ++i)
+		    nos_ptr[x + i * nos_planestride] = 255 - nos_pixel[i];
+		nos_ptr[x + num_comp * nos_planestride] = nos_pixel[num_comp];
 	    }
 	    if (nos_alpha_g_ptr != NULL)
 		++nos_alpha_g_ptr;
@@ -690,10 +781,44 @@
     rect.p.y = 0;
     rect.q.x = dev->width;
     rect.q.y = dev->height;
-    pdev->ctx = pdf14_ctx_new(&rect, 4, dev->memory);
+    pdev->ctx = pdf14_ctx_new(&rect, dev->color_info.num_components + 1,
+	pdev->color_info.polarity != GX_CINFO_POLARITY_SUBTRACTIVE, dev->memory);
     if (pdev->ctx == NULL)
 	return_error(gs_error_VMerror);
+    return 0;
+}
 
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+private gx_color_index
+pdf14_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+    int drop = sizeof(gx_color_value) * 8 - 8;
+    gx_color_index color = 0;
+    int i;
+    int ncomp = dev->color_info.num_components;
+
+    for (i = 0; i < ncomp; i++) {
+	color <<= 8;
+        color |= (colors[i] >> drop);
+    }
+    return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+private int
+pdf14_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
+{
+    int i;
+    int ncomp = dev->color_info.num_components;
+
+    for (i = 0; i < ncomp; i++) {
+        out[ncomp - i - 1] = (gx_color_value) ((color & 0xff) * 0x101);
+	color >>= 8;
+    }
     return 0;
 }
 
@@ -818,11 +943,12 @@
     gs_imager_state *pis = (gs_imager_state *)pgs;
     int y;
     pdf14_buf *buf = pdev->ctx->stack;
-
     int planestride = buf->planestride;
+    int num_comp = buf->n_chan - 1;
     byte *buf_ptr = buf->data;
     byte *linebuf;
     gs_color_space cs;
+    const byte bg = pdev->ctx->additive ? 255 : 0;
 
 #ifdef DUMP_TO_PNG
     dump_planar_rgba(buf_ptr, width, height,
@@ -836,10 +962,27 @@
     gs_setdevice_no_init(pgs, target);
 #endif
 
-    /* Set color space to RGB, in preparation for sending an RGB image. */
-    gs_setrgbcolor(pgs, 0, 0, 0);
-
-    gs_cspace_init_DeviceRGB(&cs);
+    /*
+     * Set color space to either Gray, RGB, or CMYK in preparation for sending
+     * an image.
+     */
+    switch (num_comp) {
+	case 1:				/* DeviceGray */
+	    gs_setgray(pgs, 0);
+	    gs_cspace_init_DeviceGray(&cs);
+	    break;
+	case 3:				/* DeviceRGB */
+	    gs_setrgbcolor(pgs, 0, 0, 0);
+	    gs_cspace_init_DeviceRGB(&cs);
+	    break;
+	case 4:				/* DeviceCMYK */
+	    gs_setcmykcolor(pgs, 0, 0, 0, 0);
+	    gs_cspace_init_DeviceCMYK(&cs);
+	    break;
+	default:			/* Should never occur */
+	    return_error(gs_error_rangecheck);
+	    break;
+    }
     gx_set_dev_color(pgs);
     gs_image_t_init_adjust(&image, &cs, false);
     image.ImageMatrix.xx = (float)width;
@@ -861,54 +1004,42 @@
     if (code < 0)
 	return code;
 
-    linebuf = gs_alloc_bytes(pdev->memory, width * 3, "pdf14_put_image");
+    linebuf = gs_alloc_bytes(pdev->memory, width * num_comp, "pdf14_put_image");
     for (y = 0; y < height; y++) {
 	gx_image_plane_t planes;
 	int x;
 	int rows_used;
 
 	for (x = 0; x < width; x++) {
-	    const byte bg_r = 0xff, bg_g = 0xff, bg_b = 0xff;
-	    byte r, g, b, a;
-	    int tmp;
+	    byte comp, a;
+	    int tmp, comp_num;
 
-	    /* composite RGBA pixel with over solid background */
-	    a = buf_ptr[x + planestride * 3];
+	    /* composite RGBA (or CMYKA, etc.) pixel with over solid background */
+	    a = buf_ptr[x + planestride * num_comp];
 
 	    if ((a + 1) & 0xfe) {
-		r = buf_ptr[x];
-		g = buf_ptr[x + planestride];
-		b = buf_ptr[x + planestride * 2];
 		a ^= 0xff;
-
-		tmp = ((bg_r - r) * a) + 0x80;
-		r += (tmp + (tmp >> 8)) >> 8;
-		linebuf[x * 3] = r;
-
-		tmp = ((bg_g - g) * a) + 0x80;
-		g += (tmp + (tmp >> 8)) >> 8;
-		linebuf[x * 3 + 1] = g;
-
-		tmp = ((bg_b - b) * a) + 0x80;
-		b += (tmp + (tmp >> 8)) >> 8;
-		linebuf[x * 3 + 2] = b;
+		for (comp_num = 0; comp_num < num_comp; comp_num++) {
+		    comp  = buf_ptr[x + planestride * comp_num];
+		    tmp = ((bg - comp) * a) + 0x80;
+		    comp += (tmp + (tmp >> 8)) >> 8;
+		    linebuf[x * num_comp + comp_num] = comp;
+		}
 	    } else if (a == 0) {
-		linebuf[x * 3] = bg_r;
-		linebuf[x * 3 + 1] = bg_g;
-		linebuf[x * 3 + 2] = bg_b;
+		for (comp_num = 0; comp_num < num_comp; comp_num++) {
+		    linebuf[x * num_comp + comp_num] = bg;
+		}
 	    } else {
-		r = buf_ptr[x];
-		g = buf_ptr[x + planestride];
-		b = buf_ptr[x + planestride * 2];
-		linebuf[x * 3 + 0] = r;
-		linebuf[x * 3 + 1] = g;
-		linebuf[x * 3 + 2] = b;
+		for (comp_num = 0; comp_num < num_comp; comp_num++) {
+		    comp = buf_ptr[x + planestride * comp_num];
+		    linebuf[x * num_comp + comp_num] = comp;
+		}
 	    }
 	}
 
 	planes.data = linebuf;
 	planes.data_x = 0;
-	planes.raster = width * 3;
+	planes.raster = width * num_comp;
 	info->procs->plane_data(info, &planes, 1, &rows_used);
 	/* todo: check return value */
 
@@ -999,10 +1130,25 @@
     pdf14_device *pdev = (pdf14_device *)dev;
     pdf14_buf *buf = pdev->ctx->stack;
     pdf14_mark_device *mdev;
-    int code = gs_copydevice((gx_device **)&mdev,
-			     (const gx_device *)&gs_pdf14_mark_device,
-			     dev->memory);
+    const pdf14_mark_device * dev_proto;
+    int num_comp = buf->n_chan - 1;
+    int code;
 
+    switch (num_comp) {
+	case 1:				/* DeviceGray */
+	    dev_proto = &gs_pdf14_mark_Gray_device;
+	    break;
+	case 3:				/* DeviceRGB */
+	    dev_proto = &gs_pdf14_mark_RGB_device;
+	    break;
+	case 4:				/* DeviceCMYK */
+	    dev_proto = &gs_pdf14_mark_CMYK_device;
+	    break;
+	default:			/* Should not occur */
+	    return NULL;
+    }
+    code = gs_copydevice((gx_device **)&mdev,
+			     (const gx_device *)dev_proto, dev->memory);
     if (code < 0)
 	return NULL;
 
@@ -1205,7 +1351,7 @@
 	bg_alpha = (int)(255 * ptmp->Background[0] + 0.5);
     if_debug1('v', "begin transparency mask, bg_alpha = %d\n", bg_alpha);
     for (i = 0; i < 256; i++) {
-	float in = i * (1.0 / 255.0);
+	float in = (float)(i * (1.0 / 255.0));
 	float out;
 
 	ptmp->TransferFunction(in, &out, ptmp->TransferFunction_data);
@@ -1237,18 +1383,32 @@
     byte src[PDF14_MAX_PLANES];
     byte dst[PDF14_MAX_PLANES];
     gs_blend_mode_t blend_mode = mdev->blend_mode;
+    bool additive = pdev->ctx->additive;
     int rowstride = buf->rowstride;
     int planestride = buf->planestride;
     bool has_alpha_g = buf->has_alpha_g;
     bool has_shape = buf->has_shape;
-    int shape_off = buf->n_chan * planestride;
+    int num_chan = buf->n_chan;
+    int num_comp = num_chan - 1;
+    int shape_off = num_chan * planestride;
     int alpha_g_off = shape_off + (has_shape ? planestride : 0);
     byte shape = 0; /* Quiet compiler. */
+    byte src_alpha;
 
-    src[0] = color >> 16;
-    src[1] = (color >> 8) & 0xff;
-    src[2] = color & 0xff;
-    src[3] = (byte)floor (255 * mdev->alpha + 0.5);
+    /* Complement the components for subtractive color spaces */
+    if (additive) {
+        for (i = num_comp - 1; i >= 0; i--) {
+            src[i] = (byte)(color & 0xff);
+	    color >>= 8;
+        }
+    }
+    else {
+        for (i = num_comp - 1; i >= 0; i--) {
+            src[i] = (byte)(0xff - (color & 0xff));
+	    color >>= 8;
+        }
+    }
+    src_alpha = src[num_comp] = (byte)floor (255 * mdev->alpha + 0.5);
     if (has_shape)
 	shape = (byte)floor (255 * mdev->shape + 0.5);
 
@@ -1267,13 +1427,29 @@
     for (j = 0; j < h; ++j) {
 	dst_ptr = line;
 	for (i = 0; i < w; ++i) {
-	    for (k = 0; k < 4; ++k)
-		dst[k] = dst_ptr[k * planestride];
-	    art_pdf_composite_pixel_alpha_8(dst, src, 3, blend_mode);
-	    for (k = 0; k < 4; ++k)
-		dst_ptr[k * planestride] = dst[k];
+	    /* Complement the components for subtractive color spaces */
+	    if (additive) {
+	        for (k = 0; k < num_chan; ++k)
+		    dst[k] = dst_ptr[k * planestride];
+	    }
+	    else { /* Complement the components for subtractive color spaces */
+	        for (k = 0; k < num_comp; ++k)
+		    dst[k] = 255 - dst_ptr[k * planestride];
+		dst[num_comp] = dst_ptr[num_comp * planestride];
+	    }
+	    art_pdf_composite_pixel_alpha_8(dst, src, num_comp, blend_mode);
+	    /* Complement the results for subtractive color spaces */
+	    if (additive) {
+	        for (k = 0; k < num_chan; ++k)
+		    dst_ptr[k * planestride] = dst[k];
+	    }
+	    else {
+	        for (k = 0; k < num_comp; ++k)
+		    dst_ptr[k * planestride] = 255 - dst[k];
+		dst_ptr[num_comp * planestride] = dst[num_comp];
+	    }
 	    if (has_alpha_g) {
-		int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src[3]) + 0x80;
+		int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src_alpha) + 0x80;
 		dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
 	    }
 	    if (has_shape) {
@@ -1300,14 +1476,27 @@
     byte dst[PDF14_MAX_PLANES];
     int rowstride = buf->rowstride;
     int planestride = buf->planestride;
-    int shape_off = buf->n_chan * planestride;
+    int num_chan = buf->n_chan;
+    int num_comp = num_chan - 1;
+    int shape_off = num_chan * planestride;
     bool has_shape = buf->has_shape;
     byte opacity;
+    bool additive = pdev->ctx->additive;
 
-    src[0] = color >> 16;
-    src[1] = (color >> 8) & 0xff;
-    src[2] = color & 0xff;
-    src[3] = (byte)floor (255 * mdev->shape + 0.5);
+    /* Complement the components for subtractive color spaces */
+    if (additive) {
+        for (i = num_comp - 1; i >= 0; i--) {
+            src[i] = (byte)(color & 0xff);
+	    color >>= 8;
+        }
+    }
+    else {
+        for (i = num_comp - 1; i >= 0; i--) {
+            src[i] = (byte)(0xff - (color & 0xff));
+	    color >>= 8;
+        }
+    }
+    src[num_comp] = (byte)floor (255 * mdev->alpha + 0.5);
     opacity = (byte)floor (255 * mdev->opacity + 0.5);
 
     if (x < buf->rect.p.x) x = buf->rect.p.x;
@@ -1325,12 +1514,28 @@
     for (j = 0; j < h; ++j) {
 	dst_ptr = line;
 	for (i = 0; i < w; ++i) {
-	    for (k = 0; k < 4; ++k)
-		dst[k] = dst_ptr[k * planestride];
-	    art_pdf_composite_knockout_simple_8(dst, has_shape ? dst_ptr + shape_off : NULL,
-						src, 3, opacity);
-	    for (k = 0; k < 4; ++k)
-		dst_ptr[k * planestride] = dst[k];
+	    /* Complement the components for subtractive color spaces */
+	    if (additive) {
+	        for (k = 0; k < num_chan; ++k)
+		    dst[k] = dst_ptr[k * planestride];
+	    }
+	    else {
+	        for (k = 0; k < num_comp; ++k)
+		    dst[k] = 255 - dst_ptr[k * planestride];
+		dst[num_comp] = dst_ptr[num_comp * planestride];
+	    }
+	    art_pdf_composite_knockout_simple_8(dst,
+		has_shape ? dst_ptr + shape_off : NULL, src, num_comp, opacity);
+	    /* Complement the results for subtractive color spaces */
+	    if (additive) {
+	        for (k = 0; k < num_chan; ++k)
+		    dst_ptr[k * planestride] = dst[k];
+	    }
+	    else {
+	        for (k = 0; k < num_comp; ++k)
+		    dst_ptr[k * planestride] = 255 - dst[k];
+		dst_ptr[num_comp * planestride] = dst[num_comp];
+	    }
 	    ++dst_ptr;
 	}
 	line += rowstride;
@@ -1585,16 +1790,58 @@
     return &pdf14_cmap_many;
 }
 
+/*
+ * The default color space for PDF 1.4 blend modes is based upon the process
+ * color model of the output device.
+ */
+private pdf14_default_colorspace_t
+pdf14_determine_default_blend_cs(gx_device * pdev)
+{
+    if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE)
+	/* Use DeviceCMYK for all subrtactive process color models. */
+	return DeviceCMYK;
+    else {
+	/*
+	 * Note:  We do not allow the SeparationOrder device parameter for
+	 * additive devices.  Thus we always have 1 colorant for DeviceGray
+	 * and 3 colorants for DeviceRGB.  We do not currently support
+	 * blending in a DeviceGray color space.  Thus we oniy use DeviceRGB.
+	 */
+	return DeviceRGB;
+    }
+}
+
 private int
 gs_pdf14_device_filter_push(gs_device_filter_t *self, gs_memory_t *mem,
 			    gs_state *pgs, gx_device **pdev, gx_device *target)
 {
+    const pdf14_device * dev_proto;
     pdf14_device *p14dev;
     int code;
+    pdf14_default_colorspace_t dev_cs =
+		pdf14_determine_default_blend_cs(target);
 
+    /*
+     * the PDF 1.4 transparency spec says that color space for blending
+     * operations can be based upon either a color space specified in the
+     * group or a default value based upon the output device.  We are
+     * currently only using a color space based upon the device.
+     */
+    switch (dev_cs) {
+	case DeviceGray:
+	    dev_proto = &gs_pdf14_Gray_device;
+	    break;
+	case DeviceRGB:
+	    dev_proto = &gs_pdf14_RGB_device;
+	    break;
+	case DeviceCMYK:
+	    dev_proto = &gs_pdf14_CMYK_device;
+	    break;
+	default:			/* Should not occur */
+	    return_error(gs_error_rangecheck);
+    }
     code = gs_copydevice((gx_device **) &p14dev,
-			 (const gx_device *) &gs_pdf14_device,
-			 mem);
+			 (const gx_device *) dev_proto, mem);
     if (code < 0)
 	return code;
 
Index: src/gxblend.c
===================================================================
RCS file: /cvs/ghostscript/gs/src/gxblend.c,v
retrieving revision 1.5
diff -u -r1.5 gxblend.c
--- src/gxblend.c	10 Jul 2003 18:42:02 -0000	1.5
+++ src/gxblend.c	31 Jul 2004 05:30:19 -0000
@@ -35,6 +35,10 @@
     int delta_y;
     int r, g, b;
 
+    /*
+     * From section 7.4 of the PDF 1.5 specification, for RGB, the luminosity
+     * is:  Y = 0.30 R + 0.59 G + 0.11 B)
+     */
     delta_y = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8;
     r = rb + delta_y;
     g = gb + delta_y;
@@ -66,6 +70,34 @@
     dst[2] = b;
 }
 
+/*
+ * The PDF 1.4 spec. does not give the details of the math involved in the
+ * luminosity blending.  All we are given is:
+ *   "Creates a color with the luminance of the source color and the hue
+ *    and saturation of the backdrop color. This produces an inverse
+ *    effect to that of the Color mode."
+ * From section 7.4 of the PDF 1.5 specification, which is duscussing soft
+ * masks, we are given that, for CMYK, the luminosity is:
+ *    Y = 0.30 (1 - C)(1 - K) + 0.59 (1 - M)(1 - K) + 0.11 (1 - Y)(1 - K)
+ * However the results of this equation do not match the results seen from
+ * Illustrator CS.  Very different results are obtained if process gray
+ * (.5, .5, .5, 0) is blended over pure cyan, versus gray (0, 0, 0, .5) over
+ * the same pure cyan.  The first gives a medium cyan while the later gives a
+ * medium gray.  This routine seems to match Illustrator's actions.  C, M and Y
+ * are treated similar to RGB in the previous routine and black is treated
+ * separately.
+ *
+ * Our component values have already been complemented, i.e. (1 - X).
+ */
+static void
+art_blend_luminosity_cmyk_8(byte *dst, const byte *backdrop,
+			   const byte *src)
+{
+    /* Treat CMY the same as RGB. */
+    art_blend_luminosity_rgb_8(dst, backdrop, src);
+    dst[3] = src[3];
+}
+
 static void
 art_blend_saturation_rgb_8(byte *dst, const byte *backdrop,
 			   const byte *src)
@@ -131,6 +163,16 @@
     dst[2] = b;
 }
 
+/* Our component values have already been complemented, i.e. (1 - X). */
+static void
+art_blend_saturation_cmyk_8(byte *dst, const byte *backdrop,
+			   const byte *src)
+{
+    /* Treat CMY the same as RGB */
+    art_blend_saturation_rgb_8(dst, backdrop, src);
+    dst[3] = backdrop[3];
+}
+
 /* This array consists of floor ((x - x * x / 255.0) * 65536 / 255 +
    0.5) for x in [0..255]. */
 const unsigned int art_blend_sq_diff_8[256] = {
@@ -328,20 +370,73 @@
 	    }
 	    break;
 	case BLEND_MODE_Luminosity:
-	    art_blend_luminosity_rgb_8(dst, backdrop, src);
+	    switch (n_chan) {
+		case 1:			/* DeviceGray */
+	    	    dlprintf(
+			"art_blend_pixel_8: DeviceGray luminosity blend mode not implemented\n");
+		    break;
+		case 3:			/* DeviceRGB */
+	    	    art_blend_luminosity_rgb_8(dst, backdrop, src);
+		    break;
+		case 4:			/* DeviceCMYK */
+	    	    art_blend_luminosity_cmyk_8(dst, backdrop, src);
+		    break;
+		default:		/* Should not happen */
+		    break;
+	    }
 	    break;
 	case BLEND_MODE_Color:
-	    art_blend_luminosity_rgb_8(dst, src, backdrop);
+	    switch (n_chan) {
+		case 1:			/* DeviceGray */
+	    	    dlprintf(
+			"art_blend_pixel_8: DeviceGray color blend mode not implemented\n");
+		    break;
+		case 3:			/* DeviceRGB */
+		    art_blend_luminosity_rgb_8(dst, src, backdrop);
+		    break;
+		case 4:			/* DeviceCMYK */
+		    art_blend_luminosity_cmyk_8(dst, src, backdrop);
+		    break;
+		default:		/* Should not happen */
+		    break;
+	    }
 	    break;
 	case BLEND_MODE_Saturation:
-	    art_blend_saturation_rgb_8(dst, backdrop, src);
+	    switch (n_chan) {
+		case 1:			/* DeviceGray */
+	    	    dlprintf(
+			"art_blend_pixel_8: DeviceGray saturation blend mode not implemented\n");
+		    break;
+		case 3:			/* DeviceRGB */
+	    	    art_blend_saturation_rgb_8(dst, backdrop, src);
+		    break;
+		case 4:			/* DeviceCMYK */
+	    	    art_blend_saturation_cmyk_8(dst, backdrop, src);
+		    break;
+		default:		/* Should not happen */
+		    break;
+	    }
 	    break;
 	case BLEND_MODE_Hue:
 	    {
-		byte tmp[3];
+		byte tmp[4];
 
-		art_blend_luminosity_rgb_8(tmp, src, backdrop);
-		art_blend_saturation_rgb_8(dst, tmp, backdrop);
+	        switch (n_chan) {
+		    case 1:		/* DeviceGray */
+	    		dlprintf(
+			    "art_blend_pixel_8: DeviceGray hue blend mode not implemented\n");
+		        break;
+		    case 3:		/* DeviceRGB */
+			art_blend_luminosity_rgb_8(tmp, src, backdrop);
+			art_blend_saturation_rgb_8(dst, tmp, backdrop);
+		        break;
+		    case 4:		/* DeviceCMYK */
+		        art_blend_luminosity_cmyk_8(tmp, src, backdrop);
+			art_blend_saturation_cmyk_8(dst, tmp, backdrop);
+		        break;
+		    default:		/* Should not happen */
+		        break;
+	        }
 	    }
 	    break;
 	default: