Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1989, 2000 Aladdin Enterprises.  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: gdevx.c,v 1.15 2003/01/20 22:41:24 dan Exp $ */
18
/* X Windows driver for Ghostscript library */
19
#include "gx.h"			/* for gx_bitmap; includes std.h */
20
#include "math_.h"
21
#include "memory_.h"
22
#include "x_.h"
23
#include "gserrors.h"
24
#include "gsmatrix.h"		/* for gscoord.h */
25
#include "gscoord.h"		/* for gs_currentmatrix */
26
#include "gsdevice.h"		/* for gs_currentdevice */
27
#include "gsparam.h"
28
#include "gxdevice.h"
29
#include "gxpath.h"
30
#include "gxgetbit.h"
31
#include "gxiparam.h"
32
#include "gsiparm2.h"
33
#include "gxdevmem.h"
34
#include "gdevx.h"
35
 
36
/* Define whether to try to read back exposure events after XGetImage. */
37
/****** THIS IS USELESS.  XGetImage DOES NOT GENERATE EXPOSURE EVENTS. ******/
38
#define GET_IMAGE_EXPOSURES 0
39
 
40
/* GC descriptors */
41
private_st_device_X();
42
 
43
/* Forward references */
44
private int x_copy_image(gx_device_X * xdev, const byte * base, int sourcex,
45
			 int raster, int x, int y, int w, int h);
46
private int set_tile(gx_device *, const gx_strip_bitmap *);
47
private void free_cp(gx_device *);
48
 
49
/* Screen updating machinery */
50
private void update_init(gx_device_X *);
51
private void update_do_flush(gx_device_X *);
52
 
53
#define flush_text(xdev)\
54
  if (IN_TEXT(xdev)) do_flush_text(xdev)
55
private void do_flush_text(gx_device_X *);
56
 
57
/* Driver procedures */
58
/* (External procedures are declared in gdevx.h.) */
59
/*extern int gdev_x_open(gx_device_X *);*/
60
private dev_proc_open_device(x_open);
61
private dev_proc_get_initial_matrix(x_get_initial_matrix);
62
private dev_proc_sync_output(x_sync);
63
private dev_proc_output_page(x_output_page);
64
/*extern int gdev_x_close(gx_device_X *);*/
65
private dev_proc_close_device(x_close);
66
/*extern dev_proc_map_rgb_color(gdev_x_map_rgb_color);*/
67
/*extern dev_proc_map_color_rgb(gdev_x_map_color_rgb);*/
68
private dev_proc_fill_rectangle(x_fill_rectangle);
69
private dev_proc_copy_mono(x_copy_mono);
70
private dev_proc_copy_color(x_copy_color);
71
/*extern dev_proc_get_params(gdev_x_get_params);*/
72
/*extern dev_proc_put_params(gdev_x_put_params);*/
73
/*extern dev_proc_get_xfont_procs(gdev_x_get_xfont_procs);*/
74
private dev_proc_get_page_device(x_get_page_device);
75
private dev_proc_strip_tile_rectangle(x_strip_tile_rectangle);
76
private dev_proc_begin_typed_image(x_begin_typed_image);
77
private dev_proc_get_bits_rectangle(x_get_bits_rectangle);
78
/*extern dev_proc_get_xfont_procs(gdev_x_finish_copydevice);*/
79
 
80
/* The device descriptor */
81
#define x_device(this_device, dev_body, max_bitmap) \
82
const gx_device_X this_device = { \
83
    dev_body, \
84
    {				/* std_procs */ \
85
	x_open, \
86
	x_get_initial_matrix, \
87
	x_sync, \
88
	x_output_page, \
89
	x_close, \
90
	gdev_x_map_rgb_color, \
91
	gdev_x_map_color_rgb, \
92
	x_fill_rectangle, \
93
	NULL,			/* tile_rectangle */ \
94
	x_copy_mono, \
95
	x_copy_color, \
96
	NULL,			/* draw_line */ \
97
	NULL,			/* get_bits */ \
98
	gdev_x_get_params, \
99
	gdev_x_put_params, \
100
	NULL,			/* map_cmyk_color */ \
101
	gdev_x_get_xfont_procs, \
102
	NULL,			/* get_xfont_device */ \
103
	NULL,			/* map_rgb_alpha_color */ \
104
	x_get_page_device, \
105
	NULL,			/* get_alpha_bits */ \
106
	NULL,			/* copy_alpha */ \
107
	NULL,			/* get_band */ \
108
	NULL,			/* copy_rop */ \
109
	NULL,			/* fill_path */ \
110
	NULL,			/* stroke_path */ \
111
	NULL,			/* fill_mask */ \
112
	NULL,			/* fill_trapezoid */ \
113
	NULL,			/* fill_parallelogram */ \
114
	NULL,			/* fill_triangle */ \
115
	NULL,			/* draw_thin_line */ \
116
	NULL,			/* begin_image */ \
117
	NULL,			/* image_data */ \
118
	NULL,			/* end_image */ \
119
	x_strip_tile_rectangle, \
120
	NULL,			/* strip_copy_rop */ \
121
	NULL,			/* get_clipping_box */ \
122
	x_begin_typed_image, \
123
	x_get_bits_rectangle, \
124
	NULL,			/* map_color_rgb_alpha */ \
125
	NULL,			/* create_compositor */ \
126
	NULL,			/* get_hardware_params */ \
127
	NULL,			/* text_begin */ \
128
	gdev_x_finish_copydevice \
129
    }, \
130
    gx_device_bbox_common_initial(0 /*false*/, 1 /*true*/, 1 /*true*/), \
131
 
132
    1 /*true*/,			/* IsPageDevice */ \
133
    max_bitmap,			/* MaxBitmap */ \
134
    NULL,			/* buffer */ \
135
    0,				/* buffer_size */ \
136
    {				/* image */ \
137
	0, 0,			/* width, height */ \
138
	0, XYBitmap, NULL,	/* xoffset, format, data */ \
139
	MSBFirst, 8,		/* byte-order, bitmap-unit */ \
140
	MSBFirst, 8, 1,		/* bitmap-bit-order, bitmap-pad, depth */ \
141
	0, 1,			/* bytes_per_line, bits_per_pixel */ \
142
	0, 0, 0,		/* red_mask, green_mask, blue_mask */ \
143
	NULL,			/* *obdata */ \
144
	{NULL,			/* *(*create_image)() */ \
145
	 NULL,			/* (*destroy_image)() */ \
146
	 NULL,			/* (*get_pixel)() */ \
147
	 NULL,			/* (*put_pixel)() */ \
148
	 NULL,			/* *(*sub_image)() */ \
149
	 NULL			/* (*add_pixel)() */ \
150
	}, \
151
    }, \
152
    NULL, NULL,			/* dpy, scr */ \
153
				/* (connection not initialized) */ \
154
    NULL,			/* vinfo */ \
155
    (Colormap) None,		/* cmap */ \
156
    (Window) None,		/* win */ \
157
    NULL,			/* gc */ \
158
    (Window) None,		/* pwin */ \
159
    (Pixmap) 0,			/* bpixmap */ \
160
    0,				/* ghostview */ \
161
    (Window) None,		/* mwin */ \
162
    {identity_matrix_body},	/* initial matrix (filled in) */ \
163
    (Atom) 0, (Atom) 0, (Atom) 0,	/* Atoms: NEXT, PAGE, DONE */ \
164
    {				/* update */ \
165
	{			/* box */ \
166
	    {max_int_in_fixed, max_int_in_fixed}, \
167
	    {min_int_in_fixed, min_int_in_fixed} \
168
	}, \
169
	0,			/* area */ \
170
	0,			/* total */ \
171
 
172
    }, \
173
    (Pixmap) 0,			/* dest */ \
174
    0L, (ulong)~0L,		/* colors_or, colors_and */ \
175
    {				/* cp */ \
176
	(Pixmap) 0,		/* pixmap */ \
177
	NULL,			/* gc */ \
178
	-1, -1			/* raster, height */ \
179
    }, \
180
    {				/* ht */ \
181
	(Pixmap) None,		/* pixmap */ \
182
	(Pixmap) None,		/* no_pixmap */ \
183
	gx_no_bitmap_id,	/* id */ \
184
	0, 0, 0,		/* width, height, raster */ \
185
	0, 0			/* fore_c, back_c */ \
186
    }, \
187
    GXcopy,			/* function */ \
188
    FillSolid,			/* fill_style */ \
189
    0,				/* font */ \
190
    0, 0,			/* back_color, fore_color */ \
191
    0, 0,			/* background, foreground */ \
192
    { 0 },			/* cman */ \
193
    0, 0,			/* borderColor, borderWidth */ \
194
    NULL,			/* geometry */ \
195
    128, 5,			/* maxGrayRamp, maxRGBRamp */ \
196
    NULL,			/* palette */ \
197
    NULL, NULL, NULL,		/* regularFonts, symbolFonts, dingbatFonts */ \
198
    NULL, NULL, NULL,		/* regular_fonts, symbol_fonts, dingbat_fonts */ \
199
    1, 1,			/* useXFonts, useFontExtensions */ \
200
    1, 0,			/* useScalableFonts, logXFonts */ \
201
    0.0, 0.0,			/* xResolution, yResolution */ \
202
    1,				/* useBackingPixmap */ \
203
    1, 1,			/* useXPutImage, useXSetTile */ \
204
 \
205
 
206
    20000,			/* MaxTempPixmap */ \
207
    5000,			/* MaxTempImage */ \
208
    100000,			/* MaxBufferedTotal */ \
209
    100000,			/* MaxBufferedArea */ \
210
    max_int,			/* MaxBufferedCount */ \
211
 \
212
    {				/* text */ \
213
	0,			/* item_count */ \
214
	0,			/* char_count */ \
215
	{0, 0},			/* origin */ \
216
	0,			/* x */ \
217
	{ \
218
	    {0}},		/* items */ \
219
	{0}			/* chars */ \
220
    } \
221
};
222
 
