xref: /plan9/sys/src/cmd/gs/src/gdevx.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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     0 /*false*/,		/* is_buffered */ \
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 	0			/* count */ \
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     0 /*false*/,		/* AlwaysUpdate */ \
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
x_open(gx_device * dev)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
x_close(gx_device * dev)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
x_get_initial_matrix(gx_device * dev,gs_matrix * pmat)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
x_sync(gx_device * dev)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
gdev_x_send_event(gx_device_X * xdev,Atom msg)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
x_output_page(gx_device * dev,int num_copies,int flush)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
x_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index gscolor)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
x_copy_mono(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)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
free_cp(gx_device * dev)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
x_copy_image(gx_device_X * xdev,const byte * base,int sourcex,int raster,int x,int y,int w,int h)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
x_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)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 *
x_get_page_device(gx_device * dev)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
x_strip_tile_rectangle(gx_device * dev,const gx_strip_bitmap * tiles,int x,int y,int w,int h,gx_color_index zero,gx_color_index one,int px,int py)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
x_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gx_image_enum_common_t ** pinfo)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
x_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)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
set_tile(gx_device * dev,const gx_strip_bitmap * tile)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
update_init(gx_device_X * xdev)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
update_do_flush(gx_device_X * xdev)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
x_update_add(gx_device_X * xdev,int xo,int yo,int w,int h)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
do_flush_text(gx_device_X * xdev)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
x_bbox_init_box(void * pdata)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
x_bbox_get_box(const void * pdata,gs_fixed_rect * pbox)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
x_bbox_add_rect(void * pdata,fixed x0,fixed y0,fixed x1,fixed y1)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
x_bbox_in_rect(const void * pdata,const gs_fixed_rect * pbox)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
alt_put_image(gx_device * dev,Display * dpy,Drawable win,GC gc,XImage * pi,int sx,int sy,int dx,int dy,unsigned w,unsigned h)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 }
1280