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