223
x_device(gs_x11_device,
224
	 std_device_color_stype_body(gx_device_X, 0, "x11", &st_device_X,
225
				     FAKE_RES * DEFAULT_WIDTH_10THS / 10,
226
				     FAKE_RES * DEFAULT_HEIGHT_10THS / 10,	/* x and y extent (nominal) */
227
				     FAKE_RES, FAKE_RES,	/* x and y density (nominal) */
228
				     24, 255, 256 ),
229
	 0)
230
 
231
x_device(gs_x11alpha_device,
232
	 std_device_dci_alpha_type_body(gx_device_X, 0, "x11alpha", &st_device_X,
233
					FAKE_RES * DEFAULT_WIDTH_10THS / 10,
234
					FAKE_RES * DEFAULT_HEIGHT_10THS / 10,	/* x and y extent (nominal) */
235
					FAKE_RES, FAKE_RES,	/* x and y density (nominal) */
236
					3, 24, 255, 255, 256, 256, 4, 4 ),
237
	 50000000)
238
 
239
/* If XPutImage doesn't work, do it ourselves. */
240
private int alt_put_image(gx_device * dev, Display * dpy, Drawable win,
241
GC gc, XImage * pi, int sx, int sy, int dx, int dy, unsigned w, unsigned h);
242
 
243
#define put_image(dpy,win,gc,im,sx,sy,x,y,w,h)\
244
  BEGIN\
245
    if ( xdev->useXPutImage ) {\
246
      if (XInitImage(im) == 0)\
247
	return_error(gs_error_unknownerror);\
248
      XPutImage(dpy,win,gc,im,sx,sy,x,y,w,h);\
249
    } else {\
250
      int code_ = alt_put_image(dev,dpy,win,gc,im,sx,sy,x,y,w,h);\
251
      if ( code_ < 0 ) return code_;\
252
    }\
253
  END
254
 
255
/* Open the device.  Most of the code is in gdevxini.c. */
256
private int
257
x_open(gx_device * dev)
258
{
259
    gx_device_X *xdev = (gx_device_X *) dev;
260
    int code = gdev_x_open(xdev);
261
 
262
    if (code < 0)
263
	return code;
264
    update_init(xdev);
265
    return 0;
266
}
267
 
268
/* Close the device. */
269
private int
270
x_close(gx_device * dev)
271
{
272
    gx_device_X *xdev = (gx_device_X *) dev;
273
 
274
    return gdev_x_close(xdev);
275
}
276
 
277
/* Get initial matrix for X device. */
278
/* This conflicts seriously with the code for page devices; */
279
/* we only do it if Ghostview is active. */
280
private void
281
x_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
282
{
283
    gx_device_X *xdev = (gx_device_X *) dev;
284
 
285
    if (!xdev->ghostview) {
286
	gx_default_get_initial_matrix(dev, pmat);
287
	return;
288
    }
289
    pmat->xx = xdev->initial_matrix.xx;
290
    pmat->xy = xdev->initial_matrix.xy;
291
    pmat->yx = xdev->initial_matrix.yx;
292
    pmat->yy = xdev->initial_matrix.yy;
293
    pmat->tx = xdev->initial_matrix.tx;
294
    pmat->ty = xdev->initial_matrix.ty;
295
}
296
 
297
/* Synchronize the display with the commands already given. */
298
private int
299
x_sync(gx_device * dev)
300
{
301
    gx_device_X *xdev = (gx_device_X *) dev;
302
 
303
    update_do_flush(xdev);
304
    XSync(xdev->dpy, False);
305
    return 0;
306
}
307
 
308
/* Send event to ghostview process */
309
void
310
gdev_x_send_event(gx_device_X *xdev, Atom msg)
311
{
312
    XEvent event;
313
 
314
    event.xclient.type = ClientMessage;
315
    event.xclient.display = xdev->dpy;
316
    event.xclient.window = xdev->win;
317
    event.xclient.message_type = msg;
318
    event.xclient.format = 32;
319
    event.xclient.data.l[0] = xdev->mwin;
320
    event.xclient.data.l[1] = xdev->dest;
321
    XSendEvent(xdev->dpy, xdev->win, False, 0, &event);
322
}
323
 
