Bug 686956 Display device does not support separations

"Russell Lang" <[email protected]> Fri, 02 Jul 2004 20:39:09 +1000
Newsgroups gmane.comp.printing.ghostscript.patches
Message-ID <40E5C7ED.1890.90D4D35B@localhost>
Ray, Dan,

This patch is the code that I have previously sent to Dan.

Log Message:
Add support for separations to display device.
This requires a change to the display callback structure.
Support is maintained for clients using version 1 of the structure.
Fixes bug 686956 Display device does not support separations.

DETAILS:
Add support for a new color format, DISPLAY_COLORS_SEPARATION,
which supports CMYK and spot colors.  A new callback
display_separation() is used to tell the client about
the names and CMYK equivalents of each separation.
The callback structure version number is incremented to 2
to show that has changed.  The display devices checks
whether it is passed the older v1 structure or the newer
v2 structure, and only calls the new separation callback
if it was given a v2 structure.
Only 8-bit/pixel, up to 8 components and 64-bit depth are supported.
In the Windows and gtk+ clients, a subset of these 8 separations
can be selected for display.
The new code is based on the tiffsep device.


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

Common subdirectories: l:/cvs/gs/src/CVS and src/CVS
diff -u l:/cvs/gs/src/dwimg.c src/dwimg.c
--- l:/cvs/gs/src/dwimg.c	Sat May 22 02:20:56 2004
+++ src/dwimg.c	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996-2001 Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 1996-2004 Ghostgum Software Pty Ltd.  All rights reserved.
   
   This software is provided AS-IS with no warranty, either express or
   implied.
@@ -60,14 +60,8 @@
 static void create_window(IMAGE *img);
 
 #define M_COPY_CLIP 1
-#define M_SEP_CYAN 2
-#define M_SEP_MAGENTA 3
-#define M_SEP_YELLOW 4
-#define M_SEP_BLACK 5
-#define SEP_CYAN 8
-#define SEP_MAGENTA 4
-#define SEP_YELLOW 2
-#define SEP_BLACK 1
+#define M_DEVICEN_GRAY 2	/* show single separation as gray */
+#define M_SEPARATION 3 		/* 3 to 3+IMG_DEVICEN_MAX-1 */
 
 #define DISPLAY_ERROR (-1)	/* return this to Ghostscript on error */
 
