1 /* Copyright (C) 2001 Ghostgum Software Pty Ltd.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /* $Id: dxmain.c,v 1.7.2.2.2.1 2003/01/17 00:49:00 giles Exp $ */
20 
21 /* dxmain.c */
22 /*
23  * Ghostscript frontend which provides a graphical window
24  * using Gtk+.  Load time linking to libgs.so
25  * Compile using
26  *    gcc `gtk-config --cflags` -o gs dxmain.c -lgs `gtk-config --libs`
27  *
28  * The ghostscript library needs to be compiled with
29  *  gcc -fPIC -g -c -Wall file.c
30  *  gcc -shared -Wl,-soname,libgs.so.6 -o libgs.so.6.60 file.o -lc
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <gtk/gtk.h>
39 #define __PROTOTYPES__
40 #include "errors.h"
41 #include "iapi.h"
42 #include "gdevdsp.h"
43 
44 const char start_string[] = "systemdict /start get exec\n";
45 
46 static void read_stdin_handler(gpointer data, gint fd,
47 	GdkInputCondition condition);
48 static int gsdll_stdin(void *instance, char *buf, int len);
49 static int gsdll_stdout(void *instance, const char *str, int len);
50 static int gsdll_stdout(void *instance, const char *str, int len);
51 static int display_open(void *handle, void *device);
52 static int display_preclose(void *handle, void *device);
53 static int display_close(void *handle, void *device);
54 static int display_presize(void *handle, void *device, int width, int height,
55 	int raster, unsigned int format);
56 static int display_size(void *handle, void *device, int width, int height,
57 	int raster, unsigned int format, unsigned char *pimage);
58 static int display_sync(void *handle, void *device);
59 static int display_page(void *handle, void *device, int copies, int flush);
60 static int display_update(void *handle, void *device, int x, int y,
61 	int w, int h);
62 
63 enum SEPARATIONS {
64     SEP_CYAN = 8,
65     SEP_MAGENTA = 4,
66     SEP_YELLOW = 2,
67     SEP_BLACK = 1
68 };
69 
70 #ifndef min
71 #define min(a,b) ((a) < (b) ? (a) : (b))
72 #endif
73 
74 /*********************************************************************/
75 /* stdio functions */
76 
77 struct stdin_buf {
78    char *buf;
79    int len;	/* length of buffer */
80    int count;	/* number of characters returned */
81 };
82 
83 /* handler for reading non-blocking stdin */
84 static void
read_stdin_handler(gpointer data,gint fd,GdkInputCondition condition)85 read_stdin_handler(gpointer data, gint fd, GdkInputCondition condition)
86 {
87     struct stdin_buf *input = (struct stdin_buf *)data;
88 
89     if (condition & GDK_INPUT_EXCEPTION) {
90 	g_print("input exception");
91 	input->count = 0;	/* EOF */
92     }
93     else if (condition & GDK_INPUT_READ) {
94 	/* read returns -1 for error, 0 for EOF and +ve for OK */
95 	input->count = read(fd, input->buf, input->len);
96 	if (input->count < 0)
97 	    input->count = 0;
98     }
99     else {
100 	g_print("input condition unknown");
101 	input->count = 0;	/* EOF */
102     }
103 }
104 
105 /* callback for reading stdin */
106 static int
gsdll_stdin(void * instance,char * buf,int len)107 gsdll_stdin(void *instance, char *buf, int len)
108 {
109     struct stdin_buf input;
110     gint input_tag;
111 
112     input.len = len;
113     input.buf = buf;
114     input.count = -1;
115 
116     input_tag = gdk_input_add(fileno(stdin),
117 	(GdkInputCondition)(GDK_INPUT_READ | GDK_INPUT_EXCEPTION),
118 	read_stdin_handler, &input);
119     while (input.count < 0)
120 	gtk_main_iteration_do(TRUE);
121     gdk_input_remove(input_tag);
122 
123     return input.count;
124 }
125 
126 static int
gsdll_stdout(void * instance,const char * str,int len)127 gsdll_stdout(void *instance, const char *str, int len)
128 {
129     gtk_main_iteration_do(FALSE);
130     fwrite(str, 1, len, stdout);
131     fflush(stdout);
132     return len;
133 }
134 
135 static int
gsdll_stderr(void * instance,const char * str,int len)136 gsdll_stderr(void *instance, const char *str, int len)
137 {
138     gtk_main_iteration_do(FALSE);
139     fwrite(str, 1, len, stderr);
140     fflush(stderr);
141     return len;
142 }
143 
144 /*********************************************************************/
145 /* dll display device */
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 *scroll;
155     GtkWidget *darea;
156     guchar *buf;
157     gint width;
158     gint height;
159     gint rowstride;
160     unsigned int format;
161     GdkRgbCmap *cmap;
162     int separation;	/* for displaying C or M or Y or K */
163     guchar *rgbbuf;	/* used when we need to convert raster format */
164     IMAGE *next;
165 };
166 
167 IMAGE *first_image;
168 static IMAGE *image_find(void *handle, void *device);
169 static void window_destroy(GtkWidget *w, gpointer data);
170 static void window_create(IMAGE *img);
171 static void window_resize(IMAGE *img);
172 static gboolean window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
173 
174 static IMAGE *
image_find(void * handle,void * device)175 image_find(void *handle, void *device)
176 {
177     IMAGE *img;
178     for (img = first_image; img!=0; img=img->next) {
179 	if ((img->handle == handle) && (img->device == device))
180 	    return img;
181     }
182     return NULL;
183 }
184 
185 
186 
187 static gboolean
window_draw(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)188 window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
189 {
190     IMAGE *img = (IMAGE *)user_data;
191     if (img && img->window && img->buf) {
192         int color = img->format & DISPLAY_COLORS_MASK;
193 	int depth = img->format & DISPLAY_DEPTH_MASK;
194 	int x, y, width, height;
195 	if (event->area.x + event->area.width > img->width) {
196 	    x = img->width;
197 	    width = (event->area.x + event->area.width) - x;
198 	    y = event->area.y;
199             height = min(img->height, event->area.y + event->area.height) - y;
200 	    gdk_window_clear_area(widget->window, x, y, width, height);
201 	}
202 	if (event->area.y + event->area.height > img->height) {
203 	    x = event->area.x;
204 	    width = event->area.width;
205 	    y = img->height;
206 	    height = (event->area.y + event->area.height) - y;
207 	    gdk_window_clear_area(widget->window, x, y, width, height);
208 	}
209 	x = event->area.x;
210 	y = event->area.y;
211 	width = event->area.width;
212 	height = event->area.height;
213 	if ((x>=0) && (y>=0) && (x < img->width) && (y < img->height)) {
214 	    /* drawing area intersects bitmap */
215 	    if (x + width > img->width)
216 		width = img->width - x;
217 	    if (y + height > img->height)
218 		height =  img->height - y;
219 	    switch (color) {
220 		case DISPLAY_COLORS_NATIVE:
221 		    if (depth == DISPLAY_DEPTH_8)
222 			gdk_draw_indexed_image(widget->window,
223 			    widget->style->fg_gc[GTK_STATE_NORMAL],
224 			    x, y, width, height,
225 			    GDK_RGB_DITHER_MAX,
226 			    img->buf + x + y*img->rowstride,
227 			    img->rowstride, img->cmap);
228 		    else if ((depth == DISPLAY_DEPTH_16) && img->rgbbuf)
229 			gdk_draw_rgb_image(widget->window,
230 			    widget->style->fg_gc[GTK_STATE_NORMAL],
231 			    x, y, width, height,
232 			    GDK_RGB_DITHER_MAX,
233 			    img->rgbbuf + x*3 + y*img->width*3,
234 			    img->width * 3);
235 		    break;
236 		case DISPLAY_COLORS_GRAY:
237 		    if (depth == DISPLAY_DEPTH_8)
238 			gdk_draw_gray_image(widget->window,
239 			    widget->style->fg_gc[GTK_STATE_NORMAL],
240 			    x, y, width, height,
241 			    GDK_RGB_DITHER_MAX,
242 			    img->buf + x + y*img->rowstride,
243 			    img->rowstride);
244 		    break;
245 		case DISPLAY_COLORS_RGB:
246 		    if (depth == DISPLAY_DEPTH_8) {
247 			if (img->rgbbuf)
248 			    gdk_draw_rgb_image(widget->window,
249 				widget->style->fg_gc[GTK_STATE_NORMAL],
250 				x, y, width, height,
251 				GDK_RGB_DITHER_MAX,
252 				img->rgbbuf + x*3 + y*img->width*3,
253 				img->width * 3);
254 			else
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->buf + x*3 + y*img->rowstride,
260 				img->rowstride);
261 		    }
262 		    break;
263 		case DISPLAY_COLORS_CMYK:
264 		    if ((depth == DISPLAY_DEPTH_8) && img->rgbbuf)
265 			gdk_draw_rgb_image(widget->window,
266 			    widget->style->fg_gc[GTK_STATE_NORMAL],
267 			    x, y, width, height,
268 			    GDK_RGB_DITHER_MAX,
269 			    img->rgbbuf + x*3 + y*img->width*3,
270 			    img->width * 3);
271 		    break;
272 	    }
273 	}
274     }
275     return TRUE;
276 }
277 
window_destroy(GtkWidget * w,gpointer data)278 static void window_destroy(GtkWidget *w, gpointer data)
279 {
280     IMAGE *img = (IMAGE *)data;
281     img->window = NULL;
282     img->scroll = NULL;
283     img->darea = NULL;
284 }
285 
window_create(IMAGE * img)286 static void window_create(IMAGE *img)
287 {
288     /* Create a gtk window */
289     img->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
290     gtk_window_set_title(GTK_WINDOW(img->window), "gs");
291     img->vbox = gtk_vbox_new(FALSE, 0);
292     gtk_container_add(GTK_CONTAINER(img->window), img->vbox);
293     gtk_widget_show(img->vbox);
294 
295     img->darea = gtk_drawing_area_new();
296     gtk_widget_show(img->darea);
297     img->scroll = gtk_scrolled_window_new(NULL, NULL);
298     gtk_widget_show(img->scroll);
299     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(img->scroll),
300 	GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
301     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(img->scroll),
302 	img->darea);
303     gtk_box_pack_start(GTK_BOX(img->vbox), img->scroll, TRUE, TRUE, 0);
304     gtk_signal_connect(GTK_OBJECT (img->darea), "expose-event",
305                         GTK_SIGNAL_FUNC (window_draw), img);
306     gtk_signal_connect(GTK_OBJECT (img->window), "destroy",
307 			GTK_SIGNAL_FUNC (window_destroy), img);
308     /* do not show img->window until we know the image size */
309 }
310 
window_resize(IMAGE * img)311 static void window_resize(IMAGE *img)
312 {
313     gtk_drawing_area_size(GTK_DRAWING_AREA (img->darea),
314 	img->width, img->height);
315     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE)) {
316 	/* We haven't yet shown the window, so set a default size
317 	 * which is smaller than the desktop to allow room for
318 	 * desktop toolbars, and if possible a little larger than
319 	 * the image to allow room for the scroll bars.
320 	 * We don't know the width of the scroll bars, so just guess. */
321 	gtk_window_set_default_size(GTK_WINDOW(img->window),
322 	    min(gdk_screen_width()-96, img->width+24),
323 	    min(gdk_screen_height()-96, img->height+24));
324     }
325 }
326 
window_separation(IMAGE * img,int layer)327 static void window_separation(IMAGE *img, int layer)
328 {
329     img->separation ^= layer;
330     display_sync(img->handle, img->device);
331 }
332 
cmyk_cyan(GtkWidget * w,gpointer data)333 static void cmyk_cyan(GtkWidget *w, gpointer data)
334 {
335     window_separation((IMAGE *)data, SEP_CYAN);
336 }
337 
cmyk_magenta(GtkWidget * w,gpointer data)338 static void cmyk_magenta(GtkWidget *w, gpointer data)
339 {
340     window_separation((IMAGE *)data, SEP_MAGENTA);
341 }
342 
cmyk_yellow(GtkWidget * w,gpointer data)343 static void cmyk_yellow(GtkWidget *w, gpointer data)
344 {
345     window_separation((IMAGE *)data, SEP_YELLOW);
346 }
347 
cmyk_black(GtkWidget * w,gpointer data)348 static void cmyk_black(GtkWidget *w, gpointer data)
349 {
350     window_separation((IMAGE *)data, SEP_BLACK);
351 }
352 
353 static void
window_add_button(IMAGE * img,const char * label,GtkSignalFunc fn)354 window_add_button(IMAGE *img, const char *label, GtkSignalFunc fn)
355 {
356     GtkWidget *w;
357     w = gtk_check_button_new_with_label(label);
358     gtk_box_pack_start(GTK_BOX(img->cmyk_bar), w, TRUE, FALSE, 5);
359     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
360     gtk_signal_connect(GTK_OBJECT(w), "clicked", fn, img);
361     gtk_widget_show(w);
362 }
363 
364 /* New device has been opened */
display_open(void * handle,void * device)365 static int display_open(void *handle, void *device)
366 {
367 
368     IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
369     if (img == NULL)
370 	return -1;
371     memset(img, 0, sizeof(IMAGE));
372 
373     if (first_image == NULL) {
374 	gdk_rgb_init();
375 	gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
376 	gtk_widget_set_default_visual(gdk_rgb_get_visual());
377     }
378 
379     /* add to list */
380     if (first_image)
381 	img->next = first_image;
382     first_image = img;
383 
384     /* remember device and handle */
385     img->handle = handle;
386     img->device = device;
387 
388     /* create window */
389     window_create(img);
390 
391     gtk_main_iteration_do(FALSE);
392     return 0;
393 }
394 
display_preclose(void * handle,void * device)395 static int display_preclose(void *handle, void *device)
396 {
397     IMAGE *img = image_find(handle, device);
398     if (img == NULL)
399 	return -1;
400 
401     gtk_main_iteration_do(FALSE);
402 
403     img->buf = NULL;
404     img->width = 0;
405     img->height = 0;
406     img->rowstride = 0;
407     img->format = 0;
408 
409     gtk_widget_destroy(img->window);
410     img->window = NULL;
411     img->scroll = NULL;
412     img->darea = NULL;
413     if (img->cmap)
414 	gdk_rgb_cmap_free(img->cmap);
415     img->cmap = NULL;
416     if (img->rgbbuf)
417 	free(img->rgbbuf);
418     img->rgbbuf = NULL;
419 
420     gtk_main_iteration_do(FALSE);
421 
422     return 0;
423 }
424 
display_close(void * handle,void * device)425 static int display_close(void *handle, void *device)
426 {
427     IMAGE *img = image_find(handle, device);
428     if (img == NULL)
429 	return -1;
430 
431     /* remove from list */
432     if (img == first_image) {
433 	first_image = img->next;
434     }
435     else {
436 	IMAGE *tmp;
437 	for (tmp = first_image; tmp!=0; tmp=tmp->next) {
438 	    if (img == tmp->next)
439 		tmp->next = img->next;
440 	}
441     }
442 
443     return 0;
444 }
445 
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)446 static int display_presize(void *handle, void *device, int width, int height,
447 	int raster, unsigned int format)
448 {
449     /* Assume everything is OK.
450      * It would be better to return e_rangecheck if we can't
451      * support the format.
452      */
453     return 0;
454 }
455 
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)456 static int display_size(void *handle, void *device, int width, int height,
457 	int raster, unsigned int format, unsigned char *pimage)
458 {
459     IMAGE *img = image_find(handle, device);
460     int color;
461     int depth;
462     if (img == NULL)
463 	return -1;
464 
465     if (img->cmap)
466 	gdk_rgb_cmap_free(img->cmap);
467     img->cmap = NULL;
468     if (img->rgbbuf)
469 	free(img->rgbbuf);
470     img->rgbbuf = NULL;
471 
472     img->width = width;
473     img->height = height;
474     img->rowstride = raster;
475     img->buf = pimage;
476     img->format = format;
477 
478     color = img->format & DISPLAY_COLORS_MASK;
479     depth = img->format & DISPLAY_DEPTH_MASK;
480     switch (color) {
481 	case DISPLAY_COLORS_NATIVE:
482 	    if (depth == DISPLAY_DEPTH_8) {
483 		/* palette of 96 colors */
484 		guint32 color[96];
485 		int i;
486 		int one = 255 / 3;
487 		for (i=0; i<96; i++) {
488 		    /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
489 		    if (i < 64) {
490 			color[i] =
491 			    (((i & 0x30) >> 4) * one << 16) + 	/* r */
492 			    (((i & 0x0c) >> 2) * one << 8) + 	/* g */
493 			    (i & 0x03) * one;		        /* b */
494 		    }
495 		    else {
496 			int val = i & 0x1f;
497 			val = (val << 3) + (val >> 2);
498 			color[i] = (val << 16) + (val << 8) + val;
499 		    }
500 		}
501 		img->cmap = gdk_rgb_cmap_new(color, 96);
502 		break;
503 	    }
504 	    else if (depth == DISPLAY_DEPTH_16) {
505 		/* need to convert to 24RGB */
506 		img->rgbbuf = (guchar *)malloc(width * height * 3);
507 		if (img->rgbbuf == NULL)
508 		    return -1;
509 	    }
510 	    else
511 		return e_rangecheck;	/* not supported */
512 	case DISPLAY_COLORS_GRAY:
513 	    if (depth == DISPLAY_DEPTH_8)
514 		break;
515 	    else
516 		return -1;	/* not supported */
517 	case DISPLAY_COLORS_RGB:
518 	    if (depth == DISPLAY_DEPTH_8) {
519 		if (((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)
520 		    && ((img->format & DISPLAY_ENDIAN_MASK)
521 			== DISPLAY_BIGENDIAN))
522 		    break;
523 		else {
524 		    /* need to convert to 24RGB */
525 		    img->rgbbuf = (guchar *)malloc(width * height * 3);
526 		    if (img->rgbbuf == NULL)
527 			return -1;
528 		}
529 	    }
530 	    else
531 		return -1;	/* not supported */
532 	    break;
533 	case DISPLAY_COLORS_CMYK:
534 	    if (depth == DISPLAY_DEPTH_8) {
535 		/* need to convert to 24RGB */
536 		img->rgbbuf = (guchar *)malloc(width * height * 3);
537 		if (img->rgbbuf == NULL)
538 		    return -1;
539 	    }
540 	    else
541 		return -1;	/* not supported */
542 	    break;
543     }
544 
545 
546     if (color == DISPLAY_COLORS_CMYK) {
547 	if (!img->cmyk_bar) {
548 	    /* add bar to select separation */
549 	    img->cmyk_bar = gtk_hbox_new(TRUE, 0);
550 	    gtk_box_pack_start(GTK_BOX(img->vbox), img->cmyk_bar,
551 		FALSE, FALSE, 0);
552 	    img->separation = 0xf;	/* all layers */
553 	    window_add_button(img, "Cyan", (GtkSignalFunc)cmyk_cyan);
554 	    window_add_button(img, "Magenta", (GtkSignalFunc)cmyk_magenta);
555 	    window_add_button(img, "Yellow", (GtkSignalFunc)cmyk_yellow);
556 	    window_add_button(img, "Black", (GtkSignalFunc)cmyk_black);
557 	}
558 	gtk_widget_show(img->cmyk_bar);
559     }
560     else {
561 	if (img->cmyk_bar)
562 	    gtk_widget_hide(img->cmyk_bar);
563     }
564 
565     window_resize(img);
566     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE))
567 	gtk_widget_show(img->window);
568 
569     gtk_main_iteration_do(FALSE);
570     return 0;
571 }
572 
display_sync(void * handle,void * device)573 static int display_sync(void *handle, void *device)
574 {
575     IMAGE *img = image_find(handle, device);
576     int color;
577     int depth;
578     int endian;
579     int native555;
580     int alpha;
581     if (img == NULL)
582 	return -1;
583 
584     color = img->format & DISPLAY_COLORS_MASK;
585     depth = img->format & DISPLAY_DEPTH_MASK;
586     endian = img->format & DISPLAY_ENDIAN_MASK;
587     native555 = img->format & DISPLAY_555_MASK;
588     alpha = img->format & DISPLAY_ALPHA_MASK;
589 
590     /* some formats need to be converted for use by GdkRgb */
591     switch (color) {
592 	case DISPLAY_COLORS_NATIVE:
593 	    if (depth == DISPLAY_DEPTH_16) {
594 	      if (endian == DISPLAY_LITTLEENDIAN) {
595 		if (native555 == DISPLAY_NATIVE_555) {
596 		    /* BGR555 */
597 		    int x, y;
598 		    unsigned short w;
599 		    unsigned char value;
600 		    unsigned char *s, *d;
601 		    for (y = 0; y<img->height; y++) {
602 			s = img->buf + y * img->rowstride;
603 			d = img->rgbbuf + y * img->width * 3;
604 			for (x=0; x<img->width; x++) {
605 			    w = s[0] + (s[1] << 8);
606 			    value = (w >> 10) & 0x1f;	/* red */
607 			    *d++ = (value << 3) + (value >> 2);
608 			    value = (w >> 5) & 0x1f;	/* green */
609 			    *d++ = (value << 3) + (value >> 2);
610 			    value = w & 0x1f;		/* blue */
611 			    *d++ = (value << 3) + (value >> 2);
612 			    s += 2;
613 			}
614 		    }
615 		}
616 		else {
617 		    /* BGR565 */
618 		    int x, y;
619 		    unsigned short w;
620 		    unsigned char value;
621 		    unsigned char *s, *d;
622 		    for (y = 0; y<img->height; y++) {
623 			s = img->buf + y * img->rowstride;
624 			d = img->rgbbuf + y * img->width * 3;
625 			for (x=0; x<img->width; x++) {
626 			    w = s[0] + (s[1] << 8);
627 			    value = (w >> 11) & 0x1f;	/* red */
628 			    *d++ = (value << 3) + (value >> 2);
629 			    value = (w >> 5) & 0x3f;	/* green */
630 			    *d++ = (value << 2) + (value >> 4);
631 			    value = w & 0x1f;		/* blue */
632 			    *d++ = (value << 3) + (value >> 2);
633 			    s += 2;
634 			}
635 		    }
636 		}
637 	      }
638 	      else {
639 		if (native555 == DISPLAY_NATIVE_555) {
640 		    /* RGB555 */
641 		    int x, y;
642 		    unsigned short w;
643 		    unsigned char value;
644 		    unsigned char *s, *d;
645 		    for (y = 0; y<img->height; y++) {
646 			s = img->buf + y * img->rowstride;
647 			d = img->rgbbuf + y * img->width * 3;
648 			for (x=0; x<img->width; x++) {
649 			    w = s[1] + (s[0] << 8);
650 			    value = (w >> 10) & 0x1f;	/* red */
651 			    *d++ = (value << 3) + (value >> 2);
652 			    value = (w >> 5) & 0x1f;	/* green */
653 			    *d++ = (value << 3) + (value >> 2);
654 			    value = w & 0x1f;		/* blue */
655 			    *d++ = (value << 3) + (value >> 2);
656 			    s += 2;
657 			}
658 		    }
659 		}
660 		else {
661 		    /* RGB565 */
662 		    int x, y;
663 		    unsigned short w;
664 		    unsigned char value;
665 		    unsigned char *s, *d;
666 		    for (y = 0; y<img->height; y++) {
667 			s = img->buf + y * img->rowstride;
668 			d = img->rgbbuf + y * img->width * 3;
669 			for (x=0; x<img->width; x++) {
670 			    w = s[1] + (s[0] << 8);
671 			    value = (w >> 11) & 0x1f;	/* red */
672 			    *d++ = (value << 3) + (value >> 2);
673 			    value = (w >> 5) & 0x3f;	/* green */
674 			    *d++ = (value << 2) + (value >> 4);
675 			    value = w & 0x1f;		/* blue */
676 			    *d++ = (value << 3) + (value >> 2);
677 			    s += 2;
678 			}
679 		    }
680 		}
681 	      }
682 	    }
683 	    break;
684 	case DISPLAY_COLORS_RGB:
685 	    if ( (depth == DISPLAY_DEPTH_8) &&
686 		 ((alpha == DISPLAY_ALPHA_FIRST) ||
687 	          (alpha == DISPLAY_UNUSED_FIRST)) &&
688 		 (endian == DISPLAY_BIGENDIAN) ) {
689 		/* Mac format */
690 		int x, y;
691 		unsigned char *s, *d;
692 		for (y = 0; y<img->height; y++) {
693 		    s = img->buf + y * img->rowstride;
694 		    d = img->rgbbuf + y * img->width * 3;
695 		    for (x=0; x<img->width; x++) {
696 			s++;		/* x = filler */
697 			*d++ = *s++;	/* r */
698 			*d++ = *s++;	/* g */
699 			*d++ = *s++;	/* b */
700 		    }
701 		}
702 	    }
703 	    else if ( (depth == DISPLAY_DEPTH_8) &&
704 		      (endian == DISPLAY_LITTLEENDIAN) ) {
705 	        if ((alpha == DISPLAY_UNUSED_LAST) ||
706 	            (alpha == DISPLAY_ALPHA_LAST)) {
707 		    /* Windows format + alpha = BGRx */
708 		    int x, y;
709 		    unsigned char *s, *d;
710 		    for (y = 0; y<img->height; y++) {
711 			s = img->buf + y * img->rowstride;
712 			d = img->rgbbuf + y * img->width * 3;
713 			for (x=0; x<img->width; x++) {
714 			    *d++ = s[2];	/* r */
715 			    *d++ = s[1];	/* g */
716 			    *d++ = s[0];	/* b */
717 			    s += 4;
718 			}
719 		    }
720 		}
721 	        else if ((alpha == DISPLAY_UNUSED_FIRST) ||
722 	            (alpha == DISPLAY_ALPHA_FIRST)) {
723 		    /* xBGR */
724 		    int x, y;
725 		    unsigned char *s, *d;
726 		    for (y = 0; y<img->height; y++) {
727 			s = img->buf + y * img->rowstride;
728 			d = img->rgbbuf + y * img->width * 3;
729 			for (x=0; x<img->width; x++) {
730 			    *d++ = s[3];	/* r */
731 			    *d++ = s[2];	/* g */
732 			    *d++ = s[1];	/* b */
733 			    s += 4;
734 			}
735 		    }
736 		}
737 		else {
738 		    /* Windows BGR24 */
739 		    int x, y;
740 		    unsigned char *s, *d;
741 		    for (y = 0; y<img->height; y++) {
742 			s = img->buf + y * img->rowstride;
743 			d = img->rgbbuf + y * img->width * 3;
744 			for (x=0; x<img->width; x++) {
745 			    *d++ = s[2];	/* r */
746 			    *d++ = s[1];	/* g */
747 			    *d++ = s[0];	/* b */
748 			    s += 3;
749 			}
750 		    }
751 		}
752 	    }
753 	    break;
754 	case DISPLAY_COLORS_CMYK:
755 	    if (depth == DISPLAY_DEPTH_8) {
756 	    	/* Separations */
757 		int x, y;
758 		int cyan, magenta, yellow, black;
759 		unsigned char *s, *d;
760 		for (y = 0; y<img->height; y++) {
761 		    s = img->buf + y * img->rowstride;
762 		    d = img->rgbbuf + y * img->width * 3;
763 		    for (x=0; x<img->width; x++) {
764 			cyan = *s++;
765 			magenta = *s++;
766 			yellow = *s++;
767 			black = *s++;
768 			if (!(img->separation & SEP_CYAN))
769 			    cyan = 0;
770 			if (!(img->separation & SEP_MAGENTA))
771 			    magenta = 0;
772 			if (!(img->separation & SEP_YELLOW))
773 			    yellow = 0;
774 			if (!(img->separation & SEP_BLACK))
775 			    black = 0;
776 			*d++ = (255-cyan)    * (255-black) / 255; /* r */
777 			*d++ = (255-magenta) * (255-black) / 255; /* g */
778 			*d++ = (255-yellow)  * (255-black) / 255; /* b */
779 		    }
780 		}
781 	    }
782 	    break;
783     }
784 
785     if (img->window == NULL) {
786 	window_create(img);
787 	window_resize(img);
788     }
789     if (!(GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE))
790 	gtk_widget_show_all(img->window);
791 
792     gtk_widget_draw(img->darea, NULL);
793     gtk_main_iteration_do(FALSE);
794     return 0;
795 }
796 
display_page(void * handle,void * device,int copies,int flush)797 static int display_page(void *handle, void *device, int copies, int flush)
798 {
799     display_sync(handle, device);
800     return 0;
801 }
802 
display_update(void * handle,void * device,int x,int y,int w,int h)803 static int display_update(void *handle, void *device,
804     int x, int y, int w, int h)
805 {
806     /* not implemented - eventually this will be used for progressive update */
807     return 0;
808 }
809 
810 /* callback structure for "display" device */
811 display_callback display = {
812     sizeof(display_callback),
813     DISPLAY_VERSION_MAJOR,
814     DISPLAY_VERSION_MINOR,
815     display_open,
816     display_preclose,
817     display_close,
818     display_presize,
819     display_size,
820     display_sync,
821     display_page,
822     display_update,
823     NULL,	/* memalloc */
824     NULL	/* memfree */
825 };
826 
827 /*********************************************************************/
828 
main(int argc,char * argv[])829 int main(int argc, char *argv[])
830 {
831     int exit_status;
832     int code = 1;
833     gs_main_instance *instance;
834     int nargc;
835     char **nargv;
836     char dformat[64];
837     int exit_code;
838     gboolean use_gui;
839 
840     /* Gtk initialisation */
841     gtk_set_locale();
842     use_gui = gtk_init_check(&argc, &argv);
843 
844     /* insert display device parameters as first arguments */
845     sprintf(dformat, "-dDisplayFormat=%d",
846  	    DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
847 	    DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST);
848     nargc = argc + 1;
849     nargv = (char **)malloc((nargc + 1) * sizeof(char *));
850     nargv[0] = argv[0];
851     nargv[1] = dformat;
852     memcpy(&nargv[2], &argv[1], argc * sizeof(char *));
853 
854     /* run Ghostscript */
855     if ((code = gsapi_new_instance(&instance, NULL)) == 0) {
856         gsapi_set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
857 	if (use_gui)
858             gsapi_set_display_callback(instance, &display);
859 	code = gsapi_init_with_args(instance, nargc, nargv);
860 
861 	if (code == 0)
862 	    code = gsapi_run_string(instance, start_string, 0, &exit_code);
863         gsapi_exit(instance);
864 	if (code == e_Quit)
865 	    code = 0;	/* user executed 'quit' */
866 
867 	gsapi_delete_instance(instance);
868     }
869 
870     exit_status = 0;
871     switch (code) {
872 	case 0:
873 	case e_Info:
874 	case e_Quit:
875 	    break;
876 	case e_Fatal:
877 	    exit_status = 1;
878 	    break;
879 	default:
880 	    exit_status = 255;
881     }
882 
883     return exit_status;
884 }
885 
886