324
/* Output "page" */
325
private int
326
x_output_page(gx_device * dev, int num_copies, int flush)
327
{
328
    gx_device_X *xdev = (gx_device_X *) dev;
329
 
330
    x_sync(dev);
331
 
332
    /* Send ghostview a "page" client event */
333
    /* Wait for a "next" client event */
334
    if (xdev->ghostview) {
335
	XEvent event;
336
 
337
	gdev_x_send_event(xdev, xdev->PAGE);
338
	XNextEvent(xdev->dpy, &event);
339
	while (event.type != ClientMessage ||
340
	       event.xclient.message_type != xdev->NEXT) {
341
	    XNextEvent(xdev->dpy, &event);
342
	}
343
    }
344
    return gx_finish_output_page(dev, num_copies, flush);
345
}
346
 
347
/* Fill a rectangle with a color. */
348
private int
349
x_fill_rectangle(gx_device * dev,
350
		 int x, int y, int w, int h, gx_color_index gscolor)
351
{
352
    gx_device_X *xdev = (gx_device_X *) dev;
353
    unsigned long color = (unsigned long) gscolor;
354
 
355
    fit_fill(dev, x, y, w, h);
356
    flush_text(xdev);
357
    X_SET_FILL_STYLE(xdev, FillSolid);
358
    X_SET_FORE_COLOR(xdev, color);
359
    X_SET_FUNCTION(xdev, GXcopy);
360
    XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
361
    /* If we are filling the entire screen, reset */
362
    /* colors_or and colors_and.  It's wasteful to test this */
363
    /* on every operation, but there's no separate driver routine */
364
    /* for erasepage (yet). */
365
    if (x == 0 && y == 0 && w == xdev->width && h == xdev->height) {
366
	if (color == xdev->foreground || color == xdev->background)
367
	    gdev_x_free_dynamic_colors(xdev);
368
	xdev->colors_or = xdev->colors_and = color;
369
    }
370
    if (xdev->bpixmap != (Pixmap) 0) {
371
	x_update_add(xdev, x, y, w, h);
372
    }
373
    if_debug5('F', "[F] fill (%d,%d):(%d,%d) %ld\n",
374
	      x, y, w, h, (long)color);
375
    return 0;
376
}
377
 
378
/* Copy a monochrome bitmap. */
379
private int
380
x_copy_mono(gx_device * dev,
381
	    const byte * base, int sourcex, int raster, gx_bitmap_id id,
382
	    int x, int y, int w, int h,
383
	    gx_color_index zero, gx_color_index one)
384
/*
385
 * X doesn't directly support the simple operation of writing a color
386
 * through a mask specified by an image.  The plot is the following:
387
 *  If neither color is gx_no_color_index ("transparent"),
388
 *      use XPutImage with the "copy" function as usual.
389
 *  If the color either bitwise-includes or is bitwise-included-in
390
 *      every color written to date
391
 *      (a special optimization for writing black/white on color displays),
392
 *      use XPutImage with an appropriate Boolean function.
393
 *  Otherwise, do the following complicated stuff:
394
 *      Create pixmap of depth 1 if necessary.
395
 *      If foreground color is "transparent" then
396
 *        invert the raster data.
397
 *      Use XPutImage to copy the raster image to the newly
398
 *        created Pixmap.
399
 *      Install the Pixmap as the clip_mask in the X GC and
400
 *        tweak the clip origin.
401
 *      Do an XFillRectangle, fill style=solid, specifying a
402
 *        rectangle the same size as the original raster data.
403
 *      De-install the clip_mask.
404
 */
405
{
406
    gx_device_X *xdev = (gx_device_X *) dev;
407
    int function = GXcopy;
408
    unsigned long
409
	lzero = zero,
410
	lone = one;
411
    x_pixel
412
	bc = lzero,
413
	fc = lone;
414
 
415
    fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
416
    flush_text(xdev);
417
 
418
    xdev->image.width = sourcex + w;
419
    xdev->image.height = h;
420
    xdev->image.data = (char *)base;
421
    xdev->image.bytes_per_line = raster;
422
    X_SET_FILL_STYLE(xdev, FillSolid);
423
 
424
    /* Check for null, easy 1-color, hard 1-color, and 2-color cases. */
425
    if (zero != gx_no_color_index) {
426
	if (one != gx_no_color_index) {
427
	    /* 2-color case. */
428
	    /* Simply replace existing bits with what's in the image. */
429
	} else if (!(~xdev->colors_and & bc)) {
430
	    function = GXand;
431
	    fc = ~(x_pixel) 0;
432
	} else if (!(~bc & xdev->colors_or)) {
433
	    function = GXor;
434
	    fc = 0;
435
	} else {
436
	    goto hard;
437
	}
438
    } else {
439
	if (one == gx_no_color_index) {		/* no-op */
440
	    return 0;
441
	} else if (!(~xdev->colors_and & fc)) {
442
	    function = GXand;
443
	    bc = ~(x_pixel) 0;
444
	} else if (!(~fc & xdev->colors_or)) {
445
	    function = GXor;
446
	    bc = 0;
447
	} else {
448
	    goto hard;
449
	}
450
    }
451
    xdev->image.format = XYBitmap;
452
    X_SET_FUNCTION(xdev, function);
453
    if (bc != xdev->back_color) {
454
	XSetBackground(xdev->dpy, xdev->gc, (xdev->back_color = bc));
455
    }
456
    if (fc != xdev->fore_color) {
457
	XSetForeground(xdev->dpy, xdev->gc, (xdev->fore_color = fc));
458
    }
459
    if (zero != gx_no_color_index)
460
	NOTE_COLOR(xdev, lzero);
461
    if (one != gx_no_color_index)
462
	NOTE_COLOR(xdev, lone);
463
    put_image(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
464
	      sourcex, 0, x, y, w, h);
465
 
466
    goto out;
467
 
468
  hard:			/* Handle the hard 1-color case. */
469
    if (raster > xdev->cp.raster || h > xdev->cp.height) {
470
	/* Must allocate a new pixmap and GC. */
471
	/* Release the old ones first. */
472
	free_cp(dev);
473
 
474
	/* Create the clipping pixmap, depth must be 1. */
475
	xdev->cp.pixmap =
476
	    XCreatePixmap(xdev->dpy, xdev->win, raster << 3, h, 1);
477
	if (xdev->cp.pixmap == (Pixmap) 0) {
478
	    lprintf("x_copy_mono: can't allocate pixmap\n");
479
	    return_error(gs_error_VMerror);
480
	}
481
	xdev->cp.gc = XCreateGC(xdev->dpy, xdev->cp.pixmap, 0, 0);
482
	if (xdev->cp.gc == (GC) 0) {
483
	    lprintf("x_copy_mono: can't allocate GC\n");
484
	    return_error(gs_error_VMerror);
485
	}
486
	xdev->cp.raster = raster;
487
	xdev->cp.height = h;
488
    }
489
    /* Initialize static mask image params */
490
    xdev->image.format = XYBitmap;
491
    X_SET_FUNCTION(xdev, GXcopy);
492
 
493
    /* Select polarity based on fg/bg transparency. */
494
    if (one == gx_no_color_index) {	/* invert */
495
	XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel) 1);