@@ -94,7 +88,9 @@
 void image_16RGB565_to_24BGR(int width, unsigned char *dest, 
     unsigned char *source);
 void image_32CMYK_to_24BGR(int width, unsigned char *dest, 
-    unsigned char *source, int sep);
+    unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
+void image_devicen_to_24BGR(int width, unsigned char *dest, 
+    unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
 
 
 /****************************************************************/
@@ -122,6 +118,7 @@
 {
     IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
     if (img) {
+        memset(img, 0, sizeof(IMAGE));
 	/* remember device and handle */
 	img->handle = handle;
 	img->device = device;
@@ -164,6 +161,7 @@
 image_size(IMAGE *img, int new_width, int new_height, int new_raster, 
     unsigned int new_format, void *pimage)
 {
+    int i;
     img->raster = new_raster;
     img->format = new_format;
     img->image = (unsigned char *)pimage;
@@ -173,6 +171,18 @@
     img->bmih.biWidth = new_width;
     img->bmih.biHeight = new_height;
     img->bmih.biPlanes = 1;
+
+    /* Reset separations */
+    for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	img->devicen[i].used = 0;
+	img->devicen[i].visible = 1;
+	memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
+	img->devicen[i].cyan = 0;
+	img->devicen[i].magenta = 0;
+	img->devicen[i].yellow = 0;
+	img->devicen[i].black = 0;
+    }
+
     switch (img->format & DISPLAY_COLORS_MASK) {
 	case DISPLAY_COLORS_NATIVE:
 	    switch (img->format & DISPLAY_DEPTH_MASK) {
@@ -259,6 +269,30 @@
 	    img->bmih.biBitCount = 24;
 	    img->bmih.biClrUsed = 0;
 	    img->bmih.biClrImportant = 0;
+	    img->devicen[0].used = 1;
+	    img->devicen[0].cyan = 65535;
+	    /* We already know about the CMYK components */
+	    strncpy(img->devicen[0].name, "Cyan", 
+		sizeof(img->devicen[0].name));
+	    img->devicen[1].used = 1;
+	    img->devicen[1].magenta = 65535;
+	    strncpy(img->devicen[1].name, "Magenta", 
+		sizeof(img->devicen[1].name));
+	    img->devicen[2].used = 1;
+	    img->devicen[2].yellow = 65535;
+	    strncpy(img->devicen[2].name, "Yellow", 
+		sizeof(img->devicen[2].name));
+	    img->devicen[3].used = 1;
+	    img->devicen[3].black = 65535;
+	    strncpy(img->devicen[3].name, "Black", 
+		sizeof(img->devicen[3].name));
+	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    /* we can't display this natively */
+	    /* we will convert it just before displaying */
+	    img->bmih.biBitCount = 24;
+	    img->bmih.biClrUsed = 0;
+	    img->bmih.biClrImportant = 0;
 	    break;
     }
 
@@ -275,6 +309,24 @@
     return 0;
 }
 
+int 
+image_separation(IMAGE *img,
+    int comp_num, const char *name,
+    unsigned short c, unsigned short m,
+    unsigned short y, unsigned short k)
+{
+    if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
+	return DISPLAY_ERROR;
+    img->devicen[comp_num].used = 1;
+    strncpy(img->devicen[comp_num].name, name,
+	sizeof(img->devicen[comp_num].name)-1);
+    img->devicen[comp_num].cyan    = c;
+    img->devicen[comp_num].magenta = m;
+    img->devicen[comp_num].yellow  = y;
+    img->devicen[comp_num].black   = k;
+    return 0;
+}
+
 
 /****************************************************************/
 /* These functions are only accessed by the GUI thread */
@@ -332,41 +384,58 @@
 void image_separations(IMAGE *img)
 {
     char buf[64];
+    int i;
+    int exist;
+    int num_visible = 0;
     HMENU sysmenu = GetSystemMenu(img->hwnd, FALSE);
-    int exist = GetMenuString(sysmenu, M_SEP_CYAN, buf, sizeof(buf)-1, 
-		MF_BYCOMMAND) != 0;
-    if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_CMYK) {
-        if (!exist) {
-	    /* menus don't exist - add them */
-            img->sep = 0xf;
-	    AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
-	    AppendMenu(sysmenu, MF_STRING | MF_CHECKED, 
-		M_SEP_CYAN, "Cyan");
-	    AppendMenu(sysmenu, MF_STRING | MF_CHECKED, 
-		M_SEP_MAGENTA, "Magenta");
-	    AppendMenu(sysmenu, MF_STRING | MF_CHECKED, 
-		M_SEP_YELLOW, "Yellow");
-	    AppendMenu(sysmenu, MF_STRING | MF_CHECKED, 
-		M_SEP_BLACK, "Black");
+    if (((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_CMYK) ||
+        ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)) {
+	/* Add menus if needed */
+	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	    exist = 0;
+	    if (img->devicen[i].menu)
+		exist = GetMenuString(sysmenu, M_SEPARATION+i, 
+			buf, sizeof(buf)-1, MF_BYCOMMAND) != 0;
+	    if (exist && (strcmp(img->devicen[i].name, buf) != 0)) {
+		/* remove it because name changed */
+	       RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
+	       img->devicen[i].menu = 0;
+	    }
+	    if (img->devicen[i].name[0] && !img->devicen[i].menu) {
+		AppendMenu(sysmenu, MF_STRING | MF_CHECKED, 
+		    M_SEPARATION+i, img->devicen[i].name);
+		img->devicen[i].menu = 1;
+	    }
+	    if (img->devicen[i].used && img->devicen[i].visible)
+		num_visible++;
 	}
+	EnableMenuItem(sysmenu, M_DEVICEN_GRAY, 
+	    MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
     }
     else {
-        if (exist)  {
-	    RemoveMenu(sysmenu, M_SEP_CYAN, MF_BYCOMMAND);
-	    RemoveMenu(sysmenu, M_SEP_MAGENTA, MF_BYCOMMAND);
-	    RemoveMenu(sysmenu, M_SEP_YELLOW, MF_BYCOMMAND);
-	    RemoveMenu(sysmenu, M_SEP_BLACK, MF_BYCOMMAND);
-	    /* remove separator */
-	    RemoveMenu(sysmenu, GetMenuItemCount(sysmenu)-1, MF_BYPOSITION);
+	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	   if (img->devicen[i].menu) {
+	       RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
+	       img->devicen[i].menu = 0;
+	   }
 	}
+	EnableMenuItem(sysmenu, M_DEVICEN_GRAY, MF_BYCOMMAND | MF_GRAYED);
     }
 }
 
-void sep_menu(IMAGE *img, int menu, int mask)
+void sep_menu(IMAGE *img, int component)
 {
-    img->sep ^= mask ;
-    CheckMenuItem(GetSystemMenu(img->hwnd, FALSE), menu, MF_BYCOMMAND | 
-	((img->sep & mask) ? MF_CHECKED : MF_UNCHECKED) );
+    int i;
+    int num_visible = 0;
+    img->devicen[component].visible = !img->devicen[component].visible;
+    CheckMenuItem(GetSystemMenu(img->hwnd, FALSE), 
+	M_SEPARATION+component, 
+	(img->devicen[component].visible ? MF_CHECKED : MF_UNCHECKED));
+    for (i=0; i<IMAGE_DEVICEN_MAX; i++)
+        if (img->devicen[i].used && img->devicen[i].visible)
+	    num_visible++;
+    EnableMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY, 
+	MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
     InvalidateRect(img->hwnd, NULL, 0);
     UpdateWindow(img->hwnd);
 }
@@ -449,6 +518,8 @@
     sysmenu = GetSystemMenu(img->hwnd, 0);	/* get the sysmenu */
     AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
     AppendMenu(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
+    AppendMenu(sysmenu, MF_STRING, M_DEVICEN_GRAY, "Show as Gray");
+    AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
 
     image_separations(img);
 }
@@ -500,6 +571,7 @@
 	InvalidateRect(img->hwnd, NULL, 1);
 	UpdateWindow(img->hwnd);
     }
+    image_separations(img);
 }
 
 
@@ -672,23 +744,35 @@
 /* convert one line of 32CMYK to 24BGR */
 void
 image_32CMYK_to_24BGR(int width, unsigned char *dest, unsigned char *source,
-    int sep)
+    IMAGE_DEVICEN *devicen, int devicen_gray)
 {
     int i;
     int cyan, magenta, yellow, black;
+    int vc = devicen[0].visible;
+    int vm = devicen[1].visible;
+    int vy = devicen[2].visible;
+    int vk = devicen[3].visible;
+    int vall = vc && vm && vy && vk;
+    int show_gray = (vc + vm + vy + vk == 1) && devicen_gray;
     for (i=0; i<width; i++) {
 	cyan = source[0];
 	magenta = source[1];
 	yellow = source[2];
 	black = source[3];
-	if (!(sep & SEP_CYAN))
-	    cyan = 0;
-	if (!(sep & SEP_MAGENTA))
-	    magenta = 0;
-	if (!(sep & SEP_YELLOW))
-	    yellow = 0;
-	if (!(sep & SEP_BLACK))
-	    black = 0;
+	if (!vall) {
+	    if (!vc)
+		cyan = 0;
+	    if (!vm)
+		magenta = 0;
+	    if (!vy)
+		yellow = 0;
+	    if (!vk)
+		black = 0;
+	    if (show_gray) {
+		black += cyan + magenta + yellow;
+		cyan = magenta = yellow = 0;
+	    }
+	}
 	*dest++ = (255 - yellow)  * (255 - black)/255; /* blue */
 	*dest++ = (255 - magenta) * (255 - black)/255; /* green */
 	*dest++ = (255 - cyan)    * (255 - black)/255; /* red */
@@ -697,6 +781,56 @@
 }
 
 void
+image_devicen_to_24BGR(int width, unsigned char *dest, unsigned char *source, 
+    IMAGE_DEVICEN *devicen, int devicen_gray)
+{
+    int i, j;
+    int cyan, magenta, yellow, black;
+    int num_comp = 0;
+    int value;
+    int num_visible = 0;
+    int show_gray = 0;
+    for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
+	if (devicen[j].used) {
+	   num_comp = j+1;
+	   if (devicen[j].visible)
+		num_visible++;
+	}
+    }
+    if ((num_visible == 1) && devicen_gray)
+	show_gray = 1;
+
+    for (i=0; i<width; i++) {
+	cyan = magenta = yellow = black = 0;
+	for (j=0; j<num_comp; j++) {
+	    if (devicen[j].visible && devicen[j].used) {
+		value = source[j];
+		if (show_gray)
+		    black += value;
+		else {
+		    cyan    += value * devicen[j].cyan    / 65535;
+		    magenta += value * devicen[j].magenta / 65535;
+		    yellow  += value * devicen[j].yellow  / 65535;
+		    black   += value * devicen[j].black / 65535;
+		}
+	    }
+	}
+	if (cyan > 255)
+	   cyan = 255;
+	if (magenta > 255)
+	   magenta = 255;
+	if (yellow > 255)
+	   yellow = 255;
+	if (black > 255)
+	   black = 255;
+	*dest++ = (255 - yellow)  * (255 - black)/255; /* blue */
+	*dest++ = (255 - magenta) * (255 - black)/255; /* green */
+	*dest++ = (255 - cyan)    * (255 - black)/255; /* red */
+	source += 8;
+    }
+}
+
+void
 image_convert_line(IMAGE *img, unsigned char *dest, unsigned char *source)
 {
     unsigned char *d = dest;
@@ -760,8 +894,15 @@
 	case DISPLAY_COLORS_CMYK:
 	    if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
 		return;
-	    image_32CMYK_to_24BGR(width, dest, source, img->sep);
+	    image_32CMYK_to_24BGR(width, dest, source, 
+		img->devicen, img->devicen_gray);
 	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
+		return;
+	    image_devicen_to_24BGR(width, dest, source, 
+		img->devicen, img->devicen_gray);
+	    break;
     }
 }
 
@@ -954,17 +1095,16 @@
 		    ReleaseMutex(img->hmutex);
 		return 0;
 	    }
