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