496
	XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel) 0);
497
	X_SET_FORE_COLOR(xdev, lzero);
498
    } else {
499
	XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel) 0);
500
	XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel) 1);
501
	X_SET_FORE_COLOR(xdev, lone);
502
    }
503
    put_image(xdev->dpy, xdev->cp.pixmap, xdev->cp.gc,
504
	      &xdev->image, sourcex, 0, 0, 0, w, h);
505
 
506
    /* Install as clipmask. */
507
    XSetClipMask(xdev->dpy, xdev->gc, xdev->cp.pixmap);
508
    XSetClipOrigin(xdev->dpy, xdev->gc, x, y);
509
 
510
    /*
511
     * Draw a solid rectangle through the raster clip mask.
512
     * Note fill style is guaranteed to be solid from above.
513
     */
514
    XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
515
 
516
    /* Tidy up.  Free the pixmap if it's big. */
517
    XSetClipMask(xdev->dpy, xdev->gc, None);
518
    if (raster * h > xdev->MaxTempPixmap)
519
	free_cp(dev);
520
 
521
  out:if (xdev->bpixmap != (Pixmap) 0) {
522
	/* We wrote to the pixmap, so update the display now. */
523
	x_update_add(xdev, x, y, w, h);
524
    }
525
    return 0;
526
}
527
 
528
/* Internal routine to free the GC and pixmap used for copying. */
529
private void
530
free_cp(gx_device * dev)
531
{
532
    gx_device_X *xdev = (gx_device_X *) dev;
533
 
534
    if (xdev->cp.gc != NULL) {
535
	XFreeGC(xdev->dpy, xdev->cp.gc);
536
	xdev->cp.gc = NULL;
537
    }
538
    if (xdev->cp.pixmap != (Pixmap) 0) {
539
	XFreePixmap(xdev->dpy, xdev->cp.pixmap);
540
	xdev->cp.pixmap = (Pixmap) 0;
541
    }
542
    xdev->cp.raster = -1;	/* mark as unallocated */
543
}
544
 
545
/* Copy a color bitmap. */
546
private int
547
x_copy_image(gx_device_X * xdev, const byte * base, int sourcex, int raster,
548
	     int x, int y, int w, int h)
549
{
550
    int depth = xdev->color_info.depth;
551
 
552
    X_SET_FILL_STYLE(xdev, FillSolid);
553
    X_SET_FUNCTION(xdev, GXcopy);
554
 
555
    /* Filling with a colored halftone often gives rise to */
556
    /* copy_color calls for a single pixel.  Check for this now. */
557
 
558
    if (h == 1 && w == 1) {
559
	uint sbit = sourcex * depth;
560
	const byte *ptr = base + (sbit >> 3);
561
	x_pixel pixel;
562
 
563
	if (depth < 8)
564
	    pixel = (byte) (*ptr << (sbit & 7)) >> (8 - depth);
565
	else {
566
	    pixel = *ptr++;
567
	    while ((depth -= 8) > 0)
568
		pixel = (pixel << 8) + *ptr++;
569
	}
570
	X_SET_FORE_COLOR(xdev, pixel);
571
	XDrawPoint(xdev->dpy, xdev->dest, xdev->gc, x, y);
572
    } else {
573
	xdev->image.width = sourcex + w;
574
	xdev->image.height = h;
575
	xdev->image.format = ZPixmap;
576
	xdev->image.data = (char *)base;
577
	xdev->image.depth = xdev->vinfo->depth;
578
	xdev->image.bytes_per_line = raster;
579
	xdev->image.bits_per_pixel = depth;
580
	if (XInitImage(&xdev->image) == 0)
581
	    return_error(gs_error_unknownerror);
582
	XPutImage(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
583
		  sourcex, 0, x, y, w, h);
584
	xdev->image.depth = xdev->image.bits_per_pixel = 1;
585
 
586
	/* give up on optimization */
587
	xdev->colors_or = (x_pixel)(-1);
588
	xdev->colors_and = 0;
589
    }
590
    return 0;
591
}
592
private int
593
x_copy_color(gx_device * dev,
594
	     const byte * base, int sourcex, int raster, gx_bitmap_id id,
595
	     int x, int y, int w, int h)
596
{
597
    gx_device_X *xdev = (gx_device_X *) dev;
598
    int code;
599
 
600
    fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
601
    flush_text(xdev);
602
    code = x_copy_image(xdev, base, sourcex, raster, x, y, w, h);
603
    if (xdev->bpixmap != (Pixmap) 0)
604
	x_update_add(xdev, x, y, w, h);
605
    if_debug4('F', "[F] copy_color (%d,%d):(%d,%d)\n",
606
	      x, y, w, h);
607
    return code;
608
}
609
 
610
/* Get the page device.  We reimplement this so that we can make this */
611
/* device be a page device conditionally. */
612
private gx_device *
613
x_get_page_device(gx_device * dev)
614
{
615
    return (((gx_device_X *) dev)->IsPageDevice ? dev : (gx_device *) 0);
616
}
617
 
618
/* Tile a rectangle. */
619
private int
620
x_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
621
		       int x, int y, int w, int h,
622
		       gx_color_index zero, gx_color_index one,
623
		       int px, int py)
624
{
625
    gx_device_X *xdev = (gx_device_X *) dev;
626
    unsigned long lzero = (unsigned long) zero;
627
    unsigned long lone = (unsigned long) one;
628
 
629
 
630
    /* Give up if one color is transparent, or if the tile is colored. */
631
    /* We should implement the latter someday, since X can handle it. */
632
 
633
    if (one == gx_no_color_index || zero == gx_no_color_index)
634
	return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
635
					       zero, one, px, py);
636
 
637
    /* For the moment, give up if the phase or shift is non-zero. */
638
    if (tiles->shift | px | py)
639
	return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
640
					       zero, one, px, py);
641
 
642
    fit_fill(dev, x, y, w, h);
643
    flush_text(xdev);
644
 
645
    /* Imaging with a halftone often gives rise to very small */
646
    /* tile_rectangle calls.  Check for this now. */
647
 