-	    else if (LOWORD(wParam) == M_SEP_CYAN) {
-		sep_menu(img, M_SEP_CYAN, SEP_CYAN);
-	    }
-	    else if (LOWORD(wParam) == M_SEP_MAGENTA) {
-		sep_menu(img, M_SEP_MAGENTA, SEP_MAGENTA);
-	    }
-	    else if (LOWORD(wParam) == M_SEP_YELLOW) {
-		sep_menu(img, M_SEP_YELLOW, SEP_YELLOW);
+	    else if ((LOWORD(wParam) >= M_SEPARATION) &&
+	             (LOWORD(wParam) < M_SEPARATION+IMAGE_DEVICEN_MAX)) {
+		sep_menu(img, LOWORD(wParam) - M_SEPARATION);
 	    }
-	    else if (LOWORD(wParam) == M_SEP_BLACK) {
-		sep_menu(img, M_SEP_BLACK, SEP_BLACK);
+	    else if (LOWORD(wParam) == M_DEVICEN_GRAY) {
+		img->devicen_gray = !img->devicen_gray;
+		CheckMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY, 
+		    (img->devicen_gray ? MF_CHECKED : MF_UNCHECKED));
+		InvalidateRect(img->hwnd, NULL, 0);
+		UpdateWindow(img->hwnd);
 	    }
 	    break;
 	case WM_CREATE:
diff -u l:/cvs/gs/src/dwimg.h src/dwimg.h
--- l:/cvs/gs/src/dwimg.h	Sat May 22 02:20:56 2004
+++ src/dwimg.h	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 2001, Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 1996-2004, Ghostgum Software Pty Ltd.  All rights reserved.
 
   This software is provided AS-IS with no warranty, either express or
   implied.
@@ -22,6 +22,19 @@
 
 /* Windows Image Window structure */
 
+typedef struct IMAGE_DEVICEN_S IMAGE_DEVICEN;
+struct IMAGE_DEVICEN_S {
+    int used;		/* non-zero if in use */
+    int visible;	/* show on window */
+    char name[64];
+    int cyan;
+    int magenta;
+    int yellow;
+    int black;
+    int menu;		/* non-zero if menu item added to system menu */
+};
+#define IMAGE_DEVICEN_MAX 8
+
 typedef struct IMAGE_S IMAGE;
 struct IMAGE_S {
     void *handle;
@@ -34,7 +47,8 @@
     BITMAPINFOHEADER bmih;
     HPALETTE palette;
     int bytewidth;
-    int sep;		/* CMYK separations to display */
+    int devicen_gray;	/* true if a single separation should be shown gray */
+    IMAGE_DEVICEN devicen[IMAGE_DEVICEN_MAX];
 
     /* periodic redrawing */
     SYSTEMTIME update_time;
diff -u l:/cvs/gs/src/dwmain.c src/dwmain.c
--- l:/cvs/gs/src/dwmain.c	Sat May 22 02:20:56 2004
+++ src/dwmain.c	Fri Jul 02 09:45:48 2004
@@ -226,6 +226,22 @@
     return poll();
 }
 
+int display_separation(void *handle, void *device, 
+   int comp_num, const char *name,
+   unsigned short c, unsigned short m,
+   unsigned short y, unsigned short k)
+{
+    IMAGE *img;
+#ifdef DISPLAY_DEBUG
+    fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n", 
+	handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
+#endif
+    img = image_find(handle, device);
+    if (img)
+        image_separation(img, comp_num, name, c, m, y, k);
+    return 0;
+}
+
 display_callback display = { 
     sizeof(display_callback),
     DISPLAY_VERSION_MAJOR,
@@ -239,7 +255,8 @@
     display_page,
     display_update,
     NULL,	/* memalloc */
-    NULL	/* memfree */
+    NULL,	/* memfree */
+    display_separation
 };
 
 
diff -u l:/cvs/gs/src/dwmainc.c src/dwmainc.c
--- l:/cvs/gs/src/dwmainc.c	Sat May 22 02:20:56 2004
+++ src/dwmainc.c	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996-2001 Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 1996-2004 Ghostgum Software Pty Ltd.  All rights reserved.
   
   This software is provided AS-IS with no warranty, either express or
   implied.
@@ -278,6 +278,21 @@
 }
 #endif
 
+int display_separation(void *handle, void *device, 
+   int comp_num, const char *name,
+   unsigned short c, unsigned short m,
+   unsigned short y, unsigned short k)
+{
+    IMAGE *img;
+#ifdef DISPLAY_DEBUG
+    fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n", 
+	handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
+#endif
+    img = image_find(handle, device);
+    if (img)
+        image_separation(img, comp_num, name, c, m, y, k);
+    return 0;
+}
 
 
 display_callback display = { 
@@ -294,11 +309,12 @@
     display_update,
 #ifdef DISPLAY_DEBUG_USE_ALLOC
     display_memalloc,	/* memalloc */
-    display_memfree	/* memfree */
+    display_memfree,	/* memfree */
 #else
     NULL,	/* memalloc */
-    NULL	/* memfree */
+    NULL,	/* memfree */
 #endif
+    display_separation
 };
 
 
diff -u l:/cvs/gs/src/dxmain.c src/dxmain.c
--- l:/cvs/gs/src/dxmain.c	Tue Nov 11 09:15:00 2003
+++ src/dxmain.c	Fri Jul 02 09:45:48 2004
@@ -58,13 +58,6 @@
 static int display_update(void *handle, void *device, int x, int y, 
 	int w, int h);
 
-enum SEPARATIONS {
-    SEP_CYAN = 8,
-    SEP_MAGENTA = 4,
-    SEP_YELLOW = 2,
-    SEP_BLACK = 1
-};
-
 #ifndef min
 #define min(a,b) ((a) < (b) ? (a) : (b))
 #endif
@@ -142,6 +135,19 @@
 /*********************************************************************/
 /* dll display device */
 
+typedef struct IMAGE_DEVICEN_S IMAGE_DEVICEN;
+struct IMAGE_DEVICEN_S {
+    int used;		/* non-zero if in use */
+    int visible;	/* show on window */
+    char name[64];
+    int cyan;
+    int magenta;
+    int yellow;
+    int black;
+    int menu;		/* non-zero if menu item added to system menu */
+};
+#define IMAGE_DEVICEN_MAX 8
+
 typedef struct IMAGE_S IMAGE;
 struct IMAGE_S {
     void *handle;
@@ -149,6 +155,8 @@
     GtkWidget *window;
     GtkWidget *vbox;
     GtkWidget *cmyk_bar;
+    GtkWidget *separation[IMAGE_DEVICEN_MAX];
+    GtkWidget *show_as_gray;
     GtkWidget *scroll;
     GtkWidget *darea;
     guchar *buf;
@@ -157,7 +165,8 @@
     gint rowstride;
     unsigned int format;
     GdkRgbCmap *cmap;
-    int separation;	/* for displaying C or M or Y or K */
+    int devicen_gray;	/* true if a single separation should be shown gray */
+    IMAGE_DEVICEN devicen[IMAGE_DEVICEN_MAX];
     guchar *rgbbuf;	/* used when we need to convert raster format */
     IMAGE *next;
 };
@@ -259,6 +268,7 @@
 		    }
 		    break;
 		case DISPLAY_COLORS_CMYK:
+		case DISPLAY_COLORS_SEPARATION:
 		    if ((depth == DISPLAY_DEPTH_8) && img->rgbbuf)
 			gdk_draw_rgb_image(widget->window, 
 			    widget->style->fg_gc[GTK_STATE_NORMAL],
@@ -322,43 +332,83 @@
     }
 }
 
-static void window_separation(IMAGE *img, int layer)
+static void window_separation(IMAGE *img, int sep)
 {
-    img->separation ^= layer;
+    img->devicen[sep].visible = !img->devicen[sep].visible;
     display_sync(img->handle, img->device);
 }
 
-static void cmyk_cyan(GtkWidget *w, gpointer data)
+static void signal_sep0(GtkWidget *w, gpointer data)
+{
+    window_separation((IMAGE *)data, 0);
+}
+
+static void signal_sep1(GtkWidget *w, gpointer data)
+{
+    window_separation((IMAGE *)data, 1);
+}
+
+static void signal_sep2(GtkWidget *w, gpointer data)
+{
+    window_separation((IMAGE *)data, 2);
+}
+
+static void signal_sep3(GtkWidget *w, gpointer data)
+{
+    window_separation((IMAGE *)data, 3);
+}
+
+static void signal_sep4(GtkWidget *w, gpointer data)
 {
-    window_separation((IMAGE *)data, SEP_CYAN);
+    window_separation((IMAGE *)data, 4);
 }
 
