Partial fix for 687706 Display device 8-bit native incorrectly dithers grays

"Russell Lang" <[email protected]> Wed, 29 Sep 2004 13:39:35 +1000
Newsgroups gmane.comp.printing.ghostscript.patches
Message-ID <415ABB17.5795.396AF634@localhost>
Fix for 687706 Display device 8-bit native incorrectly dithers grays. 
This is a partial fix and also need's Dan Coby's fixes to the post 
DeviceN halftoning.  The DeviceRGBK color model is added.

DETAILS:
Prior to GS 8, the display device 8-bit native format would use 32 
gray levels to avoid halftoning, and a 4x4x4 color cube which did use 
halftoning.  The halftoning logic was changed with the introduction 
of DeviceN and in GS 8 both grays and colors were being halftoned.

The DeviceRGBK color model is added and used by the display device 
native 8-bit format.  The RGB color space to RGBK color model 
conversion converts pure RGB grays to the K (black/gray additive) 
component.  This allows the 32 gray levels to be recognised by the 
halftoning logic as being sufficient to avoid halftoning.


Russell Lang                   [email protected]
Ghostgum Software Pty Ltd      http://www.ghostgum.com.au/

_______________________________________________
gs-code-review mailing list
[email protected]
http://www.ghostscript.com/mailman/listinfo/gs-code-review
rgbk4.txt (application/octet-stream, 13.7 KB)
diff -u l:/cvs/gs/src/gdevdsp.c src/gdevdsp.c
--- l:/cvs/gs/src/gdevdsp.c	Sun Sep 26 21:59:35 2004
+++ src/gdevdsp.c	Wed Sep 29 03:06:49 2004
@@ -71,8 +71,8 @@
 
 private dev_proc_map_rgb_color(display_map_rgb_color_device4);
 private dev_proc_map_color_rgb(display_map_color_rgb_device4);
-private dev_proc_map_rgb_color(display_map_rgb_color_device8);
-private dev_proc_map_color_rgb(display_map_color_rgb_device8);
+private dev_proc_encode_color(display_encode_color_device8);
+private dev_proc_decode_color(display_decode_color_device8);
 private dev_proc_map_rgb_color(display_map_rgb_color_device16);
 private dev_proc_map_color_rgb(display_map_color_rgb_device16);
 private dev_proc_map_rgb_color(display_map_rgb_color_rgb);
@@ -377,26 +377,47 @@
 }
 
 /* DISPLAY_COLORS_NATIVE, 8bit/pixel */
-/* Map a r-g-b color to a color code */
+/* Map a r-g-b-k color to a color code */
 private gx_color_index
-display_map_rgb_color_device8(gx_device * dev, const gx_color_value cv[])
+display_encode_color_device8(gx_device * dev, const gx_color_value cv[])
 {
     /* palette of 96 colors */
     /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
     gx_color_value r = cv[0];
     gx_color_value g = cv[1];
     gx_color_value b = cv[2];
-    if ((r == g) && (g == b))
-	return ((r >> (gx_color_value_bits - 5)) + 0x40);
-    return ((r >> (gx_color_value_bits - 2)) << 4) +
-	((g >> (gx_color_value_bits - 2)) << 2) +
-	((b >> (gx_color_value_bits - 2)));
+    gx_color_value k = cv[3]; /* 0 = black */
+    if ((r == 0) && (g == 0) && (b == 0)) {
+	k = ((k >> (gx_color_value_bits - 6)) + 1) >> 1;
+	if (k > 0x1f)
+	    k = 0x1f;
+	return (k + 0x40);
+    }
+    if (k > 0) {
+	/* The RGB->RGBK color mapping shouldn't generate this. */
+	r = ((r+k) > gx_max_color_value) ? gx_max_color_value : 
+	    (gx_color_value)(r+k);
+	g = ((g+k) > gx_max_color_value) ? gx_max_color_value : 
+	    (gx_color_value)(g+k);
+	b = ((b+k) > gx_max_color_value) ? gx_max_color_value : 
+	    (gx_color_value)(b+k);
+    }
+    r = ((r >> (gx_color_value_bits - 3)) + 1) >> 1;
+    if (r > 0x3)
+	r = 0x3;
+    g = ((g >> (gx_color_value_bits - 3)) + 1) >> 1;
+    if (g > 0x3)
+	g = 0x3;
+    b = ((b >> (gx_color_value_bits - 3)) + 1) >> 1;
+    if (b > 0x3)
+	b = 0x3;
+    return (r << 4) + (g << 2) + b;
 }
 