648
    if (h <= 2 && w <= 2) {
649
	int j;
650
 
651
	X_SET_FILL_STYLE(xdev, FillSolid);
652
	X_SET_FUNCTION(xdev, GXcopy);
653
	for (j = y + h; --j >= y;) {
654
	    const byte *ptr =
655
	    tiles->data + (j % tiles->rep_height) * tiles->raster;
656
	    int i;
657
 
658
	    for (i = x + w; --i >= x;) {
659
		uint tx = i % tiles->rep_width;
660
		byte mask = 0x80 >> (tx & 7);
661
		x_pixel pixel = (ptr[tx >> 3] & mask ? lone : lzero);
662
 
663
		X_SET_FORE_COLOR(xdev, pixel);
664
		XDrawPoint(xdev->dpy, xdev->dest, xdev->gc, i, j);
665
	    }
666
	}
667
	if (xdev->bpixmap != (Pixmap) 0) {
668
	    x_update_add(xdev, x, y, w, h);
669
	}
670
	return 0;
671
    }
672
    /*
673
     * Remember, an X tile is already filled with particular
674
     * pixel values (i.e., colors).  Therefore if we are changing
675
     * fore/background color, we must invalidate the tile (using
676
     * the same technique as in set_tile).  This problem only
677
     * bites when using grayscale -- you may want to change
678
     * fg/bg but use the same halftone screen.
679
     */
680
    if ((lzero != xdev->ht.back_c) || (lone != xdev->ht.fore_c))
681
	xdev->ht.id = ~tiles->id;	/* force reload */
682
 
683
    X_SET_BACK_COLOR(xdev, lzero);
684
    X_SET_FORE_COLOR(xdev, lone);
685
    if (!set_tile(dev, tiles)) {	/* Bad news.  Fall back to the default algorithm. */
686
	return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
687
					       zero, one, px, py);
688
    }
689
    /* Use the tile to fill the rectangle */
690
    X_SET_FILL_STYLE(xdev, FillTiled);
691
    X_SET_FUNCTION(xdev, GXcopy);
692
    XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
693
    if (xdev->bpixmap != (Pixmap) 0) {
694
	x_update_add(xdev, x, y, w, h);
695
    }
696
    if_debug6('F', "[F] tile (%d,%d):(%d,%d) %ld,%ld\n",
697
	      x, y, w, h, lzero, lone);
698
    return 0;
699
}
700
 
701
/* Implement ImageType 2 using CopyArea if possible. */
702
/* Note that since ImageType 2 images don't have any source data, */
703
/* this procedure does all the work. */
704
private int
705
x_begin_typed_image(gx_device * dev,
706
		    const gs_imager_state * pis, const gs_matrix * pmat,
707
		    const gs_image_common_t * pic, const gs_int_rect * prect,
708
	      const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
709
		    gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
710
{
711
    gx_device_X *xdev = (gx_device_X *) dev;
712
    const gs_image2_t *pim;
713
    gs_state *pgs;
714
    gx_device *sdev;
715
    gs_matrix smat, dmat;
716
 
717
    if (pic->type->index != 2)
718
	goto punt;
719
    pim = (const gs_image2_t *)pic;
720
    if (!pim->PixelCopy)
721
	goto punt;
722
    pgs = pim->DataSource;
723
    sdev = gs_currentdevice(pgs);
724
    if (dev->dname != sdev->dname ||
725
	memcmp(&dev->color_info, &sdev->color_info,
726
	       sizeof(dev->color_info))
727
	)
728
	goto punt;
729
    flush_text(xdev);
730
    gs_currentmatrix(pgs, &smat);
731
    /*
732
     * Figure 7.2 of the Adobe 3010 Supplement says that we should
733
     * compute CTM x ImageMatrix here, but I'm almost certain it
734
     * should be the other way around.  Also see gximage2.c.
735
     */
736
    gs_matrix_multiply(&pim->ImageMatrix, &smat, &smat);
737
    if (pis == 0)
738
	dmat = *pmat;
739
    else
740
	gs_currentmatrix((const gs_state *)pis, &dmat);
741
    if (!((is_xxyy(&dmat) || is_xyyx(&dmat)) &&
742
#define eqe(e) smat.e == dmat.e
743
	  eqe(xx) && eqe(xy) && eqe(yx) && eqe(yy))
744
#undef eqe
745
	)
746
	goto punt;
747
    {
748
	gs_rect rect, src, dest;
749
	gs_int_point size;
750
	int srcx, srcy, destx, desty;
751
 
752
	rect.p.x = rect.p.y = 0;
753
	rect.q.x = pim->Width, rect.q.y = pim->Height;
754
	gs_bbox_transform(&rect, &dmat, &dest);
755
	if (pcpath != NULL &&
756
	    !gx_cpath_includes_rectangle(pcpath,
757
			       float2fixed(dest.p.x), float2fixed(dest.p.y),
758
			       float2fixed(dest.q.x), float2fixed(dest.q.y))
759
	    )
760
	    goto punt;
761
	rect.q.x += (rect.p.x = pim->XOrigin);
762
	rect.q.y += (rect.p.y = pim->YOrigin);
763
	gs_bbox_transform(&rect, &smat, &src);
764
	(*pic->type->source_size) (pis, pic, &size);
765
	X_SET_FILL_STYLE(xdev, FillSolid);
766
	X_SET_FUNCTION(xdev, GXcopy);
767
	srcx = (int)(src.p.x + 0.5);
768
	srcy = (int)(src.p.y + 0.5);
769
	destx = (int)(dest.p.x + 0.5);
770
	desty = (int)(dest.p.y + 0.5);
771
	XCopyArea(xdev->dpy, xdev->bpixmap, xdev->bpixmap, xdev->gc,
772
		  srcx, srcy, size.x, size.y, destx, desty);
773
	x_update_add(xdev, destx, desty, size.x, size.y);
774
    }
775
    return 0;
776
  punt:return gx_default_begin_typed_image(dev, pis, pmat, pic, prect,
777
					pdcolor, pcpath, mem, pinfo);
778
}
779
 
780
/* Read bits back from the screen. */
781
private int
782
x_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
783
		     gs_get_bits_params_t * params, gs_int_rect ** unread)
