Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 
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 
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 
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 
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 *
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
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
 
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
 
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
 
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
 
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
 
350
static void signal_sep0(GtkWidget *w, gpointer data)
351
{
352
    window_separation((IMAGE *)data, 0);
353
}
354
 
355
static void signal_sep1(GtkWidget *w, gpointer data)
356
{
357
    window_separation((IMAGE *)data, 1);
358
}
359
 
360
static void signal_sep2(GtkWidget *w, gpointer data)
361
{
362
    window_separation((IMAGE *)data, 2);
363
}
364
 
365
static void signal_sep3(GtkWidget *w, gpointer data)
366
{
367
    window_separation((IMAGE *)data, 3);
368
}
369
 
370
static void signal_sep4(GtkWidget *w, gpointer data)
371
{
372
    window_separation((IMAGE *)data, 4);
373
}
374
 
375
static void signal_sep5(GtkWidget *w, gpointer data)
376
{
377
    window_separation((IMAGE *)data, 5);
378
}
379
 
380
static void signal_sep6(GtkWidget *w, gpointer data)
381
{
382
    window_separation((IMAGE *)data, 6);
383
}
384
 
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 *
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
 
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 */
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
 
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
 
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
 
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
 
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
 
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
 
1044
static int display_page(void *handle, void *device, int copies, int flush)
1045
{
1046
    display_sync(handle, device);
1047
    return 0;
1048
}
1049
 
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 
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
 
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