-/* Map a color code to r-g-b. */
+/* Map a color code to r-g-b-k. */
 private int
-display_map_color_rgb_device8(gx_device * dev, gx_color_index color,
-		 gx_color_value prgb[3])
+display_decode_color_device8(gx_device * dev, gx_color_index color,
+		 gx_color_value prgb[4])
 {
     gx_color_value one;
     /* palette of 96 colors */
@@ -406,14 +427,15 @@
 	prgb[0] = (gx_color_value) (((color >> 4) & 3) * one);
 	prgb[1] = (gx_color_value) (((color >> 2) & 3) * one);
 	prgb[2] = (gx_color_value) (((color) & 3) * one);
+	prgb[3] = 0;
     }
     else if (color < 96) {
 	one = (gx_color_value) (gx_max_color_value / 31);
-	prgb[0] = prgb[1] = prgb[2] = 
-	    (gx_color_value) ((color & 0x1f) * one);
+	prgb[0] = prgb[1] = prgb[2] = 0;
+	prgb[3] = (gx_color_value) ((color & 0x1f) * one);
     }
     else {
-	prgb[0] = prgb[1] = prgb[2] = 0;
+	prgb[0] = prgb[1] = prgb[2] = prgb[3] = 0;
     }
     return 0;
 }
@@ -1331,12 +1353,21 @@
     return 0;
 }
 
+typedef enum DISPLAY_MODEL_e {
+    DISPLAY_MODEL_GRAY=0,
+    DISPLAY_MODEL_RGB=1,
+    DISPLAY_MODEL_RGBK=2,
+    DISPLAY_MODEL_CMYK=3,
+    DISPLAY_MODEL_SEP=4
+} DISPLAY_MODEL;
+
 /*
  * This is a utility routine to build the display device's color_info
  * structure (except for the anti alias info).
  */
 private void