784
{
785
    gx_device_X *xdev = (gx_device_X *) dev;
786
    int depth = dev->color_info.depth;
787
    int x0 = prect->p.x, y0 = prect->p.y, x1 = prect->q.x, y1 = prect->q.y;
788
    uint width_bytes = ((x1 - x0) * depth + 7) >> 3;
789
    uint band = xdev->MaxTempImage / width_bytes;
790
    uint default_raster = bitmap_raster((x1 - x0) * depth);
791
    gs_get_bits_options_t options = params->options;
792
    uint raster =
793
    (options & GB_RASTER_SPECIFIED ? params->raster :
794
     (params->raster = default_raster));
795
    long plane_mask = (1L << depth) - 1;
796
    int y, h;
797
    XImage *image;
798
    int code = 0;
799
#if GET_IMAGE_EXPOSURES
800
    XWindowAttributes attributes;
801
#endif /* GET_IMAGE_EXPOSURES */
802
 
803
    if (x0 < 0 || y0 < 0 || x1 > dev->width || y1 > dev->height)
804
	return_error(gs_error_rangecheck);
805
    /* XGetImage can only handle x_offset = 0. */
806
    if ((options & GB_OFFSET_SPECIFIED) && params->x_offset == 0)
807
	options = (options & ~GB_OFFSET_SPECIFIED) | GB_OFFSET_0;
808
    if (~options &
809
	(GB_RETURN_COPY | GB_OFFSET_0 | GB_PACKING_CHUNKY |
810
	 GB_COLORS_NATIVE) ||
811
	!(options & GB_ALIGN_ALL) ||
812
	!(options & GB_RASTER_ALL)
813
	)
814
	return
815
	    gx_default_get_bits_rectangle(dev, prect, params, unread);
816
    params->options =
817
	GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_CHUNKY |
818
	GB_RETURN_COPY | GB_OFFSET_0 |
819
	(options & GB_ALIGN_ALL) |
820
	(options & GB_RASTER_SPECIFIED ? GB_RASTER_SPECIFIED :
821
	 GB_RASTER_STANDARD);
822
    if (x0 >= x1 || y0 >= y1)
823
	return 0;
824
    if (x1 <= xdev->update.box.p.x || x0 >= xdev->update.box.q.x ||
825
	y1 <= xdev->update.box.p.y || y0 >= xdev->update.box.q.y
826
	) {
827
	/*
828
	 * The area being read back doesn't overlap the pending update:
829
	 * just flush text.
830
	 */
831
	flush_text(xdev);
832
    } else
833
	update_do_flush(xdev);
834
    /*
835
     * If we want a list of unread rectangles, turn on graphics
836
     * exposures, and accept exposure events.
837
     */
838
	/******
839
	 ****** FOLLOWING IS WRONG.  XGetImage DOES NOT GENERATE
840
	 ****** EXPOSURE EVENTS.
841
	 ******/
842
#if GET_IMAGE_EXPOSURES
843
    if (unread) {
844
	XSetGraphicsExposures(xdev->dpy, xdev->gc, True);
845
	XGetWindowAttributes(xdev->dpy, xdev->win, &attributes);
846
	XSelectInput(xdev->dpy, xdev->win,
847
		     attributes.your_event_mask | ExposureMask);
848
    }
849
#endif /* GET_IMAGE_EXPOSURES */
850
    /*
851
     * The X library doesn't provide any way to specify the desired
852
     * bit or byte ordering for the result, so we may have to swap the
853
     * bit or byte order.
854
     */
855
    if (band == 0)
856
	band = 1;
857
    for (y = y0; y < y1; y += h) {
858
	int cy;
859
 
860
	h = min(band, y1 - y);
861
	image = XGetImage(xdev->dpy, xdev->dest, x0, y, x1 - x0, h,
862
			  plane_mask, ZPixmap);
863
	for (cy = y; cy < y + h; ++cy) {
864
	    const byte *source =
865
		(const byte *)image->data + (cy - y) * image->bytes_per_line;
866
	    byte *dest = params->data[0] + (cy - y0) * raster;
867
 
868
	    /*
869
	     * XGetImage can return an image with any bit order, byte order,
870
	     * unit size (for bitmaps), and bits_per_pixel it wants: it's up
871
	     * to us to convert the results.  (It's a major botch in the X
872
	     * design that even though the server has to have the ability to
873
	     * convert images from any format to any format, there's no way
874
	     * to specify a requested format for XGetImage.)
875
	     */
876
	    if (image->bits_per_pixel == image->depth &&
877
		(image->depth > 1 || image->bitmap_bit_order == MSBFirst) &&
878
		(image->byte_order == MSBFirst || image->depth <= 8)
879
		) {
880
		/*
881
		 * The server has been nice enough to return an image in the
882
		 * format we use.  
883
		 */
884
		memcpy(dest, source, width_bytes);
885
	    } else {
886
		/*
887
		 * We need to swap byte order and/or bit order.  What a
888
		 * totally unnecessary nuisance!  For the moment, the only
889
		 * cases we deal with are 16- and 24-bit images with padding
890
		 * and/or byte swapping.
891
		 */
892
		if (image->depth == 24) {
893
		    int cx;
894
		    const byte *p = source;
895
		    byte *q = dest;
896
		    int step = image->bits_per_pixel >> 3;
897
 
898
		    if (image->byte_order == MSBFirst) {
899
			p += step - 3;
900
			for (cx = x0; cx < x1; p += step, q += 3, ++cx)
901
			    q[0] = p[0], q[1] = p[1], q[2] = p[2];
902
		    } else {
903
			for (cx = x0; cx < x1; p += step, q += 3, ++cx)
904
			    q[0] = p[2], q[1] = p[1], q[2] = p[0];
905
		    }
906
		} else if (image->depth == 16) {
907
		    int cx;
908
		    const byte *p = source;
909
		    byte *q = dest;
910
		    int step = image->bits_per_pixel >> 3;
911
 
912
		    if (image->byte_order == MSBFirst) {
913
			p += step - 2;
914
			for (cx = x0; cx < x1; p += step, q += 2, ++cx)
915
			    q[0] = p[0], q[1] = p[1];
916
		    } else {
917
			for (cx = x0; cx < x1; p += step, q += 2, ++cx)
918
			    q[0] = p[1], q[1] = p[0];
919
		    }
920
		} else
921
		    code = gs_note_error(gs_error_rangecheck);
922
	    }
923
	}
924
	XDestroyImage(image);
925
    }
926
    if (unread) {
927
#if GET_IMAGE_EXPOSURES
928
	XEvent event;
929
#endif /* GET_IMAGE_EXPOSURES */
930
 
931
	*unread = 0;
932
#if GET_IMAGE_EXPOSURES
933
	/* Read any exposure events. */
934
	XWindowEvent(xdev->dpy, xdev->win, ExposureMask, &event);
935
	if (event.type == GraphicsExpose) {
936
	    gs_int_rect *rects = (gs_int_rect *)
937
		gs_alloc_bytes(dev->memory, sizeof(gs_int_rect),
938
			       "x_get_bits_rectangle");
939
	    int num_rects = 0;
940
 
941
	    for (;;) {
942
		if (rects == 0) {
943
		    code = gs_note_error(gs_error_VMerror);
944
		    break;
945
		}
946
#define xevent (*(XGraphicsExposeEvent *)&event)
947
		rects[num_rects].q.x = xevent.width +
948
		    (rects[num_rects].p.x = xevent.x);
949
		rects[num_rects].q.y = xevent.height +
950
		    (rects[num_rects].p.y = xevent.y);
951
		++num_rects;
952
		if (!xevent.count)
953
		    break;
954
#undef xevent
955
		rects = gs_resize_object(dev->memory, rects,
956
					 (num_rects + 1) * sizeof(gs_int_rect),
957
					 "x_get_bits_rectangle");
958
	    }
959
	    if (code >= 0) {
960
		*unread = rects;
961
		code = num_rects;
962
	    }
963
	}
964
	/* Restore the window state. */
965
	XSetGraphicsExposures(xdev->dpy, xdev->gc, False);
966
	XSelectInput(xdev->dpy, xdev->win, attributes.your_event_mask);
967
#endif /* GET_IMAGE_EXPOSURES */
968
    }
969
    return code;
970
}
971
 