-static void cmyk_magenta(GtkWidget *w, gpointer data)
+static void signal_sep5(GtkWidget *w, gpointer data)
 {
-    window_separation((IMAGE *)data, SEP_MAGENTA);
+    window_separation((IMAGE *)data, 5);
 }
 
-static void cmyk_yellow(GtkWidget *w, gpointer data)
+static void signal_sep6(GtkWidget *w, gpointer data)
 {
-    window_separation((IMAGE *)data, SEP_YELLOW);
+    window_separation((IMAGE *)data, 6);
 }
 
-static void cmyk_black(GtkWidget *w, gpointer data)
+static void signal_sep7(GtkWidget *w, gpointer data)
 {
-    window_separation((IMAGE *)data, SEP_BLACK);
+    window_separation((IMAGE *)data, 7);
 }
 
-static void
+GtkSignalFunc signal_separation[IMAGE_DEVICEN_MAX] = {
+    signal_sep0, 
+    signal_sep1, 
+    signal_sep2, 
+    signal_sep3, 
+    signal_sep4, 
+    signal_sep5, 
+    signal_sep6, 
+    signal_sep7
+};
+
+static GtkWidget *
 window_add_button(IMAGE *img, const char *label, GtkSignalFunc fn)
 {
     GtkWidget *w;
     w = gtk_check_button_new_with_label(label);
-    gtk_box_pack_start(GTK_BOX(img->cmyk_bar), w, TRUE, FALSE, 5);
+    gtk_box_pack_start(GTK_BOX(img->cmyk_bar), w, FALSE, FALSE, 5);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
     gtk_signal_connect(GTK_OBJECT(w), "clicked", fn, img);
     gtk_widget_show(w);
+    return w;
+}
+
+static void signal_show_as_gray(GtkWidget *w, gpointer data)
+{
+    IMAGE *img= (IMAGE *)data;
+    img->devicen_gray= !img->devicen_gray;
+    display_sync(img->handle, img->device);
 }
 
+
 /* New device has been opened */
 static int display_open(void *handle, void *device)
 {
@@ -457,6 +507,7 @@
     IMAGE *img = image_find(handle, device);
     int color;
     int depth;
+    int i;
     if (img == NULL)
 	return -1;
 
@@ -473,6 +524,17 @@
     img->buf = pimage;
     img->format = format;
 
+    /* Reset separations */
+    for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	img->devicen[i].used = 0;
+	img->devicen[i].visible = 1;
+	memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
+	img->devicen[i].cyan = 0;
+	img->devicen[i].magenta = 0;
+	img->devicen[i].yellow = 0;
+	img->devicen[i].black = 0;
+    }
+
     color = img->format & DISPLAY_COLORS_MASK;
     depth = img->format & DISPLAY_DEPTH_MASK;
     switch (color) {
@@ -534,24 +596,59 @@
 		img->rgbbuf = (guchar *)malloc(width * height * 3);
 		if (img->rgbbuf == NULL)
 		    return -1;
+		/* We already know about the CMYK components */
+		img->devicen[0].used = 1;
+		img->devicen[0].cyan = 65535;
+		strncpy(img->devicen[0].name, "Cyan", 
+		    sizeof(img->devicen[0].name));
+		img->devicen[1].used = 1;
+		img->devicen[1].magenta = 65535;
+		strncpy(img->devicen[1].name, "Magenta", 
+		    sizeof(img->devicen[1].name));
+		img->devicen[2].used = 1;
+		img->devicen[2].yellow = 65535;
+		strncpy(img->devicen[2].name, "Yellow", 
+		    sizeof(img->devicen[2].name));
+		img->devicen[3].used = 1;
+		img->devicen[3].black = 65535;
+		strncpy(img->devicen[3].name, "Black", 
+		    sizeof(img->devicen[3].name));
 	    }
 	    else
 		return -1;	/* not supported */
 	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    /* we can't display this natively */
+	    /* we will convert it just before displaying */
+	    if (depth != DISPLAY_DEPTH_8)
+		return -1;	/* not supported */
+	    img->rgbbuf = (guchar *)malloc(width * height * 3);
+	    if (img->rgbbuf == NULL)
+		return -1;
+	    break;
     }
 
 
-    if (color == DISPLAY_COLORS_CMYK) {
+    if ((color == DISPLAY_COLORS_CMYK) || 
+	(color == DISPLAY_COLORS_SEPARATION)) {
 	if (!img->cmyk_bar) {
 	    /* add bar to select separation */
-	    img->cmyk_bar = gtk_hbox_new(TRUE, 0);
+	    img->cmyk_bar = gtk_hbox_new(FALSE, 0);
 	    gtk_box_pack_start(GTK_BOX(img->vbox), img->cmyk_bar, 
 		FALSE, FALSE, 0);
-	    img->separation = 0xf;	/* all layers */
-	    window_add_button(img, "Cyan", (GtkSignalFunc)cmyk_cyan);
-	    window_add_button(img, "Magenta", (GtkSignalFunc)cmyk_magenta);
-	    window_add_button(img, "Yellow", (GtkSignalFunc)cmyk_yellow);
-	    window_add_button(img, "Black", (GtkSignalFunc)cmyk_black);
+	    for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	       img->separation[i] = 
+		window_add_button(img, img->devicen[i].name,
+		   signal_separation[i]);
+	    }
+	    img->show_as_gray = gtk_check_button_new_with_label("Show as Gray");
+	    gtk_box_pack_end(GTK_BOX(img->cmyk_bar), img->show_as_gray, 
+		FALSE, FALSE, 5);
+	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(img->show_as_gray), 
+		FALSE);
+	    gtk_signal_connect(GTK_OBJECT(img->show_as_gray), "clicked", 
+		signal_show_as_gray, img);
+	    gtk_widget_show(img->show_as_gray);
 	}
 	gtk_widget_show(img->cmyk_bar);
     }
@@ -584,6 +681,33 @@
     endian = img->format & DISPLAY_ENDIAN_MASK;
     native555 = img->format & DISPLAY_555_MASK;
     alpha = img->format & DISPLAY_ALPHA_MASK;