-set_color_info(gx_device_color_info * pdci, int nc, int depth, int maxgray, int maxcolor)
+set_color_info(gx_device_color_info * pdci, DISPLAY_MODEL model, 
+    int nc, int depth, int maxgray, int maxcolor)
 {
     pdci->num_components = pdci->max_components = nc;
     pdci->depth = depth;
@@ -1346,23 +1377,29 @@
     pdci->dither_grays = maxgray + 1;
     pdci->dither_colors = maxcolor + 1;
     pdci->separable_and_linear = GX_CINFO_UNKNOWN_SEP_LIN;
-    switch (nc) {
-	case 1:
+    switch (model) {
+	case DISPLAY_MODEL_GRAY:
 	    pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
 	    pdci->cm_name = "DeviceGray";
 	    pdci->gray_index = 0;
 	    break;
-	case 3:
+	case DISPLAY_MODEL_RGB:
 	    pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
 	    pdci->cm_name = "DeviceRGB";
 	    pdci->gray_index = GX_CINFO_COMP_NO_INDEX;
 	    break;
-	case 4:
+	case DISPLAY_MODEL_RGBK:
+	    pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
+	    pdci->cm_name = "DeviceRGBK";
+	    pdci->gray_index = 3;
+	    break;
+	case DISPLAY_MODEL_CMYK:
 	    pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
 	    pdci->cm_name = "DeviceCMYK";
 	    pdci->gray_index = 3;
 	    break;
 	default:
+	case DISPLAY_MODEL_SEP:
 	    /* Anything else is separations */
 	    pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
 	    pdci->cm_name = "DeviceCMYK";
@@ -1422,6 +1459,20 @@
 
 /*
  * This is an utility routine to set up the color procs for the display
+ * device.  This routine is used when the display device is RGBK.
+ */
+private void
+set_rgbk_color_procs(gx_device * pdev, 
+	dev_t_proc_encode_color((*encode_color), gx_device),
+	dev_t_proc_decode_color((*decode_color), gx_device))
+{
+    set_color_procs(pdev, encode_color, decode_color,
+	gx_default_DevRGBK_get_color_mapping_procs,
+	gx_default_DevRGBK_get_color_comp_index);
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
  * device.  This routine is used when the display device is CMYK.
  */
 private void
@@ -1483,33 +1534,33 @@
 	    switch (nFormat & DISPLAY_DEPTH_MASK) {
 		case DISPLAY_DEPTH_1: 
 		    /* 1bit/pixel, black is 1, white is 0 */
-	    	    set_color_info(&dci, 1, 1, 1, 0);
+ 	    	    set_color_info(&dci, DISPLAY_MODEL_GRAY, 1, 1, 1, 0);
                     dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
 		    set_gray_color_procs(pdev, gx_b_w_gray_encode,
 		    				gx_default_b_w_map_color_rgb);
 		    break;
 		case DISPLAY_DEPTH_4:
 		    /* 4bit/pixel VGA color */
-	    	    set_color_info(&dci, 3, 4, 1, 1);
+ 	    	    set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 4, 3, 2);
                     dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
 		    set_rgb_color_procs(pdev, display_map_rgb_color_device4,
 		    				display_map_color_rgb_device4);
 		    break;
 		case DISPLAY_DEPTH_8:
 		    /* 8bit/pixel 96 color palette */
-	    	    set_color_info(&dci, 3, 8, 31, 3);
+ 	    	    set_color_info(&dci, DISPLAY_MODEL_RGBK, 4, 8, 31, 3);
                     dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
-		    set_rgb_color_procs(pdev, display_map_rgb_color_device8,
-		    				display_map_color_rgb_device8);
+		    set_rgbk_color_procs(pdev, display_encode_color_device8,
+		    				display_decode_color_device8);
 		    break;
 		case DISPLAY_DEPTH_16:
 		    /* Windows 16-bit display */
 		    /* Is maxgray = maxcolor = 63 correct? */
 	            if ((ddev->nFormat & DISPLAY_555_MASK) 
 			== DISPLAY_NATIVE_555)
-	    	        set_color_info(&dci, 3, 16, 31, 31);
+ 	    	        set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 16, 31, 31);
 		    else
-	    	        set_color_info(&dci, 3, 16, 63, 63);
+ 	    	        set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 16, 63, 63);
 		    set_rgb_color_procs(pdev, display_map_rgb_color_device16,
 		    				display_map_color_rgb_device16);
 		    break;
@@ -1519,7 +1570,7 @@
 	    dci.gray_index = GX_CINFO_COMP_NO_INDEX;
 	    break;
 	case DISPLAY_COLORS_GRAY:
-	    set_color_info(&dci, 1, bpc, maxvalue, 0);
+	    set_color_info(&dci, DISPLAY_MODEL_GRAY, 1, bpc, maxvalue, 0);
 	    if (bpc == 1)
 	    	set_gray_color_procs(pdev, gx_default_gray_encode,
 						gx_default_w_b_map_color_rgb);
@@ -1532,7 +1583,7 @@
 		bpp = bpc * 3;
 	    else
 		bpp = bpc * 4; 
-	    set_color_info(&dci, 3, bpp, maxvalue, maxvalue);
+	    set_color_info(&dci, DISPLAY_MODEL_RGB, 3, bpp, maxvalue, maxvalue);
 	    if (((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
 	 	((nFormat & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)) {
 		if ((nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN)
@@ -1550,7 +1601,7 @@
 	    break;
 	case DISPLAY_COLORS_CMYK:
 	    bpp = bpc * 4;
-	    set_color_info(&dci, 4, bpp, maxvalue, maxvalue);
+	    set_color_info(&dci, DISPLAY_MODEL_CMYK, 4, bpp, maxvalue, maxvalue);
 	    if ((nFormat & DISPLAY_ALPHA_MASK) != DISPLAY_ALPHA_NONE)
 		return_error(gs_error_rangecheck);
 	    if ((nFormat & DISPLAY_ENDIAN_MASK) != DISPLAY_BIGENDIAN)
@@ -1569,7 +1620,8 @@
 	    if ((nFormat & DISPLAY_ENDIAN_MASK) != DISPLAY_BIGENDIAN)
 		return_error(gs_error_rangecheck);
 	    bpp = sizeof(gx_color_index)*8;
-	    set_color_info(&dci, bpp/bpc, bpp, maxvalue, maxvalue);
+	    set_color_info(&dci, DISPLAY_MODEL_SEP, bpp/bpc, bpp, 
+		maxvalue, maxvalue);
 	    if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) {
 		ddev->devn_params.bitspercomponent = bpc;
 		set_color_procs(pdev, 
@@ -1594,6 +1646,8 @@
 	    ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
 	    if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_1)
 	        ddev->color_info.gray_index = 0;
+	    else if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8)
+	        ddev->color_info.gray_index = 3;
 	    break;
 	case DISPLAY_COLORS_RGB:
 	    ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
diff -u l:/cvs/gs/src/gxcmap.c src/gxcmap.c
--- l:/cvs/gs/src/gxcmap.c	Fri Jul 02 09:38:28 2004
+++ src/gxcmap.c	Tue Sep 28 11:15:39 2004
@@ -232,6 +232,43 @@
 }
 
 static void
+gray_cs_to_rgbk_cm(gx_device * dev, frac gray, frac out[])
+{
+    out[0] = out[1] = out[2] = frac_0;
+    out[3] = gray;
+}
+
+static void
+rgb_cs_to_rgbk_cm(gx_device * dev, const gs_imager_state *pis,
+				  frac r, frac g, frac b, frac out[])
+{
+    if ((r == g) && (g == b)) {
+	out[0] = out[1] = out[2] = frac_0;
+	out[3] = r;
+    }
+    else {
+	out[0] = r;
+	out[1] = g;
+	out[2] = b;
+	out[3] = frac_0;
+    }
+}
+
+static void
+cmyk_cs_to_rgbk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+    frac rgb[3];
+    if ((c == frac_0) && (m == frac_0) && (y == frac_0)) {
+	out[0] = out[1] = out[2] = frac_0;
+	out[3] = frac_1 - k;
+    }
+    else {
+	color_cmyk_to_rgb(c, m, y, k, NULL, rgb);
+	rgb_cs_to_rgbk_cm(dev, NULL, rgb[0], rgb[1], rgb[2], out);
+    }
+}
+
+static void
 gray_cs_to_cmyk_cm(gx_device * dev, frac gray, frac out[])
 {
     out[0] = out[1] = out[2] = frac_0;
@@ -294,6 +331,10 @@
     gray_cs_to_cmyk_cm, rgb_cs_to_cmyk_cm, cmyk_cs_to_cmyk_cm
 };
 
+static const gx_cm_color_map_procs DeviceRGBK_procs = {
+    gray_cs_to_rgbk_cm, rgb_cs_to_rgbk_cm, cmyk_cs_to_rgbk_cm
+};
+
 /*
  * These are the default handlers for returning the list of color space
  * to color model conversion routines.
@@ -317,6 +358,12 @@
 }
 
 const gx_cm_color_map_procs *
+gx_default_DevRGBK_get_color_mapping_procs(const gx_device * dev)
+{
+    return &DeviceRGBK_procs;
+}
+
+const gx_cm_color_map_procs *
 gx_error_get_color_mapping_procs(const gx_device * dev)
 {
     /*
@@ -372,6 +419,23 @@
         return 1;
     if (compare_color_names(pname, name_size, "Yellow"))
         return 2;
+    if (compare_color_names(pname, name_size, "Black"))
+        return 3;
+    else
+        return -1;		    /* Indicate that the component name is "unknown" */
+}
+
+/* Default color component to index for a DeviceRGBK color model */
+int
+gx_default_DevRGBK_get_color_comp_index(gx_device * dev, const char * pname,
+					    int name_size, int component_type)
+{
+    if (compare_color_names(pname, name_size, "Red"))
+        return 0;
+    if (compare_color_names(pname, name_size, "Green"))
+        return 1;
+    if (compare_color_names(pname, name_size, "Blue"))
+        return 2;
     if (compare_color_names(pname, name_size, "Black"))
         return 3;
     else
diff -u l:/cvs/gs/src/gxcmap.h src/gxcmap.h
--- l:/cvs/gs/src/gxcmap.h	Fri Jul 02 09:38:28 2004
+++ src/gxcmap.h	Tue Sep 28 11:15:39 2004
@@ -242,6 +242,7 @@
 dev_proc_get_color_comp_index(gx_default_DevGray_get_color_comp_index);
 dev_proc_get_color_comp_index(gx_default_DevRGB_get_color_comp_index);
 dev_proc_get_color_comp_index(gx_default_DevCMYK_get_color_comp_index);
+dev_proc_get_color_comp_index(gx_default_DevRGBK_get_color_comp_index);
 
 /*
  * These are the default routines for getting the color space conversion
@@ -251,6 +252,7 @@
 dev_proc_get_color_mapping_procs(gx_default_DevGray_get_color_mapping_procs);
 dev_proc_get_color_mapping_procs(gx_default_DevRGB_get_color_mapping_procs);
 dev_proc_get_color_mapping_procs(gx_default_DevCMYK_get_color_mapping_procs);
+dev_proc_get_color_mapping_procs(gx_default_DevRGBK_get_color_mapping_procs);
 
 /*
  * These are the default routines for converting a colorant value list
diff -u l:/cvs/gs/src/zcolor.c src/zcolor.c
--- l:/cvs/gs/src/zcolor.c	Sun Sep 26 21:59:37 2004
+++ src/zcolor.c	Tue Sep 28 23:24:48 2004
@@ -487,23 +487,24 @@
     gx_device *dev = gs_currentdevice(igs);
     int ncomp = dev->color_info.num_components;
     gx_color_index color;
+    os_ptr op = osp - (ncomp-1);
     int i;
     if (ref_stack_count(&o_stack) < ncomp)
 	return_error(e_stackunderflow);
     for (i=0; i<ncomp; i++) {
-	if (r_has_type(&osp[-i], t_real))
+	if (r_has_type(op+i, t_real))
 	    cv[i] = (gx_color_value)
-		(osp[-i].value.realval * gx_max_color_value);
-	else if (r_has_type(&osp[-i], t_integer))
+		(op[i].value.realval * gx_max_color_value);
+	else if (r_has_type(op+i, t_integer))
 	    cv[i] = (gx_color_value)
-		(osp[-i].value.intval * gx_max_color_value);
+		(op[i].value.intval * gx_max_color_value);
 	else
 	    return_error(e_typecheck);
     }
     color = (*dev_proc(dev, encode_color)) (dev, cv);
     (*dev_proc(dev, decode_color)) (dev, color, cv);
     for (i=0; i<ncomp; i++)
-        make_real(&osp[-i], (float)cv[i] / (float)gx_max_color_value);
+        make_real(op+i, (float)cv[i] / (float)gx_max_color_value);
     return 0;
 }