972
/* Set up with a specified tile. */
973
/* Return false if we can't do it for some reason. */
974
private int
975
set_tile(gx_device * dev, const gx_strip_bitmap * tile)
976
{
977
    gx_device_X *xdev = (gx_device_X *) dev;
978
 
979
#ifdef DEBUG
980
    if (gs_debug['T'])
981
	return 0;
982
#endif
983
    if (tile->id == xdev->ht.id && tile->id != gx_no_bitmap_id)
984
	return xdev->useXSetTile;
985
    /* Set up the tile Pixmap */
986
    if (tile->size.x != xdev->ht.width ||
987
	tile->size.y != xdev->ht.height ||
988
	xdev->ht.pixmap == (Pixmap) 0) {
989
	if (xdev->ht.pixmap != (Pixmap) 0)
990
	    XFreePixmap(xdev->dpy, xdev->ht.pixmap);
991
	xdev->ht.pixmap = XCreatePixmap(xdev->dpy, xdev->win,
992
					tile->size.x, tile->size.y,
993
					xdev->vinfo->depth);
994
	if (xdev->ht.pixmap == (Pixmap) 0)
995
	    return 0;
996
	xdev->ht.width = tile->size.x, xdev->ht.height = tile->size.y;
997
	xdev->ht.raster = tile->raster;
998
    }
999
    xdev->ht.fore_c = xdev->fore_color;
1000
    xdev->ht.back_c = xdev->back_color;
1001
    /* Copy the tile into the Pixmap */
1002
    xdev->image.data = (char *)tile->data;
1003
    xdev->image.width = tile->size.x;
1004
    xdev->image.height = tile->size.y;
1005
    xdev->image.bytes_per_line = tile->raster;
1006
    xdev->image.format = XYBitmap;
1007
    X_SET_FILL_STYLE(xdev, FillSolid);
1008
#ifdef DEBUG
1009
    if (gs_debug['H']) {
1010
	int i;
1011
 
1012
	dlprintf4("[H] 0x%lx: width=%d height=%d raster=%d\n",
1013
	      (ulong) tile->data, tile->size.x, tile->size.y, tile->raster);
1014
	dlputs("");
1015
	for (i = 0; i < tile->raster * tile->size.y; i++)
1016
	    dprintf1(" %02x", tile->data[i]);
1017
	dputc('\n');
1018
    }
1019
#endif
1020
    XSetTile(xdev->dpy, xdev->gc, xdev->ht.no_pixmap);	/* *** X bug *** */
1021
    X_SET_FUNCTION(xdev, GXcopy);
1022
    put_image(xdev->dpy, xdev->ht.pixmap, xdev->gc, &xdev->image,
1023
	      0, 0, 0, 0, tile->size.x, tile->size.y);
1024
    XSetTile(xdev->dpy, xdev->gc, xdev->ht.pixmap);
1025
    xdev->ht.id = tile->id;
1026
    return xdev->useXSetTile;
1027
}
1028
 
1029
/* ------ Screen update procedures ------ */
1030
 
1031
/* Initialize the update machinery. */
1032
private void
1033
update_init(gx_device_X *xdev)
1034
{
1035
    xdev->update.box.p.x = xdev->update.box.p.y = max_int_in_fixed;
1036
    xdev->update.box.q.x = xdev->update.box.q.y = min_int_in_fixed;
1037
    xdev->update.area = xdev->update.total = xdev->update.count = 0;
1038
}
1039
 
1040
/* Flush updates to the screen if needed. */
1041
private void
1042
update_do_flush(gx_device_X * xdev)
1043
{
1044
    flush_text(xdev);
1045
    if (xdev->update.count != 0) {
1046
	int x = xdev->update.box.p.x, y = xdev->update.box.p.y;
1047
	int w = xdev->update.box.q.x - x, h = xdev->update.box.q.y - y;
1048
 
1049
	fit_fill_xywh(xdev, x, y, w, h);
1050
	if (w > 0 && h > 0) {
1051
	    if (xdev->is_buffered) {
1052
		/* Copy from memory image to X server. */
1053
		const gx_device_memory *mdev =
1054
		    (const gx_device_memory *)xdev->target;
1055
 
1056
		/*
1057
		 * The bbox device may have set the target to NULL
1058
		 * temporarily.  If this is the case, defer the screen
1059
		 * update.
1060
		 */
1061
		if (mdev == NULL)
1062
		    return;	/* don't reset */
1063
		x_copy_image(xdev, mdev->line_ptrs[y], x, mdev->raster,
1064
			     x, y, w, h);
1065
	    }
1066
	    if (xdev->bpixmap) {
1067
		/* Copy from X backing pixmap to screen. */
1068
 
1069
		X_SET_FUNCTION(xdev, GXcopy);
1070
		XCopyArea(xdev->dpy, xdev->bpixmap, xdev->win, xdev->gc,
1071
			  x, y, w, h, x, y);
1072
	    }
1073
	}
1074
	update_init(xdev);
1075
    }
1076
}
1077
 
1078
/* Add a region to be updated, after writing to that region. */
1079
void
1080
x_update_add(gx_device_X * xdev, int xo, int yo, int w, int h)
1081
{
1082
    int xe = xo + w, ye = yo + h;
1083
    long added = (long)w * h;
1084
    long old_area = xdev->update.area;
1085
    gs_int_rect u;
1086
    int nw, nh;
1087
    long new_up_area;
1088
 
1089
    u.p.x = min(xo, xdev->update.box.p.x);
1090
    u.p.y = min(yo, xdev->update.box.p.y);
1091
    u.q.x = max(xe, xdev->update.box.q.x);
1092
    u.q.y = max(ye, xdev->update.box.q.y);
1093
    nw = u.q.x - u.p.x;
1094
    nh = u.q.y - u.p.y;
1095
    new_up_area = (long)nw * nh;
1096
    xdev->update.count++;
1097
    xdev->update.area = new_up_area;
1098
    xdev->update.total += added;
1099
    if (!xdev->AlwaysUpdate &&
1100
	xdev->update.count < xdev->MaxBufferedCount &&
1101
	xdev->update.area < xdev->MaxBufferedArea &&
1102
	xdev->update.total < xdev->MaxBufferedTotal
1103
	) {
1104
	/*
1105
	 * Test whether adding this rectangle would result in too much being
1106
	 * copied unnecessarily.  The fraction of new_up_area used in the
1107
	 * following test is not particularly critical; using a denominator
1108
	 * that is a power of 2 eliminates a divide.
1109
	 */
1110
	if (nw + nh >= 70 && (nw | nh) >= 16 &&
1111
	    old_area + added < new_up_area - (new_up_area >> 2)
1112
	    )
1113
	    DO_NOTHING;
1114
	else {
1115
	    xdev->update.box = u;
1116
	    return;
1117
	}
1118
    }
1119
    if (xdev->is_buffered && (xdev->target == NULL))
1120
	xdev->update.box = u;	/* update deferred since bbox has target disabled */
1121
    else {
1122
	update_do_flush(xdev);
1123
	xdev->update.box.p.x = xo, xdev->update.box.p.y = yo;
1124
	xdev->update.box.q.x = xe, xdev->update.box.q.y = ye;
1125
	xdev->update.count = 1;
1126
	xdev->update.area = xdev->update.total = added;
1127
    }
1128
}
1129
 
