xref: /plan9/sys/src/cmd/gs/src/dxmain.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2001 Ghostgum Software Pty Ltd.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: dxmain.c,v 1.15 2004/09/14 06:42:32 ghostgum Exp $ */
18 
19 /* dxmain.c */
20 /*
21  * Ghostscript frontend which provides a graphical window
22  * using Gtk+.  Load time linking to libgs.so
23  * Compile using
24  *    gcc `gtk-config --cflags` -o gs dxmain.c -lgs `gtk-config --libs`
25  *
26  * The ghostscript library needs to be compiled with
27  *  gcc -fPIC -g -c -Wall file.c
28  *  gcc -shared -Wl,-soname,libgs.so.6 -o libgs.so.6.60 file.o -lc
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <gtk/gtk.h>
37 #define __PROTOTYPES__
38 #include "ierrors.h"
39 #include "iapi.h"
40 #include "gdevdsp.h"
41 
42 const char start_string[] = "systemdict /start get exec\n";
43 
44 static void read_stdin_handler(gpointer data, gint fd,
45 	GdkInputCondition condition);
46 static int gsdll_stdin(void *instance, char *buf, int len);
47 static int gsdll_stdout(void *instance, const char *str, int len);
48 static int gsdll_stdout(void *instance, const char *str, int len);
49 static int display_open(void *handle, void *device);
50 static int display_preclose(void *handle, void *device);
51 static int display_close(void *handle, void *device);
52 static int display_presize(void *handle, void *device, int width, int height,
53 	int raster, unsigned int format);
54 static int display_size(void *handle, void *device, int width, int height,
55 	int raster, unsigned int format, unsigned char *pimage);
56 static int display_sync(void *handle, void *device);
57 static int display_page(void *handle, void *device, int copies, int flush);
58 static int display_update(void *handle, void *device, int x, int y,
59 	int w, int h);
60 
61 #ifndef min
62 #define min(a,b) ((a) < (b) ? (a) : (b))
63 #endif
64 
65 /*********************************************************************/
66 /* stdio functions */
67 
68 struct stdin_buf {
69    char *buf;
70    int len;	/* length of buffer */
71    int count;	/* number of characters returned */
72 };
73 
74 /* handler for reading non-blocking stdin */
75 static void
read_stdin_handler(gpointer data,gint fd,GdkInputCondition condition)76 read_stdin_handler(gpointer data, gint fd, GdkInputCondition condition)
77 {
78     struct stdin_buf *input = (struct stdin_buf *)data;
79 
80     if (condition & GDK_INPUT_EXCEPTION) {
81 	g_print("input exception");
82 	input->count = 0;	/* EOF */
83     }
84     else if (condition & GDK_INPUT_READ) {
85 	/* read returns -1 for error, 0 for EOF and +ve for OK */
86 	input->count = read(fd, input->buf, input->len);
87 	if (input->count < 0)
88 	    input->count = 0;
89     }
90     else {
91 	g_print("input condition unknown");
92 	input->count = 0;	/* EOF */
93     }
94 }
95 
96 /* callback for reading stdin */
97 static int
gsdll_stdin(void * instance,char * buf,int len)98 gsdll_stdin(void *instance, char *buf, int len)
99 {
100     struct stdin_buf input;
101     gint input_tag;
102 
103     input.len = len;
104     input.buf = buf;
105     input.count = -1;
106 
107     input_tag = gdk_input_add(fileno(stdin),
108 	(GdkInputCondition)(GDK_INPUT_READ | GDK_INPUT_EXCEPTION),
109 	read_stdin_handler, &input);
110     while (input.count < 0)
111 	gtk_main_iteration_do(TRUE);
112     gdk_input_remove(input_tag);
113 
114     return input.count;
115 }
116 
117 static int
gsdll_stdout(void * instance,const char * str,int len)118 gsdll_stdout(void *instance, const char *str, int len)
119 {
120     gtk_main_iteration_do(FALSE);
121     fwrite(str, 1, len, stdout);
122     fflush(stdout);
123     return len;
124 }
125 
126 static int
gsdll_stderr(void * instance,const char * str,int len)127 gsdll_stderr(void *instance, const char *str, int len)
128 {
129     gtk_main_iteration_do(FALSE);
130     fwrite(str, 1, len, stderr);
131     fflush(stderr);
132     return len;
133 }
134 
135 /*********************************************************************/
136 /* dll display device */
137 
138 typedef struct IMAGE_DEVICEN_S IMAGE_DEVICEN;
139 struct IMAGE_DEVICEN_S {
140     int used;		/* non-zero if in use */
141     int visible;	/* show on window */
142     char name[64];
143     int cyan;
144     int magenta;
145     int yellow;
146     int black;
147     int menu;		/* non-zero if menu item added to system menu */
148 };
149 #define IMAGE_DEVICEN_MAX 8
150 
151 typedef struct IMAGE_S IMAGE;
152 struct IMAGE_S {
153     void *handle;
154     void *device;
155     GtkWidget *window;
156     GtkWidget *vbox;
157     GtkWidget *cmyk_bar;
158     GtkWidget *separation[IMAGE_DEVICEN_MAX];
159     GtkWidget *show_as_gray;
160     GtkWidget *scroll;
161     GtkWidget *darea;
162     guchar *buf;
163     gint width;
164     gint height;
165     gint rowstride;
166     unsigned int format;
167     GdkRgbCmap *cmap;
168     int devicen_gray;	/* true if a single separation should be shown gray */
169     IMAGE_DEVICEN devicen[IMAGE_DEVICEN_MAX];
170     guchar *rgbbuf;	/* used when we need to convert raster format */
171     IMAGE *next;
172 };
173 
174 IMAGE *first_image;
175 static IMAGE *image_find(void *handle, void *device);
176 static void window_destroy(GtkWidget *w, gpointer data);
177 static void window_create(IMAGE *img);
178 static void window_resize(IMAGE *img);
179 static gboolean window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
180 
181 static IMAGE *
image_find(void * handle,void * device)182 image_find(void *handle, void *device)
183 {
184     IMAGE *img;
185     for (img = first_image; img!=0; img=img->next) {
186 	if ((img->handle == handle) && (img->device == device))
187 	    return img;
188     }
189     return NULL;
190 }
191 
192 
193 
194 static gboolean
window_draw(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)195 window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
196 {
197     IMAGE *img = (IMAGE *)user_data;
198     if (img && img->window && img->buf) {
199         int color = img->format & DISPLAY_COLORS_MASK;
200 	int depth = img->format & DISPLAY_DEPTH_MASK;
201 	int x, y, width, height;
202 	if (event->area.x + event->area.width > img->width) {
203 	    x = img->width;
204 	    width = (event->area.x + event->area.width) - x;
205 	    y = event->area.y;
206             height = min(img->height, event->area.y + event->area.height) - y;
207 	    gdk_window_clear_area(widget->window, x, y, width, height);
208 	}
209 	if (event->area.y + event->area.height > img->height) {
210 	    x = event->area.x;
211 	    width = event->area.width;
212 	    y = img->height;
213 	    height = (event->area.y + event->area.height) - y;
214 	    gdk_window_clear_area(widget->window, x, y, width, height);
215 	}
216 	x = event->area.x;
217 	y = event->area.y;
218 	width = event->area.width;
219 	height = event->area.height;
220 	if ((x>=0) && (y>=0) && (x < img->width) && (y < img->height)) {
221 	    /* drawing area intersects bitmap */
222 	    if (x + width > img->width)
223 		width = img->width - x;
224 	    if (y + height > img->height)
225 		height =  img->height - y;
226 	    switch (color) {
227 		case DISPLAY_COLORS_NATIVE:
228 		    if (depth == DISPLAY_DEPTH_8)
229 			gdk_draw_indexed_image(widget->window,
230 			    widget->style->fg_gc[GTK_STATE_NORMAL],
231 			    x, y, width, height,
232 			    GDK_RGB_DITHER_MAX,
233 			    img->buf + x + y*img->rowstride,
234 			    img->rowstride, img->cmap);
235 		    else if ((depth == DISPLAY_DEPTH_16) && img->rgbbuf)
236 			gdk_draw_rgb_image(widget->window,
237 			    widget->style->fg_gc[GTK_STATE_NORMAL],
238 			    x, y, width, height,
239 			    GDK_RGB_DITHER_MAX,
240 			    img->rgbbuf + x*3 + y*img->width*3,
241 			    img->width * 3);
242 		    break;
243 		case DISPLAY_COLORS_GRAY:
244 		    if (depth == DISPLAY_DEPTH_8)
245 			gdk_draw_gray_image(widget->window,
246 			    widget->style->fg_gc[GTK_STATE_NORMAL],
247 			    x, y, width, height,
248 			    GDK_RGB_DITHER_MAX,
249 			    img->buf + x + y*img->rowstride,
250 			    img->rowstride);
251 		    break;
252 		case DISPLAY_COLORS_RGB:
253 		    if (depth == DISPLAY_DEPTH_8) {
254 			if (img->rgbbuf)
255 			    gdk_draw_rgb_image(widget->window,
256 				widget->style->fg_gc[GTK_STATE_NORMAL],
257 				x, y, width, height,
258 				GDK_RGB_DITHER_MAX,
259 				img->rgbbuf + x*3 + y*img->width*3,
260 				img->width * 3);
261 			else
262 			    gdk_draw_rgb_image(widget->window,
263 				widget->style->fg_gc[GTK_STATE_NORMAL],
264 				x, y, width, height,
265 				GDK_RGB_DITHER_MAX,
266 				img->buf + x*3 + y*img->rowstride,
267 				img->rowstride);
268 		    }
269 		    break;
270 		case DISPLAY_COLORS_CMYK:
271 		    if (((depth == DISPLAY_DEPTH_1) ||
272 		        (depth == DISPLAY_DEPTH_8)) && img->rgbbuf)
273 			gdk_draw_rgb_image(widget->window,
274 			    widget->style->fg_gc[GTK_STATE_NORMAL],
275 			    x, y, width, height,
276 			    GDK_RGB_DITHER_MAX,
277 			    img->rgbbuf + x*3 + y*img->width*3,
278 			    img->width * 3);
279 		    break;
280 		case DISPLAY_COLORS_SEPARATION:
281 		    if ((depth == DISPLAY_DEPTH_8) && img->rgbbuf)
282 			gdk_draw_rgb_image(widget->window,
283 			    widget->style->fg_gc[GTK_STATE_NORMAL],
284 			    x, y, width, height,
285 			    GDK_RGB_DITHER_MAX,
286 			    img->rgbbuf + x*3 + y*img->width*3,
287 			    img->width * 3);
288 		    break;
289 	    }
290 	}
291     }
292     return TRUE;
293 }
294 
window_destroy(GtkWidget * w,gpointer data)295 static void window_destroy(GtkWidget *w, gpointer data)
296 {
297     IMAGE *img = (IMAGE *)data;
298     img->window = NULL;
299     img->scroll = NULL;
300     img->darea = NULL;
301 }
302 
window_create(IMAGE * img)303 static void window_create(IMAGE *img)
304 {
305     /* Create a gtk window */
306     img->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
307     gtk_window_set_title(GTK_WINDOW(img->window), "gs");
308     img->vbox = gtk_vbox_new(FALSE, 0);
309     gtk_container_add(GTK_CONTAINER(img->window), img->vbox);
310     gtk_widget_show(img->vbox);
311 
312     img->darea = gtk_drawing_area_new();
313     gtk_widget_show(img->darea);
314     img->scroll = gtk_scrolled_window_new(NULL, NULL);
315     gtk_widget_show(img->scroll);
316     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(img->scroll),
317 	GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
318     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(img->scroll),
319 	img->darea);
320     gtk_box_pack_start(GTK_BOX(img->vbox), img->scroll, TRUE, TRUE, 0);
321     gtk_signal_connect(GTK_OBJECT (img->darea), "expose-event",
322                         GTK_SIGNAL_FUNC (window_draw), img);
323     gtk_signal_connect(GTK_OBJECT (img->window), "destroy",
324 			GTK_SIGNAL_FUNC (window_destroy), img);
325     /* do not show img->window until we know the image size */
326 }
327 
window_resize(IMAGE * img)328 static void window_resize(IMAGE *img)
329 {
330     gtk_drawing_area_size(GTK_DRAWING_AREA (img->darea),
331 	img->width, img->height);
332     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE)) {
333 	/* We haven't yet shown the window, so set a default size
334 	 * which is smaller than the desktop to allow room for
335 	 * desktop toolbars, and if possible a little larger than
336 	 * the image to allow room for the scroll bars.
337 	 * We don't know the width of the scroll bars, so just guess. */
338 	gtk_window_set_default_size(GTK_WINDOW(img->window),
339 	    min(gdk_screen_width()-96, img->width+24),
340 	    min(gdk_screen_height()-96, img->height+24));
341     }
342 }
343 
window_separation(IMAGE * img,int sep)344 static void window_separation(IMAGE *img, int sep)
345 {
346     img->devicen[sep].visible = !img->devicen[sep].visible;
347     display_sync(img->handle, img->device);
348 }
349 
signal_sep0(GtkWidget * w,gpointer data)350 static void signal_sep0(GtkWidget *w, gpointer data)
351 {
352     window_separation((IMAGE *)data, 0);
353 }
354 
signal_sep1(GtkWidget * w,gpointer data)355 static void signal_sep1(GtkWidget *w, gpointer data)
356 {
357     window_separation((IMAGE *)data, 1);
358 }
359 
signal_sep2(GtkWidget * w,gpointer data)360 static void signal_sep2(GtkWidget *w, gpointer data)
361 {
362     window_separation((IMAGE *)data, 2);
363 }
364 
signal_sep3(GtkWidget * w,gpointer data)365 static void signal_sep3(GtkWidget *w, gpointer data)
366 {
367     window_separation((IMAGE *)data, 3);
368 }
369 
signal_sep4(GtkWidget * w,gpointer data)370 static void signal_sep4(GtkWidget *w, gpointer data)
371 {
372     window_separation((IMAGE *)data, 4);
373 }
374 
signal_sep5(GtkWidget * w,gpointer data)375 static void signal_sep5(GtkWidget *w, gpointer data)
376 {
377     window_separation((IMAGE *)data, 5);
378 }
379 
signal_sep6(GtkWidget * w,gpointer data)380 static void signal_sep6(GtkWidget *w, gpointer data)
381 {
382     window_separation((IMAGE *)data, 6);
383 }
384 
signal_sep7(GtkWidget * w,gpointer data)385 static void signal_sep7(GtkWidget *w, gpointer data)
386 {
387     window_separation((IMAGE *)data, 7);
388 }
389 
390 GtkSignalFunc signal_separation[IMAGE_DEVICEN_MAX] = {
391     signal_sep0,
392     signal_sep1,
393     signal_sep2,
394     signal_sep3,
395     signal_sep4,
396     signal_sep5,
397     signal_sep6,
398     signal_sep7
399 };
400 
401 static GtkWidget *
window_add_button(IMAGE * img,const char * label,GtkSignalFunc fn)402 window_add_button(IMAGE *img, const char *label, GtkSignalFunc fn)
403 {
404     GtkWidget *w;
405     w = gtk_check_button_new_with_label(label);
406     gtk_box_pack_start(GTK_BOX(img->cmyk_bar), w, FALSE, FALSE, 5);
407     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
408     gtk_signal_connect(GTK_OBJECT(w), "clicked", fn, img);
409     gtk_widget_show(w);
410     return w;
411 }
412 
signal_show_as_gray(GtkWidget * w,gpointer data)413 static void signal_show_as_gray(GtkWidget *w, gpointer data)
414 {
415     IMAGE *img= (IMAGE *)data;
416     img->devicen_gray= !img->devicen_gray;
417     display_sync(img->handle, img->device);
418 }
419 
420 
421 /* New device has been opened */
display_open(void * handle,void * device)422 static int display_open(void *handle, void *device)
423 {
424 
425     IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
426     if (img == NULL)
427 	return -1;
428     memset(img, 0, sizeof(IMAGE));
429 
430     if (first_image == NULL) {
431 	gdk_rgb_init();
432 	gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
433 	gtk_widget_set_default_visual(gdk_rgb_get_visual());
434     }
435 
436     /* add to list */
437     if (first_image)
438 	img->next = first_image;
439     first_image = img;
440 
441     /* remember device and handle */
442     img->handle = handle;
443     img->device = device;
444 
445     /* create window */
446     window_create(img);
447 
448     gtk_main_iteration_do(FALSE);
449     return 0;
450 }
451 
display_preclose(void * handle,void * device)452 static int display_preclose(void *handle, void *device)
453 {
454     IMAGE *img = image_find(handle, device);
455     if (img == NULL)
456 	return -1;
457 
458     gtk_main_iteration_do(FALSE);
459 
460     img->buf = NULL;
461     img->width = 0;
462     img->height = 0;
463     img->rowstride = 0;
464     img->format = 0;
465 
466     gtk_widget_destroy(img->window);
467     img->window = NULL;
468     img->scroll = NULL;
469     img->darea = NULL;
470     if (img->cmap)
471 	gdk_rgb_cmap_free(img->cmap);
472     img->cmap = NULL;
473     if (img->rgbbuf)
474 	free(img->rgbbuf);
475     img->rgbbuf = NULL;
476 
477     gtk_main_iteration_do(FALSE);
478 
479     return 0;
480 }
481 
display_close(void * handle,void * device)482 static int display_close(void *handle, void *device)
483 {
484     IMAGE *img = image_find(handle, device);
485     if (img == NULL)
486 	return -1;
487 
488     /* remove from list */
489     if (img == first_image) {
490 	first_image = img->next;
491     }
492     else {
493 	IMAGE *tmp;
494 	for (tmp = first_image; tmp!=0; tmp=tmp->next) {
495 	    if (img == tmp->next)
496 		tmp->next = img->next;
497 	}
498     }
499 
500     return 0;
501 }
502 
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)503 static int display_presize(void *handle, void *device, int width, int height,
504 	int raster, unsigned int format)
505 {
506     /* Assume everything is OK.
507      * It would be better to return e_rangecheck if we can't
508      * support the format.
509      */
510     return 0;
511 }
512 
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)513 static int display_size(void *handle, void *device, int width, int height,
514 	int raster, unsigned int format, unsigned char *pimage)
515 {
516     IMAGE *img = image_find(handle, device);
517     int color;
518     int depth;
519     int i;
520     if (img == NULL)
521 	return -1;
522 
523     if (img->cmap)
524 	gdk_rgb_cmap_free(img->cmap);
525     img->cmap = NULL;
526     if (img->rgbbuf)
527 	free(img->rgbbuf);
528     img->rgbbuf = NULL;
529 
530     img->width = width;
531     img->height = height;
532     img->rowstride = raster;
533     img->buf = pimage;
534     img->format = format;
535 
536     /* Reset separations */
537     for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
538 	img->devicen[i].used = 0;
539 	img->devicen[i].visible = 1;
540 	memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
541 	img->devicen[i].cyan = 0;
542 	img->devicen[i].magenta = 0;
543 	img->devicen[i].yellow = 0;
544 	img->devicen[i].black = 0;
545     }
546 
547     color = img->format & DISPLAY_COLORS_MASK;
548     depth = img->format & DISPLAY_DEPTH_MASK;
549     switch (color) {
550 	case DISPLAY_COLORS_NATIVE:
551 	    if (depth == DISPLAY_DEPTH_8) {
552 		/* palette of 96 colors */
553 		guint32 color[96];
554 		int i;
555 		int one = 255 / 3;
556 		for (i=0; i<96; i++) {
557 		    /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
558 		    if (i < 64) {
559 			color[i] =
560 			    (((i & 0x30) >> 4) * one << 16) + 	/* r */
561 			    (((i & 0x0c) >> 2) * one << 8) + 	/* g */
562 			    (i & 0x03) * one;		        /* b */
563 		    }
564 		    else {
565 			int val = i & 0x1f;
566 			val = (val << 3) + (val >> 2);
567 			color[i] = (val << 16) + (val << 8) + val;
568 		    }
569 		}
570 		img->cmap = gdk_rgb_cmap_new(color, 96);
571 		break;
572 	    }
573 	    else if (depth == DISPLAY_DEPTH_16) {
574 		/* need to convert to 24RGB */
575 		img->rgbbuf = (guchar *)malloc(width * height * 3);
576 		if (img->rgbbuf == NULL)
577 		    return -1;
578 	    }
579 	    else
580 		return e_rangecheck;	/* not supported */
581 	case DISPLAY_COLORS_GRAY:
582 	    if (depth == DISPLAY_DEPTH_8)
583 		break;
584 	    else
585 		return -1;	/* not supported */
586 	case DISPLAY_COLORS_RGB:
587 	    if (depth == DISPLAY_DEPTH_8) {
588 		if (((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)
589 		    && ((img->format & DISPLAY_ENDIAN_MASK)
590 			== DISPLAY_BIGENDIAN))
591 		    break;
592 		else {
593 		    /* need to convert to 24RGB */
594 		    img->rgbbuf = (guchar *)malloc(width * height * 3);
595 		    if (img->rgbbuf == NULL)
596 			return -1;
597 		}
598 	    }
599 	    else
600 		return -1;	/* not supported */
601 	    break;
602 	case DISPLAY_COLORS_CMYK:
603 	    if ((depth == DISPLAY_DEPTH_1) || (depth == DISPLAY_DEPTH_8)) {
604 		/* need to convert to 24RGB */
605 		img->rgbbuf = (guchar *)malloc(width * height * 3);
606 		if (img->rgbbuf == NULL)
607 		    return -1;
608 		/* We already know about the CMYK components */
609 		img->devicen[0].used = 1;
610 		img->devicen[0].cyan = 65535;
611 		strncpy(img->devicen[0].name, "Cyan",
612 		    sizeof(img->devicen[0].name));
613 		img->devicen[1].used = 1;
614 		img->devicen[1].magenta = 65535;
615 		strncpy(img->devicen[1].name, "Magenta",
616 		    sizeof(img->devicen[1].name));
617 		img->devicen[2].used = 1;
618 		img->devicen[2].yellow = 65535;
619 		strncpy(img->devicen[2].name, "Yellow",
620 		    sizeof(img->devicen[2].name));
621 		img->devicen[3].used = 1;
622 		img->devicen[3].black = 65535;
623 		strncpy(img->devicen[3].name, "Black",
624 		    sizeof(img->devicen[3].name));
625 	    }
626 	    else
627 		return -1;	/* not supported */
628 	    break;
629 	case DISPLAY_COLORS_SEPARATION:
630 	    /* we can't display this natively */
631 	    /* we will convert it just before displaying */
632 	    if (depth != DISPLAY_DEPTH_8)
633 		return -1;	/* not supported */
634 	    img->rgbbuf = (guchar *)malloc(width * height * 3);
635 	    if (img->rgbbuf == NULL)
636 		return -1;
637 	    break;
638     }
639 
640 
641     if ((color == DISPLAY_COLORS_CMYK) ||
642 	(color == DISPLAY_COLORS_SEPARATION)) {
643 	if (!img->cmyk_bar) {
644 	    /* add bar to select separation */
645 	    img->cmyk_bar = gtk_hbox_new(FALSE, 0);
646 	    gtk_box_pack_start(GTK_BOX(img->vbox), img->cmyk_bar,
647 		FALSE, FALSE, 0);
648 	    for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
649 	       img->separation[i] =
650 		window_add_button(img, img->devicen[i].name,
651 		   signal_separation[i]);
652 	    }
653 	    img->show_as_gray = gtk_check_button_new_with_label("Show as Gray");
654 	    gtk_box_pack_end(GTK_BOX(img->cmyk_bar), img->show_as_gray,
655 		FALSE, FALSE, 5);
656 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(img->show_as_gray),
657 		FALSE);
658 	    gtk_signal_connect(GTK_OBJECT(img->show_as_gray), "clicked",
659 		signal_show_as_gray, img);
660 	    gtk_widget_show(img->show_as_gray);
661 	}
662 	gtk_widget_show(img->cmyk_bar);
663     }
664     else {
665 	if (img->cmyk_bar)
666 	    gtk_widget_hide(img->cmyk_bar);
667     }
668 
669     window_resize(img);
670     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE))
671 	gtk_widget_show(img->window);
672 
673     gtk_main_iteration_do(FALSE);
674     return 0;
675 }
676 
display_sync(void * handle,void * device)677 static int display_sync(void *handle, void *device)
678 {
679     IMAGE *img = image_find(handle, device);
680     int color;
681     int depth;
682     int endian;
683     int native555;
684     int alpha;
685     if (img == NULL)
686 	return -1;
687 
688     color = img->format & DISPLAY_COLORS_MASK;
689     depth = img->format & DISPLAY_DEPTH_MASK;
690     endian = img->format & DISPLAY_ENDIAN_MASK;
691     native555 = img->format & DISPLAY_555_MASK;
692     alpha = img->format & DISPLAY_ALPHA_MASK;
693 
694     if ((color == DISPLAY_COLORS_CMYK) ||
695 	(color == DISPLAY_COLORS_SEPARATION)) {
696 	/* check if separations have changed */
697 	int i;
698 	int num_visible = 0;
699 	gchar *str;
700 	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
701 	    gtk_label_get(
702 		GTK_LABEL(GTK_BIN(img->separation[i])->child), &str);
703 	    if (!img->devicen[i].used)
704 		gtk_widget_hide(img->separation[i]);
705 	    else if (strcmp(img->devicen[i].name, str) != 0) {
706 		/* text has changed, update it */
707 		gtk_label_set_text(
708 		    GTK_LABEL(GTK_BIN(img->separation[i])->child),
709 		    img->devicen[i].name);
710 		gtk_widget_show(img->separation[i]);
711 	    }
712 	    if (img->devicen[i].used && img->devicen[i].visible)
713 		num_visible++;
714 	}
715 	if (num_visible <= 1)
716 	    gtk_widget_show(img->separation[i]);
717 	else
718 	    gtk_widget_hide(img->separation[i]);
719     }
720 
721     /* some formats need to be converted for use by GdkRgb */
722     switch (color) {
723 	case DISPLAY_COLORS_NATIVE:
724 	    if (depth == DISPLAY_DEPTH_16) {
725 	      if (endian == DISPLAY_LITTLEENDIAN) {
726 		if (native555 == DISPLAY_NATIVE_555) {
727 		    /* BGR555 */
728 		    int x, y;
729 		    unsigned short w;
730 		    unsigned char value;
731 		    unsigned char *s, *d;
732 		    for (y = 0; y<img->height; y++) {
733 			s = img->buf + y * img->rowstride;
734 			d = img->rgbbuf + y * img->width * 3;
735 			for (x=0; x<img->width; x++) {
736 			    w = s[0] + (s[1] << 8);
737 			    value = (w >> 10) & 0x1f;	/* red */
738 			    *d++ = (value << 3) + (value >> 2);
739 			    value = (w >> 5) & 0x1f;	/* green */
740 			    *d++ = (value << 3) + (value >> 2);
741 			    value = w & 0x1f;		/* blue */
742 			    *d++ = (value << 3) + (value >> 2);
743 			    s += 2;
744 			}
745 		    }
746 		}
747 		else {
748 		    /* BGR565 */
749 		    int x, y;
750 		    unsigned short w;
751 		    unsigned char value;
752 		    unsigned char *s, *d;
753 		    for (y = 0; y<img->height; y++) {
754 			s = img->buf + y * img->rowstride;
755 			d = img->rgbbuf + y * img->width * 3;
756 			for (x=0; x<img->width; x++) {
757 			    w = s[0] + (s[1] << 8);
758 			    value = (w >> 11) & 0x1f;	/* red */
759 			    *d++ = (value << 3) + (value >> 2);
760 			    value = (w >> 5) & 0x3f;	/* green */
761 			    *d++ = (value << 2) + (value >> 4);
762 			    value = w & 0x1f;		/* blue */
763 			    *d++ = (value << 3) + (value >> 2);
764 			    s += 2;
765 			}
766 		    }
767 		}
768 	      }
769 	      else {
770 		if (native555 == DISPLAY_NATIVE_555) {
771 		    /* RGB555 */
772 		    int x, y;
773 		    unsigned short w;
774 		    unsigned char value;
775 		    unsigned char *s, *d;
776 		    for (y = 0; y<img->height; y++) {
777 			s = img->buf + y * img->rowstride;
778 			d = img->rgbbuf + y * img->width * 3;
779 			for (x=0; x<img->width; x++) {
780 			    w = s[1] + (s[0] << 8);
781 			    value = (w >> 10) & 0x1f;	/* red */
782 			    *d++ = (value << 3) + (value >> 2);
783 			    value = (w >> 5) & 0x1f;	/* green */
784 			    *d++ = (value << 3) + (value >> 2);
785 			    value = w & 0x1f;		/* blue */
786 			    *d++ = (value << 3) + (value >> 2);
787 			    s += 2;
788 			}
789 		    }
790 		}
791 		else {
792 		    /* RGB565 */
793 		    int x, y;
794 		    unsigned short w;
795 		    unsigned char value;
796 		    unsigned char *s, *d;
797 		    for (y = 0; y<img->height; y++) {
798 			s = img->buf + y * img->rowstride;
799 			d = img->rgbbuf + y * img->width * 3;
800 			for (x=0; x<img->width; x++) {
801 			    w = s[1] + (s[0] << 8);
802 			    value = (w >> 11) & 0x1f;	/* red */
803 			    *d++ = (value << 3) + (value >> 2);
804 			    value = (w >> 5) & 0x3f;	/* green */
805 			    *d++ = (value << 2) + (value >> 4);
806 			    value = w & 0x1f;		/* blue */
807 			    *d++ = (value << 3) + (value >> 2);
808 			    s += 2;
809 			}
810 		    }
811 		}
812 	      }
813 	    }
814 	    break;
815 	case DISPLAY_COLORS_RGB:
816 	    if ( (depth == DISPLAY_DEPTH_8) &&
817 		 ((alpha == DISPLAY_ALPHA_FIRST) ||
818 	          (alpha == DISPLAY_UNUSED_FIRST)) &&
819 		 (endian == DISPLAY_BIGENDIAN) ) {
820 		/* Mac format */
821 		int x, y;
822 		unsigned char *s, *d;
823 		for (y = 0; y<img->height; y++) {
824 		    s = img->buf + y * img->rowstride;
825 		    d = img->rgbbuf + y * img->width * 3;
826 		    for (x=0; x<img->width; x++) {
827 			s++;		/* x = filler */
828 			*d++ = *s++;	/* r */
829 			*d++ = *s++;	/* g */
830 			*d++ = *s++;	/* b */
831 		    }
832 		}
833 	    }
834 	    else if ( (depth == DISPLAY_DEPTH_8) &&
835 		      (endian == DISPLAY_LITTLEENDIAN) ) {
836 	        if ((alpha == DISPLAY_UNUSED_LAST) ||
837 	            (alpha == DISPLAY_ALPHA_LAST)) {
838 		    /* Windows format + alpha = BGRx */
839 		    int x, y;
840 		    unsigned char *s, *d;
841 		    for (y = 0; y<img->height; y++) {
842 			s = img->buf + y * img->rowstride;
843 			d = img->rgbbuf + y * img->width * 3;
844 			for (x=0; x<img->width; x++) {
845 			    *d++ = s[2];	/* r */
846 			    *d++ = s[1];	/* g */
847 			    *d++ = s[0];	/* b */
848 			    s += 4;
849 			}
850 		    }
851 		}
852 	        else if ((alpha == DISPLAY_UNUSED_FIRST) ||
853 	            (alpha == DISPLAY_ALPHA_FIRST)) {
854 		    /* xBGR */
855 		    int x, y;
856 		    unsigned char *s, *d;
857 		    for (y = 0; y<img->height; y++) {
858 			s = img->buf + y * img->rowstride;
859 			d = img->rgbbuf + y * img->width * 3;
860 			for (x=0; x<img->width; x++) {
861 			    *d++ = s[3];	/* r */
862 			    *d++ = s[2];	/* g */
863 			    *d++ = s[1];	/* b */
864 			    s += 4;
865 			}
866 		    }
867 		}
868 		else {
869 		    /* Windows BGR24 */
870 		    int x, y;
871 		    unsigned char *s, *d;
872 		    for (y = 0; y<img->height; y++) {
873 			s = img->buf + y * img->rowstride;
874 			d = img->rgbbuf + y * img->width * 3;
875 			for (x=0; x<img->width; x++) {
876 			    *d++ = s[2];	/* r */
877 			    *d++ = s[1];	/* g */
878 			    *d++ = s[0];	/* b */
879 			    s += 3;
880 			}
881 		    }
882 		}
883 	    }
884 	    break;
885 	case DISPLAY_COLORS_CMYK:
886 	    if (depth == DISPLAY_DEPTH_8) {
887 	    	/* Separations */
888 		int x, y;
889 		int cyan, magenta, yellow, black;
890 		unsigned char *s, *d;
891 		int vc = img->devicen[0].visible;
892 		int vm = img->devicen[1].visible;
893 		int vy = img->devicen[2].visible;
894 		int vk = img->devicen[3].visible;
895 		int vall = vc && vm && vy && vk;
896 		int show_gray = (vc + vm + vy + vk == 1) && img->devicen_gray;
897 		for (y = 0; y<img->height; y++) {
898 		    s = img->buf + y * img->rowstride;
899 		    d = img->rgbbuf + y * img->width * 3;
900 		    for (x=0; x<img->width; x++) {
901 			cyan = *s++;
902 			magenta = *s++;
903 			yellow = *s++;
904 			black = *s++;
905 			if (!vall) {
906 			    if (!vc)
907 				cyan = 0;
908 			    if (!vm)
909 				magenta = 0;
910 			    if (!vy)
911 				yellow = 0;
912 			    if (!vk)
913 				black = 0;
914 			    if (show_gray) {
915 				black += cyan + magenta + yellow;
916 				cyan = magenta = yellow = 0;
917 			    }
918 			}
919 			*d++ = (255-cyan)    * (255-black) / 255; /* r */
920 			*d++ = (255-magenta) * (255-black) / 255; /* g */
921 			*d++ = (255-yellow)  * (255-black) / 255; /* b */
922 		    }
923 		}
924 	    }
925 	    else if (depth == DISPLAY_DEPTH_1) {
926 	    	/* Separations */
927 		int x, y;
928 		int cyan, magenta, yellow, black;
929 		unsigned char *s, *d;
930 		int vc = img->devicen[0].visible;
931 		int vm = img->devicen[1].visible;
932 		int vy = img->devicen[2].visible;
933 		int vk = img->devicen[3].visible;
934 		int vall = vc && vm && vy && vk;
935 		int show_gray = (vc + vm + vy + vk == 1) && img->devicen_gray;
936 		int value;
937 		for (y = 0; y<img->height; y++) {
938 		    s = img->buf + y * img->rowstride;
939 		    d = img->rgbbuf + y * img->width * 3;
940 		    for (x=0; x<img->width; x++) {
941 			value = s[x/2];
942 			if (x & 0)
943 			    value >>= 4;
944 			cyan = ((value >> 3) & 1) * 255;
945 			magenta = ((value >> 2) & 1) * 255;
946 			yellow = ((value >> 1) & 1) * 255;
947 			black = (value & 1) * 255;
948 			if (!vall) {
949 			    if (!vc)
950 				cyan = 0;
951 			    if (!vm)
952 				magenta = 0;
953 			    if (!vy)
954 				yellow = 0;
955 			    if (!vk)
956 				black = 0;
957 			    if (show_gray) {
958 				black += cyan + magenta + yellow;
959 				cyan = magenta = yellow = 0;
960 			    }
961 			}
962 			*d++ = (255-cyan)    * (255-black) / 255; /* r */
963 			*d++ = (255-magenta) * (255-black) / 255; /* g */
964 			*d++ = (255-yellow)  * (255-black) / 255; /* b */
965 		    }
966 		}
967 	    }
968 	    break;
969 	case DISPLAY_COLORS_SEPARATION:
970 	    if (depth == DISPLAY_DEPTH_8) {
971 		int j;
972 		int x, y;
973 		unsigned char *s, *d;
974 		int cyan, magenta, yellow, black;
975 		int num_comp = 0;
976 		int value;
977 		int num_visible = 0;
978 		int show_gray = 0;
979 	        IMAGE_DEVICEN *devicen = img->devicen;
980 		for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
981 		    if (img->devicen[j].used) {
982 		       num_comp = j+1;
983 		       if (img->devicen[j].visible)
984 			    num_visible++;
985 		    }
986 		}
987 		if ((num_visible == 1) && img->devicen_gray)
988 		    show_gray = 1;
989 
990 		for (y = 0; y<img->height; y++) {
991 		    s = img->buf + y * img->rowstride;
992 		    d = img->rgbbuf + y * img->width * 3;
993 		    for (x=0; x<img->width; x++) {
994 			cyan = magenta = yellow = black = 0;
995 			if (show_gray) {
996 			    for (j=0; j<num_comp; j++) {
997 				devicen = &img->devicen[j];
998 				if (devicen->visible && devicen->used)
999 				    black += s[j];
1000 			    }
1001 			}
1002 			else {
1003 			    for (j=0; j<num_comp; j++) {
1004 				devicen = &img->devicen[j];
1005 				if (devicen->visible && devicen->used) {
1006 				    value = s[j];
1007 				    cyan    += value*devicen->cyan   /65535;
1008 				    magenta += value*devicen->magenta/65535;
1009 				    yellow  += value*devicen->yellow /65535;
1010 				    black   += value*devicen->black  /65535;
1011 				}
1012 			    }
1013 			}
1014 			if (cyan > 255)
1015 			   cyan = 255;
1016 			if (magenta > 255)
1017 			   magenta = 255;
1018 			if (yellow > 255)
1019 			   yellow = 255;
1020 			if (black > 255)
1021 			   black = 255;
1022 			*d++ = (255-cyan)    * (255-black) / 255; /* r */
1023 			*d++ = (255-magenta) * (255-black) / 255; /* g */
1024 			*d++ = (255-yellow)  * (255-black) / 255; /* b */
1025 			s += 8;
1026 		    }
1027 		}
1028 	    }
1029 	    break;
1030     }
1031 
1032     if (img->window == NULL) {
1033 	window_create(img);
1034 	window_resize(img);
1035     }
1036     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE))
1037 	gtk_widget_show_all(img->window);
1038 
1039     gtk_widget_draw(img->darea, NULL);
1040     gtk_main_iteration_do(FALSE);
1041     return 0;
1042 }
1043 
display_page(void * handle,void * device,int copies,int flush)1044 static int display_page(void *handle, void *device, int copies, int flush)
1045 {
1046     display_sync(handle, device);
1047     return 0;
1048 }
1049 
display_update(void * handle,void * device,int x,int y,int w,int h)1050 static int display_update(void *handle, void *device,
1051     int x, int y, int w, int h)
1052 {
1053     /* not implemented - eventually this will be used for progressive update */
1054     return 0;
1055 }
1056 
1057 
1058 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)1059 display_separation(void *handle, void *device,
1060     int comp_num, const char *name,
1061     unsigned short c, unsigned short m,
1062     unsigned short y, unsigned short k)
1063 {
1064     IMAGE *img = image_find(handle, device);
1065     if (img == NULL)
1066 	return -1;
1067     if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
1068 	return -1;
1069     img->devicen[comp_num].used = 1;
1070     strncpy(img->devicen[comp_num].name, name,
1071 	sizeof(img->devicen[comp_num].name)-1);
1072     img->devicen[comp_num].cyan    = c;
1073     img->devicen[comp_num].magenta = m;
1074     img->devicen[comp_num].yellow  = y;
1075     img->devicen[comp_num].black   = k;
1076     return 0;
1077 }
1078 
1079 
1080 /* callback structure for "display" device */
1081 display_callback display = {
1082     sizeof(display_callback),
1083     DISPLAY_VERSION_MAJOR,
1084     DISPLAY_VERSION_MINOR,
1085     display_open,
1086     display_preclose,
1087     display_close,
1088     display_presize,
1089     display_size,
1090     display_sync,
1091     display_page,
1092     display_update,
1093     NULL,	/* memalloc */
1094     NULL,	/* memfree */
1095     display_separation
1096 };
1097 
1098 /*********************************************************************/
1099 
main(int argc,char * argv[])1100 int main(int argc, char *argv[])
1101 {
1102     int exit_status;
1103     int code = 1, code1;
1104     void *instance;
1105     int nargc;
1106     char **nargv;
1107     char dformat[64];
1108     int exit_code;
1109     gboolean use_gui;
1110 
1111     /* Gtk initialisation */
1112     gtk_set_locale();
1113     use_gui = gtk_init_check(&argc, &argv);
1114 
1115     /* insert display device parameters as first arguments */
1116     sprintf(dformat, "-dDisplayFormat=%d",
1117  	    DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
1118 	    DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST);
1119     nargc = argc + 1;
1120     nargv = (char **)malloc((nargc + 1) * sizeof(char *));
1121     nargv[0] = argv[0];
1122     nargv[1] = dformat;
1123     memcpy(&nargv[2], &argv[1], argc * sizeof(char *));
1124 
1125     /* run Ghostscript */
1126     if ((code = gsapi_new_instance(&instance, NULL)) == 0) {
1127         gsapi_set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
1128 	if (use_gui)
1129             gsapi_set_display_callback(instance, &display);
1130 	code = gsapi_init_with_args(instance, nargc, nargv);
1131 
1132 	if (code == 0)
1133 	    code = gsapi_run_string(instance, start_string, 0, &exit_code);
1134         code1 = gsapi_exit(instance);
1135 	if (code == 0 || code == e_Quit)
1136 	    code = code1;
1137 	if (code == e_Quit)
1138 	    code = 0;	/* user executed 'quit' */
1139 
1140 	gsapi_delete_instance(instance);
1141     }
1142 
1143     exit_status = 0;
1144     switch (code) {
1145 	case 0:
1146 	case e_Info:
1147 	case e_Quit:
1148 	    break;
1149 	case e_Fatal:
1150 	    exit_status = 1;
1151 	    break;
1152 	default:
1153 	    exit_status = 255;
1154     }
1155 
1156     return exit_status;
1157 }
1158 
1159