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