1130
/* Flush buffered text to the screen. */
1131
private void
1132
do_flush_text(gx_device_X * xdev)
1133
{
1134
    if (!IN_TEXT(xdev))
1135
	return;
1136
    DRAW_TEXT(xdev);
1137
    xdev->text.item_count = xdev->text.char_count = 0;
1138
}
1139
 
1140
/* Bounding box device procedures (only used when buffering) */
1141
private bool
1142
x_bbox_init_box(void *pdata)
1143
{
1144
    gx_device_X *const xdev = pdata;
1145
 
1146
    update_init(xdev);
1147
    return true;
1148
}
1149
private void
1150
x_bbox_get_box(const void *pdata, gs_fixed_rect *pbox)
1151
{
1152
    const gx_device_X *const xdev = pdata;
1153
 
1154
    pbox->p.x = int2fixed(xdev->update.box.p.x);
1155
    pbox->p.y = int2fixed(xdev->update.box.p.y);
1156
    pbox->q.x = int2fixed(xdev->update.box.q.x);
1157
    pbox->q.y = int2fixed(xdev->update.box.q.y);
1158
}
1159
private void
1160
x_bbox_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
1161
{
1162
    gx_device_X *const xdev = pdata;
1163
    int x = fixed2int(x0), y = fixed2int(y0);
1164
 
1165
    x_update_add(xdev, x, y, fixed2int_ceiling(x1) - x,
1166
		 fixed2int_ceiling(y1) - y);
1167
}
1168
private bool
1169
x_bbox_in_rect(const void *pdata, const gs_fixed_rect *pbox)
1170
{
1171
    gs_fixed_rect box;
1172
 
1173
    x_bbox_get_box(pdata, &box);
1174
    return rect_within(*pbox, box);
1175
}
1176
const gx_device_bbox_procs_t gdev_x_box_procs = {
1177
    x_bbox_init_box, x_bbox_get_box, x_bbox_add_rect, x_bbox_in_rect
1178
};
1179
 
1180
/* ------ Internal procedures ------ */
1181
 
1182
/*
1183
 * Substitute for XPutImage using XFillRectangle.  This is a hack to get
1184
 * around an apparent bug in some X servers.  It only works with the
1185
 * specific parameters (bit/byte order, padding) used above.
1186
 */
1187
private int
1188
alt_put_image(gx_device *dev, Display *dpy, Drawable win, GC gc, XImage *pi,
1189
	      int sx, int sy, int dx, int dy, unsigned w, unsigned h)
1190
{
1191
    int raster = pi->bytes_per_line;
1192
    byte *data = (byte *) pi->data + sy * raster + (sx >> 3);
1193
    int init_mask = 0x80 >> (sx & 7);
1194
    int invert = 0;
1195
    int yi;
1196
#define NUM_RECTS 40
1197
    XRectangle rects[NUM_RECTS];
1198
    XRectangle *rp = rects;
1199
    XGCValues gcv;
1200
 
1201
#ifdef DEBUG
1202
    if (pi->format != XYBitmap || pi->byte_order != MSBFirst ||
1203
	pi->bitmap_bit_order != MSBFirst || pi->depth != 1
1204
	) {
1205
	lprintf("alt_put_image: unimplemented parameter values!\n");
1206
	return_error(gs_error_rangecheck);
1207
    }
1208
#endif
1209
 
1210
    XGetGCValues(dpy, gc, (GCFunction | GCForeground | GCBackground), &gcv);
1211
 
1212
    if (gcv.function == GXcopy) {
1213
	XSetForeground(dpy, gc, gcv.background);
1214
	XFillRectangle(dpy, win, gc, dx, dy, w, h);
1215
	XSetForeground(dpy, gc, gcv.foreground);
1216
    } else if (gcv.function == GXand) {
1217
	/* The only cases used above are fc = ~0 or bc = ~0. */
1218
#ifdef DEBUG
1219
	if (gcv.foreground != ~(x_pixel)0 && gcv.background != ~(x_pixel)0) {
1220
	    lprintf("alt_put_image: unimplemented GXand case!\n");
1221
	    return_error(gs_error_rangecheck);
1222
	}
1223
#endif
1224
	if (gcv.background != ~(x_pixel) 0) {
1225
	    XSetForeground(dpy, gc, gcv.background);
1226
	    invert = 0xff;
1227
	}
1228
    } else if (gcv.function == GXor) {
1229
	/* The only cases used above are fc = 0 or bc = 0. */
1230
#ifdef DEBUG
1231
	if (gcv.foreground != 0 && gcv.background != 0) {
1232
	    lprintf("alt_put_image: unimplemented GXor case!\n");
1233
	    return_error(gs_error_rangecheck);
1234
	}
1235
#endif
1236
	if (gcv.background != 0) {
1237
	    XSetForeground(dpy, gc, gcv.background);
1238
	    invert = 0xff;
1239
	}
1240
    } else {
1241
	lprintf("alt_put_image: unimplemented function.\n");
1242
	return_error(gs_error_rangecheck);
1243
    }
1244
 
1245
    for (yi = 0; yi < h; yi++, data += raster) {
1246
	int mask = init_mask;
1247
	byte *dp = data;
1248
	int xi = 0;
1249
 
1250
	while (xi < w) {
1251
	    if ((*dp ^ invert) & mask) {
1252
		int xleft = xi;
1253
 
1254
		if (rp == &rects[NUM_RECTS]) {
1255
		    XFillRectangles(dpy, win, gc, rects, NUM_RECTS);
1256
		    rp = rects;
1257
		}
1258
		/* Scan over a run of 1-bits */
1259
		rp->x = dx + xi, rp->y = dy + yi;
1260
		do {
1261
		    if (!(mask >>= 1))
1262
			mask = 0x80, dp++;
1263
		    xi++;
1264
		} while (xi < w && ((*dp ^ invert) & mask));
1265
		rp->width = xi - xleft, rp->height = 1;
1266
		rp++;
1267
	    } else {
1268
		if (!(mask >>= 1))
1269
		    mask = 0x80, dp++;
1270
		xi++;
1271
	    }
1272
	}
1273
    }
1274
    XFillRectangles(dpy, win, gc, rects, rp - rects);
1275
    if (invert)
1276
	XSetForeground(dpy, gc, gcv.foreground);
1277
    return 0;
1278
#undef NUM_RECTS
1279
}