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