+
+    if ((color == DISPLAY_COLORS_CMYK) ||
+	(color == DISPLAY_COLORS_SEPARATION)) {
+	/* check if separations have changed */
+	int i;
+	int num_visible = 0;
+	gchar *str;
+	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+	    gtk_label_get(
+		GTK_LABEL(GTK_BIN(img->separation[i])->child), &str);
+	    if (!img->devicen[i].used)
+		gtk_widget_hide(img->separation[i]);
+	    else if (strcmp(img->devicen[i].name, str) != 0) {
+		/* text has changed, update it */
+		gtk_label_set_text(
+		    GTK_LABEL(GTK_BIN(img->separation[i])->child),
+		    img->devicen[i].name);
+		gtk_widget_show(img->separation[i]);
+	    }
+	    if (img->devicen[i].used && img->devicen[i].visible)
+		num_visible++;
+	}
+	if (num_visible <= 1)
+	    gtk_widget_show(img->separation[i]);
+	else
+	    gtk_widget_hide(img->separation[i]);
+    }
 		
     /* some formats need to be converted for use by GdkRgb */
     switch (color) {
@@ -755,6 +879,12 @@
 		int x, y;
 		int cyan, magenta, yellow, black;
 		unsigned char *s, *d;
+		int vc = img->devicen[0].visible;
+		int vm = img->devicen[1].visible;
+		int vy = img->devicen[2].visible;
+		int vk = img->devicen[3].visible;
+		int vall = vc && vm && vy && vk;
+		int show_gray = (vc + vm + vy + vk == 1) && img->devicen_gray;
 		for (y = 0; y<img->height; y++) {
 		    s = img->buf + y * img->rowstride;
 		    d = img->rgbbuf + y * img->width * 3;
@@ -763,17 +893,84 @@
 			magenta = *s++;
 			yellow = *s++;
 			black = *s++;
-			if (!(img->separation & SEP_CYAN))
-			    cyan = 0;
-			if (!(img->separation & SEP_MAGENTA))
-			    magenta = 0;
-			if (!(img->separation & SEP_YELLOW))
-			    yellow = 0;
-			if (!(img->separation & SEP_BLACK))
-			    black = 0;
+			if (!vall) {
+			    if (!vc)
+				cyan = 0;
+			    if (!vm)
+				magenta = 0;
+			    if (!vy)
+				yellow = 0;
+			    if (!vk)
+				black = 0;
+			    if (show_gray) {
+				black += cyan + magenta + yellow;
+				cyan = magenta = yellow = 0;
+			    }
+			}
+			*d++ = (255-cyan)    * (255-black) / 255; /* r */
+			*d++ = (255-magenta) * (255-black) / 255; /* g */
+			*d++ = (255-yellow)  * (255-black) / 255; /* b */
+		    }
+		}
+	    }
+	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    if (depth == DISPLAY_DEPTH_8) {
+		int j;
+		int x, y;
+		unsigned char *s, *d;
+		int cyan, magenta, yellow, black;
+		int num_comp = 0;
+		int value;
+		int num_visible = 0;
+		int show_gray = 0;
+	        IMAGE_DEVICEN *devicen = img->devicen;
+		for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
+		    if (img->devicen[j].used) {
+		       num_comp = j+1;
+		       if (img->devicen[j].visible)
+			    num_visible++;
+		    }
+		}
+		if ((num_visible == 1) && img->devicen_gray)
+		    show_gray = 1;
+
+		for (y = 0; y<img->height; y++) {
+		    s = img->buf + y * img->rowstride;
+		    d = img->rgbbuf + y * img->width * 3;
+		    for (x=0; x<img->width; x++) {
+			cyan = magenta = yellow = black = 0;
+			if (show_gray) {
+			    for (j=0; j<num_comp; j++) {
+				devicen = &img->devicen[j];
+				if (devicen->visible && devicen->used)
+				    black += s[j];
+			    }
+			}
+			else {
+			    for (j=0; j<num_comp; j++) {
+				devicen = &img->devicen[j];
+				if (devicen->visible && devicen->used) {
+				    value = s[j];
+				    cyan    += value*devicen->cyan   /65535;
+				    magenta += value*devicen->magenta/65535;
+				    yellow  += value*devicen->yellow /65535;
+				    black   += value*devicen->black  /65535;
+				}
+			    }
+			}
+			if (cyan > 255)
+			   cyan = 255;
+			if (magenta > 255)
+			   magenta = 255;
+			if (yellow > 255)
+			   yellow = 255;
+			if (black > 255)
+			   black = 255;
 			*d++ = (255-cyan)    * (255-black) / 255; /* r */
 			*d++ = (255-magenta) * (255-black) / 255; /* g */
 			*d++ = (255-yellow)  * (255-black) / 255; /* b */
+			s += 8;
 		    }
 		}
 	    }
@@ -805,6 +1002,29 @@
     return 0;
 }
 
+
+static int 
+display_separation(void *handle, void *device,
+    int comp_num, const char *name,
+    unsigned short c, unsigned short m,
+    unsigned short y, unsigned short k)
+{
+    IMAGE *img = image_find(handle, device);
+    if (img == NULL)
+	return -1;
+    if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
+	return -1;
+    img->devicen[comp_num].used = 1;
+    strncpy(img->devicen[comp_num].name, name,
+	sizeof(img->devicen[comp_num].name)-1);
+    img->devicen[comp_num].cyan    = c;
+    img->devicen[comp_num].magenta = m;
+    img->devicen[comp_num].yellow  = y;
+    img->devicen[comp_num].black   = k;
+    return 0;
+}
+
+
 /* callback structure for "display" device */
 display_callback display = { 
     sizeof(display_callback),
@@ -819,7 +1039,8 @@
     display_page,
     display_update,
     NULL,	/* memalloc */
-    NULL	/* memfree */
+    NULL,	/* memfree */
+    display_separation
 };
 
 /*********************************************************************/
diff -u l:/cvs/gs/src/gdevdsp.c src/gdevdsp.c
--- l:/cvs/gs/src/gdevdsp.c	Sun May 30 09:50:05 2004
+++ src/gdevdsp.c	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 2001-2004, Ghostgum Software Pty Ltd.  All rights reserved.
 
    This software is provided AS-IS with no warranty, either express or
    implied.
@@ -50,6 +50,8 @@
 
 #include "gdevpccm.h"		/* 4-bit PC color */
 #include "gxdevmem.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
 #include "gdevdsp.h"
 #include "gdevdsp2.h"
 
@@ -86,6 +88,13 @@
 private dev_proc_put_params(display_put_params);
 private dev_proc_finish_copydevice(display_finish_copydevice);
 
+private dev_proc_get_color_mapping_procs(display_separation_get_color_mapping_procs);
+private dev_proc_get_color_comp_index(display_separation_get_color_comp_index);
+private dev_proc_encode_color(display_separation_encode_color);
+private dev_proc_decode_color(display_separation_decode_color);
+private dev_proc_update_spot_equivalent_colors(display_update_spot_equivalent_colors);
+
+
 private const gx_device_procs display_procs =
 {
     display_open,
@@ -137,19 +146,35 @@
     NULL,				/* end_transparency_group */
     NULL,				/* begin_transparency_mask */
     NULL,				/* end_transparency_mask */
-    NULL				/* discard_transparency_layer */
+    NULL,				/* discard_transparency_layer */
+    NULL,				/* get_color_mapping_procs */
+    NULL,				/* get_color_comp_index */
+    NULL,           			/* encode_color */
+    NULL,           			/* 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 */\
+    display_update_spot_equivalent_colors /* update_spot_equivalent_colors */
 };
 
 /* GC descriptor */
 public_st_device_display();
 
 private 
-ENUM_PTRS_WITH(display_enum_ptrs, gx_device_display *ddev) return 0;
-    case 0: 
+ENUM_PTRS_WITH(display_enum_ptrs, gx_device_display *ddev)
+    if (index == 0) {
 	if (ddev->mdev) {
 	    return ENUM_OBJ(gx_device_enum_ptr((gx_device *)ddev->mdev));
 	}
 	return 0;
+    }
+    else if (index-1 < ddev->devn_params.separations.num_separations)
+        ENUM_RETURN(ddev->devn_params.separations.names[index-1]);
+    else
+	return 0;
 ENUM_PTRS_END
 
 private 
@@ -158,6 +183,11 @@
 	ddev->mdev = (gx_device_memory *)
 	    gx_device_reloc_ptr((gx_device *)ddev->mdev, gcst);
     }
+    {   int i;
+        for (i = 0; i < ddev->devn_params.separations.num_separations; ++i) {
+            RELOC_PTR(gx_device_display, devn_params.separations.names[i]);
+        }
+    }
 RELOC_PTRS_END
 
 
@@ -174,6 +204,17 @@
     0,				/* nFormat */
     NULL,			/* pBitmap */
     0, 				/* ulBitmapSize */
+
+    {    /* devn_params specific parameters */
+      8,        /* Bits per color - must match ncomp, depth, etc. */
+      DeviceCMYKComponents,     /* Names of color model colorants */
+      4,                        /* Number of colorants for CMYK */
+      0,                        /* MaxSeparations has not been specified */
+      {0},                      /* SeparationNames */
+      {0},                      /* SeparationOrder names */
+      {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+    },
+    { true }                   /* equivalent CMYK colors for spot colors */
 };
 
 
@@ -183,6 +224,7 @@
 private void display_free_bitmap(gx_device_display * dev);
 private int display_alloc_bitmap(gx_device_display *, gx_device *);
 private int display_set_color_format(gx_device_display *dev, int nFormat);
+private int display_set_separations(gx_device_display *dev);
 
 /* Open the display driver. */
 private int
@@ -260,6 +302,8 @@
     gx_device_display *ddev = (gx_device_display *) dev;
     if (ddev->callback == NULL)
 	return 0;
+    display_set_separations(ddev);
+
     (*(ddev->callback->display_sync))(ddev->pHandle, dev);
     return (0);
 }
@@ -273,6 +317,7 @@
     int code;
     if (ddev->callback == NULL)
 	return 0;
+    display_set_separations(ddev);
 
     code = (*(ddev->callback->display_page))
 			(ddev->pHandle, dev, copies, flush);
@@ -695,12 +740,16 @@
 display_get_params(gx_device * dev, gs_param_list * plist)
 {
     gx_device_display *ddev = (gx_device_display *) dev;
-    int code = gx_default_get_params(dev, plist);
+    int code;
+
+    code = gx_default_get_params(dev, plist);
     (void)(code < 0 ||
 	(code = param_write_long(plist, 
 	    "DisplayHandle", (long *)(&ddev->pHandle))) < 0 ||
 	(code = param_write_int(plist, 
-	    "DisplayFormat", &ddev->nFormat)) < 0 );
+	    "DisplayFormat", &ddev->nFormat)) < 0 ||
+	(code = devn_get_params(dev, plist, &ddev->devn_params, 
+		&ddev->equiv_cmyk_colors)) < 0);
     return code;
 }
 
@@ -721,6 +770,13 @@
     int old_format = ddev->nFormat;
     void *old_handle = ddev->pHandle;
 
+    gs_devn_params *pdevn_params = &ddev->devn_params;
+    equivalent_cmyk_color_params *pequiv_colors = &ddev->equiv_cmyk_colors;
+    /* Save current data in case we have a problem */
+    gx_device_color_info save_info = dev->color_info;
+    gs_devn_params saved_devn_params = *pdevn_params;
+    equivalent_cmyk_color_params saved_equiv_colors = *pequiv_colors;
+
     int format;
     void *handle;
 
@@ -769,12 +825,27 @@
     }
 
     if (ecode >= 0) {
+	/* Use utility routine to handle devn parameters */
+	ecode = devn_put_params(dev, plist, pdevn_params, pequiv_colors);
+        /* 
+	 * Setting MaxSeparations changes color_info.depth in
+	 * devn_put_params, but we always use 64bpp,
+	 * so reset it to the the correct value.
+	 */
+	if ((ddev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)
+	    dev->color_info.depth = sizeof(gx_color_index)*8;
+    }
+
+    if (ecode >= 0) {
 	/* Prevent gx_default_put_params from closing the device. */
 	dev->is_open = false;
 	ecode = gx_default_put_params(dev, plist);
 	dev->is_open = is_open;
     }
     if (ecode < 0) {
+	/* If we have an error then restore original data. */
+	*pdevn_params = saved_devn_params;
+	*pequiv_colors = saved_equiv_colors;
 	if (format != old_format)
 	    display_set_color_format(ddev, old_format);
 	ddev->pHandle = old_handle;
@@ -795,6 +866,8 @@
 	    ddev->nFormat) < 0) {
 	    /* caller won't let us change the size */
 	    /* restore parameters then return an error */
+	    *pdevn_params = saved_devn_params;
+	    *pequiv_colors = saved_equiv_colors;
 	    display_set_color_format(ddev, old_format);
 	    ddev->nFormat = old_format;
 	    ddev->pHandle = old_handle;
@@ -840,6 +913,134 @@
     return 0;
 }
 
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the separation color components for the display device.
+ */
+private void
+display_separation_gray_cs_to_cmyk_cm(gx_device * dev, frac gray, frac out[])
+{
+    int * map =
+      (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+    gray_cs_to_devn_cm(dev, map, gray, out);
+}
+
+private void
+display_separation_rgb_cs_to_cmyk_cm(gx_device * dev, 
+    const gs_imager_state *pis, frac r, frac g, frac b, frac out[])
+{
+    int * map =
+      (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+    rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
+}
+
+private void
+display_separation_cmyk_cs_to_cmyk_cm(gx_device * dev, 
+    frac c, frac m, frac y, frac k, frac out[])
+{
+    int * map =
+      (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+    cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
+}
+
+private const gx_cm_color_map_procs display_separation_cm_procs = {
+    display_separation_gray_cs_to_cmyk_cm, 
+    display_separation_rgb_cs_to_cmyk_cm, 
+    display_separation_cmyk_cs_to_cmyk_cm
+};
+
+private const gx_cm_color_map_procs *
+display_separation_get_color_mapping_procs(const gx_device * dev)
+{
+    return &display_separation_cm_procs;
+}
+
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+private gx_color_index
+display_separation_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+    int bpc = ((gx_device_display *)dev)->devn_params.bitspercomponent;
+    int drop = sizeof(gx_color_value) * 8 - bpc;
+    gx_color_index color = 0;
+    int i = 0;
+    int ncomp = dev->color_info.num_components;
+
+    for (; i<ncomp; i++) {
+	color <<= bpc;
+	color |= (colors[i] >> drop);
+    }
+    if (bpc*ncomp < sizeof(gx_color_index)*8)
+	color <<= (sizeof(gx_color_index)*8 - ncomp*bpc);
+    return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+private int
+display_separation_decode_color(gx_device * dev, gx_color_index color, 
+    gx_color_value * out)
+{
+    int bpc = ((gx_device_display *)dev)->devn_params.bitspercomponent;
+    int drop = sizeof(gx_color_value) * 8 - bpc;
+    int mask = (1 << bpc) - 1;
+    int i = 0;
+    int ncomp = dev->color_info.num_components;
+
+    if (bpc*ncomp < sizeof(gx_color_index)*8)
+	color >>= (sizeof(gx_color_index)*8 - ncomp*bpc);
+    for (; i<ncomp; i++) {
+	out[ncomp - i - 1] = (gx_color_value) ((color & mask) << drop);
+	color >>= bpc;
+    }
+    return 0;
+}
+
+/*
+ *  Device proc for updating the equivalent CMYK color for spot colors.
+ */
+private int
+display_update_spot_equivalent_colors(gx_device * dev, const gs_state * pgs)
+{
+    gx_device_display * ddev = (gx_device_display *)dev;
+
+    if ((ddev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)
+        update_spot_equivalent_cmyk_colors(dev, pgs,
+		    &ddev->devn_params, &ddev->equiv_cmyk_colors);
+    return 0;
+}
+
+/*
+ * This routine will check to see if the color component name  match those
+ * that are available amoung the current device's color components.  
+ *
+ * Parameters:
+ *   dev - pointer to device data structure.
+ *   pname - pointer to name (zero termination not required)
+ *   nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found.  It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
+ * the colorant is not being used due to a SeparationOrder device parameter.
+ * It returns a negative value if not found.
+ */
+private int
+display_separation_get_color_comp_index(gx_device * dev, 
+    const char * pname, int name_size, int component_type)
+{
+    return devn_get_color_comp_index(dev,
+		&(((gx_device_display *)dev)->devn_params), 
+		&(((gx_device_display *)dev)->equiv_cmyk_colors), 
+		pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS);
+}
+
+
 /* ------ Internal routines ------ */
 
 /* Make sure we have been given a valid structure */
@@ -849,15 +1050,27 @@
     if (ddev->callback == 0)
 	return_error(gs_error_rangecheck);
 
-    if (ddev->callback->size != sizeof(display_callback))
-	return_error(gs_error_rangecheck);
+    if (ddev->callback->size == sizeof(struct display_callback_v1_s)) {
+	/* Original V1 structure */
+	if (ddev->callback->version_major != DISPLAY_VERSION_MAJOR_V1)
+	    return_error(gs_error_rangecheck);
 
-    if (ddev->callback->version_major != DISPLAY_VERSION_MAJOR)
-	return_error(gs_error_rangecheck);
+	/* complain if caller asks for newer features */
+	if (ddev->callback->version_minor > DISPLAY_VERSION_MINOR_V1)
+	    return_error(gs_error_rangecheck);
+    }
+    else {
+	/* V2 structure with added display_separation callback */
+	if (ddev->callback->size != sizeof(display_callback))
+	    return_error(gs_error_rangecheck);
 
-    /* complain if caller asks for newer features */
-    if (ddev->callback->version_minor > DISPLAY_VERSION_MINOR)
-	return_error(gs_error_rangecheck);
+	if (ddev->callback->version_major != DISPLAY_VERSION_MAJOR)
+	    return_error(gs_error_rangecheck);
+
+	/* complain if caller asks for newer features */
+	if (ddev->callback->version_minor > DISPLAY_VERSION_MINOR)
+	    return_error(gs_error_rangecheck);
+    }
 
     if ((ddev->callback->display_open == NULL) ||
 	(ddev->callback->display_close == NULL) ||
@@ -867,8 +1080,10 @@
 	(ddev->callback->display_page == NULL))
 	return_error(gs_error_rangecheck);
 
-    /* don't test display_update, display_memalloc or display_memfree
-     * since these may be NULL if not provided
+    /* Don't test display_update, display_memalloc or display_memfree
+     * since these may be NULL if not provided.
+     * Don't test display_separation, since this may be NULL if
+     * separation format is not supported.
      */
 
     return 0;
@@ -980,6 +1195,79 @@
     return ccode;
 }
 
+private int 
+display_set_separations(gx_device_display *dev)
+{
+    if (((dev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION) &&
+	(dev->callback->version_major > DISPLAY_VERSION_MAJOR_V1) &&
+	(dev->callback->display_separation != NULL)) {
+	/* Tell the client about the separation to composite mapping */
+	char name[gp_file_name_sizeof];
+	int num_spot = dev->devn_params.separations.num_separations;
+	int num_std_colorants = dev->devn_params.num_std_colorant_names;
+ 	int num_comp = num_std_colorants + num_spot;
+        int comp_map[GX_DEVICE_COLOR_MAX_COMPONENTS];
+	int comp_num;
+	int sep_num;
+	int sep_name_size;
+	unsigned int c, m, y, k;
+
+	/* Map the separation numbers to component numbers */
+	memset(comp_map, 0, sizeof(comp_map));
+	for (sep_num = 0; sep_num < num_comp; sep_num++) {
+	    comp_num = dev->devn_params.separation_order_map[sep_num];
+	    if (comp_num >= 0 && comp_num < GX_DEVICE_COLOR_MAX_COMPONENTS)
+		comp_map[comp_num] = sep_num;
+	}
+	/* For each component, tell the client the separation mapping */
+	for (comp_num = 0; comp_num < num_comp; comp_num++) {
+	    c = y = m = k = 0;
+	    sep_num = comp_map[comp_num];
+	    /* Get the CMYK equivalent */
+	    if (sep_num < dev->devn_params.num_std_colorant_names) {
+		sep_name_size = 
+		    strlen(dev->devn_params.std_colorant_names[sep_num]);
+		if (sep_name_size > sizeof(name)-2)
+		    sep_name_size = sizeof(name)-1;
+		memcpy(name, dev->devn_params.std_colorant_names[sep_num],
+		    sep_name_size);
+		name[sep_name_size] = '\0';
+		switch (sep_num) {
+		    case 0: c = 65535; break;
+		    case 1: m = 65535; break;
+		    case 2: y = 65535; break;
+		    case 3: k = 65535; break;
+		}
+	    }
+	    else {
+		sep_num -= dev->devn_params.num_std_colorant_names;
+		sep_name_size = 
+		    dev->devn_params.separations.names[sep_num]->size;
+		if (sep_name_size > sizeof(name)-2)
+		    sep_name_size = sizeof(name)-1;
+		memcpy(name, dev->devn_params.separations.names[sep_num]->data, 
+		    sep_name_size);
+		name[sep_name_size] = '\0';
+		if (dev->equiv_cmyk_colors.color[sep_num].color_info_valid) {
+		    c = dev->equiv_cmyk_colors.color[sep_num].c
+			   * 65535 / frac_1;
+		    m = dev->equiv_cmyk_colors.color[sep_num].m
+			   * 65535 / frac_1;
+		    y = dev->equiv_cmyk_colors.color[sep_num].y
+			   * 65535 / frac_1;
+		    k = dev->equiv_cmyk_colors.color[sep_num].k
+			   * 65535 / frac_1;
+		}
+	    }
+	    (*dev->callback->display_separation)(dev->pHandle, dev, 
+		comp_num, name, 
+		(unsigned short)c, (unsigned short)m, 
+		(unsigned short)y, (unsigned short)k);
+	}
+    }
+    return 0;
+}
+
 /*
  * This is a utility routine to build the display device's color_info
  * structure (except for the anti alias info).
@@ -1012,6 +1300,10 @@
 	    pdci->gray_index = 3;
 	    break;
 	default:
+	    /* Anything else is separations */
+	    pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+	    pdci->cm_name = "DeviceCMYK";
+	    pdci->gray_index = GX_CINFO_COMP_NO_INDEX; /* may not have K */
 	    break;
     }
 }
@@ -1210,6 +1502,22 @@
 	    else
 		return_error(gs_error_rangecheck);
 	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    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);
+	    if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) {
+		ddev->devn_params.bitspercomponent = bpc;
+		set_color_procs(pdev, 
+		    display_separation_encode_color, 
+		    display_separation_decode_color,
+		    display_separation_get_color_mapping_procs,
+		    display_separation_get_color_comp_index);
+	    }
+	    else
+		return_error(gs_error_rangecheck);
+	    break;
 	default:
 	    return_error(gs_error_rangecheck);
     }
@@ -1229,6 +1537,9 @@
 	case DISPLAY_COLORS_CMYK:
 	    ddev->color_info.gray_index = 3;
 	    break;
+	case DISPLAY_COLORS_SEPARATION:
+	    ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
+	    break;
     }
     ddev->nFormat = nFormat;
 
diff -u l:/cvs/gs/src/gdevdsp.h src/gdevdsp.h
--- l:/cvs/gs/src/gdevdsp.h	Thu Nov 21 09:36:43 2002
+++ src/gdevdsp.h	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 2001-2004, Ghostgum Software Pty Ltd.  All rights reserved.
 
    This software is provided AS-IS with no warranty, either express or
    implied.
@@ -55,19 +55,23 @@
  * 
  */
 
-#define DISPLAY_VERSION_MAJOR 1
+#define DISPLAY_VERSION_MAJOR 2
 #define DISPLAY_VERSION_MINOR 0
 
+#define DISPLAY_VERSION_MAJOR_V1 1 /* before separation format was added */
+#define DISPLAY_VERSION_MINOR_V1 0
+
 /* The display format is set by a combination of the following bitfields */
 
 /* Define the color space alternatives */
 typedef enum {
-    DISPLAY_COLORS_NATIVE = (1<<0),
-    DISPLAY_COLORS_GRAY   = (1<<1),
-    DISPLAY_COLORS_RGB    = (1<<2),
-    DISPLAY_COLORS_CMYK   = (1<<3)
+    DISPLAY_COLORS_NATIVE	 = (1<<0),
+    DISPLAY_COLORS_GRAY  	 = (1<<1),
+    DISPLAY_COLORS_RGB   	 = (1<<2),
+    DISPLAY_COLORS_CMYK  	 = (1<<3),
+    DISPLAY_COLORS_SEPARATION    = (1<<19)
 } DISPLAY_FORMAT_COLOR;
-#define DISPLAY_COLORS_MASK 0x000fL
+#define DISPLAY_COLORS_MASK 0x8000fL
 
 /* Define whether alpha information, or an extra unused bytes is included */
 /* DISPLAY_ALPHA_FIRST and DISPLAY_ALPHA_LAST are not implemented */
@@ -129,6 +133,11 @@
 typedef struct display_callback_s display_callback;
 #endif
 
+/*
+ * Note that for Windows, the display callback functions are 
+ * cdecl, not stdcall.  This differs from those in iapi.h.
+ */
+
 struct display_callback_s {
     /* Size of this structure */
     /* Used for checking if we have been handed a valid structure */
@@ -196,7 +205,44 @@
     /* If this is NULL, the Ghostscript memory device will free the bitmap */
     int (*display_memfree)(void *handle, void *device, void *mem);
    
+    /* Added in V2 */
+    /* When using separation color space (DISPLAY_COLORS_SEPARATION),
+     * give a mapping for one separation component.
+     * This is called for each new component found.
+     * It may be called multiple times for each component.
+     * It may be called at any time between display_size
+     * and display_close.
+     * The client uses this to map from the separations to CMYK
+     * and hence to RGB for display.
+     * GS must only use this callback if version_major >= 2.
+     * The unsigned short c,m,y,k values are 65535 = 1.0.
+     */
+    int (*display_separation)(void *handle, void *device,
+	int component, const char *component_name,
+	unsigned short c, unsigned short m, 
+	unsigned short y, unsigned short k);
+};
+
+/* This is the V1 structure, before separation format was added */
+struct display_callback_v1_s {
+    int size;
+    int version_major;
+    int version_minor;
+    int (*display_open)(void *handle, void *device);
+    int (*display_preclose)(void *handle, void *device);
+    int (*display_close)(void *handle, void *device);
+    int (*display_presize)(void *handle, void *device,
+	int width, int height, int raster, unsigned int format);
+    int (*display_size)(void *handle, void *device, int width, int height, 
+	int raster, unsigned int format, unsigned char *pimage);
+    int (*display_sync)(void *handle, void *device);
+    int (*display_page)(void *handle, void *device, int copies, int flush);
+    int (*display_update)(void *handle, void *device, int x, int y, 
+	int w, int h);
+    void *(*display_memalloc)(void *handle, void *device, unsigned long size);
+    int (*display_memfree)(void *handle, void *device, void *mem);
 };
 
+#define DISPLAY_CALLBACK_V1_SIZEOF sizeof(struct display_callback_v1_s)
 
 #endif /* gdevdsp_INCLUDED */
diff -u l:/cvs/gs/src/gdevdsp2.h src/gdevdsp2.h
--- l:/cvs/gs/src/gdevdsp2.h	Thu Feb 28 22:50:38 2002
+++ src/gdevdsp2.h	Fri Jul 02 09:45:48 2004
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, Ghostgum Software Pty Ltd.  All rights reserved.
+/* Copyright (C) 2001-2004, Ghostgum Software Pty Ltd.  All rights reserved.
 
    This software is provided AS-IS with no warranty, either express or
    implied.
@@ -28,7 +28,9 @@
 	void *pHandle;\
 	int nFormat;\
 	void *pBitmap;\
-	unsigned long ulBitmapSize
+	unsigned long ulBitmapSize;\
+        gs_devn_params devn_params;\
+        equivalent_cmyk_color_params equiv_cmyk_colors
 
 /* The device descriptor */
 struct gx_device_display_s {
diff -u l:/cvs/gs/src/idisp.c src/idisp.c
--- l:/cvs/gs/src/idisp.c	Wed Apr 30 10:34:54 2003
+++ src/idisp.c	Fri Jul 02 09:45:48 2004
@@ -44,6 +44,8 @@
 #include "gxdevice.h"
 #include "gxdevmem.h"
 #include "idisp.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
 #include "gdevdsp.h"
 #include "gdevdsp2.h"

diff -u l:/cvs/gs/doc/API.htm doc/API.htm
--- l:/cvs/gs/doc/API.htm	Sun May 30 09:46:57 2004
+++ doc/API.htm	Fri Jul 02 10:03:58 2004
@@ -662,7 +662,7 @@
 </blockquote>
 Options include
 <ul>
-<li> native, gray, RGB or CMYK color spaces.
+<li> native, gray, RGB, CMYK or separation color spaces.
 <li> alpha byte (ignored).
 <li> 1 to 16 bits/component.
 <li> bigendian (RGB) or littleendian (BGR) order.
Common subdirectories: l:/cvs/gs/doc/CVS and doc/CVS
diff -u l:/cvs/gs/doc/Devices.htm doc/Devices.htm
--- l:/cvs/gs/doc/Devices.htm	Thu Jun 24 08:47:39 2004
+++ doc/Devices.htm	Fri Jul 02 10:03:25 2004
@@ -43,6 +43,7 @@
 <li><a href="#Display_devices">Display devices</a>
 <ul>
 <li><a href="#x11_devices">X Window System</a>
+<li><a href="#display_device">display device (MS Windows, OS/2, gtk+)</a>
 </ul>
 <li><a href="#IJS">IJS - Inkjet and other raster devices</a>
 <li><a href="#Rinkj">Rinkj - Resplendent inkjet driver</a>
@@ -609,6 +610,52 @@
 <dd>This is a device for 4 bpp (16-level) monochrome displays.
 </dl>
 
+<h3><a name="display_device"></a>display device (MS Windows, OS/2, gtk+)</h3>
+<p>
+The <b><tt>display</tt></b> device is used by the MS Windows, 
+OS/2 and the gtk+ versions of ghostscript.
+</p>
+
+<h4>Options</h4>
+
+<p>The display device has several user settable options.</p>
+
+<blockquote>
+<dl>
+<dt><b><tt>-dDisplayFormat=</tt></b><b><em>N</em></b> (integer bit-field)
+<dd>Some common values are 16#30804 for Windows RGB, 16#804 for gtk+ RGB,
+16#20101 for Windows monochrome, 16#102 for gtk+ monochrome, 
+16#20802 grayscale, 16#20808 for CMYK, 16#a0800 for separations.
+The bit fields are
+<ul>
+<li> native (1), gray (2), RGB (4), CMYK (8), or separation (80000) 
+  color spaces.
+<li> unused first byte (40) or last byte (80).
+<li> 1 (100), 4 (400), or 8 (800) bits/component.
+<li> bigendian (00000 = RGB) or littleendian (10000 = BGR) order.
+<li> top first (20000) or bottom first (00000) raster.
+<li> 16 bits/pixel with 555 (00000) or 565 (40000) bitfields.
+</ul>
+For more details, see the <a href="API.htm#display">Ghostscript 
+Interpreter API.</a>
+</dl>
+
+</blockquote>
+
+When using the separation color space, the following options may be set
+using setpagedevice, as described in the PostScript Language Reference:
+
+<blockquote>
+<dl>
+<dt><b><tt>SeparationColorNames</tt></b>
+<dd>An array giving the names of the spot colors
+
+<dt><b><tt>SeparationOrder</tt></b>
+<dd>An array giving the names and order of the colorants 
+to be output.
+</dl>
+</blockquote>
+
 
 <hr>

_______________________________________________
gs-code-review mailing list
[email protected]
http://www.ghostscript.com/mailman/listinfo/gs-code-review