386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gdevx.c
CommitLineData
769a6dd7
WJ
1/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gdevx.c */
21/* X Windows driver for Ghostscript library */
22/* The X include files include <sys/types.h>, which, on some machines */
23/* at least, define uint, ushort, and ulong, which std.h also defines. */
24/* std.h has taken care of this. */
25#include "gx.h" /* for gx_bitmap; includes std.h */
26#include "memory_.h"
27#include "x_.h"
28#include "gxdevice.h"
29#include "gdevx.h"
30
31/* Flags for patching around bugs in the X library */
32private int use_XPutImage = 1;
33private int use_XSetTile = 1;
34
35/* Define the maximum size of the temporary pixmap for copy_mono */
36/* that we are willing to leave lying around in the server */
37/* between uses. (Assume 32-bit ints here!) */
38private int max_temp_pixmap = 20000;
39
40/* Forward references */
41private int set_tile(P2(gx_device *, gx_bitmap *));
42private void free_cp(P1(gx_device *));
43/* Screen updating machinery */
44#define update_init(dev)\
45 ((gx_device_X *)(dev))->up_area = 0,\
46 ((gx_device_X *)(dev))->up_count = 0
47#define update_flush(dev)\
48 if ( ((gx_device_X *)(dev))->up_area != 0 ) update_do_flush(dev)
49private void update_do_flush(P1(gx_device *));
50private void update_add(P5(gx_device *, int, int, int, int));
51private void send_event(P2(gx_device *, Atom));
52
53/* Procedures */
54
55extern int gdev_x_open(P1(gx_device_X *));
56private dev_proc_open_device(x_open);
57private dev_proc_get_initial_matrix(x_get_initial_matrix);
58private dev_proc_sync_output(x_sync);
59private dev_proc_output_page(x_output_page);
60private dev_proc_close_device(x_close);
61private dev_proc_map_rgb_color(x_map_rgb_color);
62private dev_proc_map_color_rgb(x_map_color_rgb);
63private dev_proc_fill_rectangle(x_fill_rectangle);
64private dev_proc_tile_rectangle(x_tile_rectangle);
65private dev_proc_copy_mono(x_copy_mono);
66private dev_proc_copy_color(x_copy_color);
67private dev_proc_draw_line(x_draw_line);
68
69/* The device descriptor */
70private gx_device_procs x_procs = {
71 x_open,
72 x_get_initial_matrix,
73 x_sync,
74 x_output_page,
75 x_close,
76 x_map_rgb_color,
77 x_map_color_rgb,
78 x_fill_rectangle,
79 x_tile_rectangle,
80 x_copy_mono,
81 x_copy_color,
82 x_draw_line,
83 gx_default_get_bits,
84 gx_default_get_props,
85 gx_default_put_props
86};
87
88/* The instance is public. */
89gx_device_X gs_x11_device = {
90 sizeof(gx_device_X),
91 &x_procs,
92 "x11",
93 (int)(FAKE_RES*DEFAULT_WIDTH_INCHES), (int)(FAKE_RES*DEFAULT_HEIGHT_INCHES), /* x and y extent (nominal) */
94 FAKE_RES, FAKE_RES, /* x and y density (nominal) */
95 no_margins,
96 dci_black_and_white,
97 0, /* connection not initialized */
98 { /* image */
99 0, 0, /* width, height */
100 0, XYBitmap, NULL, /* xoffset, format, data */
101 LSBFirst, 8, /* byte-order, bitmap-unit */
102 MSBFirst, 8, 1, /* bitmap-bit-order, bitmap-pad, depth */
103 0, 1, /* bytes_per_line, bits_per_pixel */
104 0, 0, 0, /* red_mask, green_mask, blue_mask */
105 NULL, /* *obdata */
106 { NULL, /* *(*create_image)() */
107 NULL, /* (*destroy_image)() */
108 NULL, /* (*get_pixel)() */
109 NULL, /* (*put_pixel)() */
110 NULL, /* *(*sub_image)() */
111 NULL /* (*add_pixel)() */
112 },
113 },
114 NULL, NULL, /* dpy, scr */
115 /* (connection not initialized) */
116 NULL, /* vinfo */
117 (Colormap)None, /* cmap */
118 (Window)None, /* win */
119 NULL, /* gc */
120 (Pixmap)0, /* bpixmap */
121 0, /* ghostview */
122 (Window)None, /* mwin */
123#if HaveStdCMap
124 NULL, /* std_cmap */
125#endif
126 identity_matrix_body, /* initial matrix (filled in) */
127 (Atom)0, (Atom)0, (Atom)0, /* Atoms: NEXT, PAGE, DONE */
128 { 0, 0, 0, 0 }, 0, 0, /* update, up_area, up_count */
129 (Pixmap)0, /* dest */
130 0L, ~0L, /* colors_or, colors_and */
131 { /* cp */
132 (Pixmap)0, /* pixmap */
133 NULL, /* gc */
134 -1, -1 /* raster, height */
135 },
136 { /* ht */
137 (Pixmap)None, /* pixmap */
138 (Pixmap)None, /* no_pixmap */
139 gx_no_bitmap_id, /* id */
140 0, 0, 0, /* width, height, raster */
141 0, 0 /* fore_c, back_c */
142 },
143 GXcopy, /* function */
144 FillSolid, /* fill_style */
145 0, /* pixel_fix */
146 { 0, 0, 0, 0, 0, 0, 0, 0 }, /* colors[8] */
147 0, 0 /* back_color, fore_color */
148
149};
150
151/* Macro for casting gx_device argument */
152#define xdev ((gx_device_X *)dev)
153
154/* Macros to validate and coerce arguments */
155#define check_rect_extent()\
156 if ( x + w > xdev->width ) w = xdev->width - x;\
157 if ( y + h > xdev->height ) h = xdev->height - y;\
158 if ( w <= 0 || h <= 0 ) return 0
159#define check_rect()\
160 if ( x < 0 ) w += x, x = 0;\
161 if ( y < 0 ) h += y, y = 0;\
162 check_rect_extent()
163
164/* If XPutImage doesn't work, do it ourselves. */
165private void alt_put_image();
166#define put_image(dpy,win,gc,im,sx,sy,x,y,w,h)\
167 if ( use_XPutImage) XPutImage(dpy,win,gc,im,sx,sy,x,y,w,h);\
168 else alt_put_image(dev,dpy,win,gc,im,sx,sy,x,y,w,h)
169
170
171/* Open the device. Most of the code is in gdevxini.c. */
172private int
173x_open(gx_device *dev)
174{ int code = gdev_x_open(xdev);
175 if ( code < 0 ) return code;
176 update_init(dev);
177 return 0;
178}
179
180/* Close the device. NOT SURE WHAT TO DO HERE YET. */
181private int
182x_close(gx_device *dev)
183{ if ( xdev->ghostview )
184 { send_event(dev, xdev->done);
185 }
186 XCloseDisplay(xdev->dpy);
187 return 0;
188}
189
190/* Map a color. The "device colors" are just r,g,b packed together. */
191private gx_color_index
192x_map_rgb_color(register gx_device *dev,
193 gx_color_value r, gx_color_value g, gx_color_value b)
194{
195#if HaveStdCMap
196 if ( xdev->std_cmap )
197 { XStandardColormap *cmap = xdev->std_cmap;
198 x_pixel color;
199 if ( r == 0 && g == 0 && b == 0 )
200 return pixel_black;
201 if ( r == gx_max_color_value && g == gx_max_color_value &&
202 b == gx_max_color_value )
203 return pixel_white;
204#define cv_denom (gx_max_color_value + 1)
205 color =
206 (gx_device_has_color(xdev) ?
207 (r * (cmap->red_max + 1) / cv_denom * cmap->red_mult) +
208 (g * (cmap->green_max + 1) / cv_denom * cmap->green_mult) +
209 (b * (cmap->blue_max + 1) / cv_denom * cmap->blue_mult) :
210 (r * (xdev->color_info.max_gray + 1) / cv_denom *
211 cmap->red_mult)) +
212 cmap->base_pixel;
213#undef cv_denom
214 return pixel_to_color_index(color);
215 }
216 else
217#endif
218#define cv_half (gx_max_color_value / 2)
219 { return
220 pixel_to_color_index(xdev->colors[(r > cv_half ? 4 : 0) +
221 (g > cv_half ? 2 : 0) +
222 (b > cv_half ? 1 : 0)]);
223 }
224#undef cv_half
225}
226
227
228/* Map a "device color" back to r-g-b. */
229private int
230x_map_color_rgb(register gx_device *dev, gx_color_index color,
231 gx_color_value prgb[3])
232{ x_pixel pixel = color_index_to_pixel(color);
233 if ( pixel == pixel_black )
234 { prgb[0] = prgb[1] = prgb[2] = 0;
235 }
236 else if ( pixel == pixel_white )
237 { prgb[0] = prgb[1] = prgb[2] = gx_max_color_value;
238 }
239#if HaveStdCMap
240 else if ( xdev->std_cmap )
241 { XStandardColormap *cmap = xdev->std_cmap;
242 if ( gx_device_has_color(xdev) )
243 { prgb[0] =
244 ((pixel - cmap->base_pixel) / cmap->red_mult) %
245 (cmap->red_max + 1) * gx_max_color_value /
246 cmap->red_max;
247 prgb[1] =
248 ((pixel - cmap->base_pixel) / cmap->green_mult) %
249 (cmap->green_max + 1) * gx_max_color_value /
250 cmap->green_max;
251 prgb[2] =
252 ((pixel - cmap->base_pixel) / cmap->blue_mult) %
253 (cmap->blue_max + 1) * gx_max_color_value /
254 cmap->blue_max;
255 }
256 else
257 { prgb[0] = prgb[1] = prgb[2] =
258 (pixel - cmap->base_pixel) / cmap->red_mult *
259 gx_max_color_value / xdev->color_info.max_gray;
260 }
261 }
262#endif
263 else
264 { int i;
265 for ( i = 1; i < 7; i++ )
266 { if ( pixel == xdev->colors[i] )
267 { prgb[0] = (i & 4 ? gx_max_color_value : 0);
268 prgb[1] = (i & 2 ? gx_max_color_value : 0);
269 prgb[2] = (i & 1 ? gx_max_color_value : 0);
270 break;
271 }
272 }
273 }
274 return 0;
275}
276
277/* Get initial matrix for X device */
278private void
279x_get_initial_matrix(register gx_device *dev, register gs_matrix *pmat)
280{ pmat->xx = xdev->initial_matrix.xx;
281 pmat->xy = xdev->initial_matrix.xy;
282 pmat->yx = xdev->initial_matrix.yx;
283 pmat->yy = xdev->initial_matrix.yy;
284 pmat->tx = xdev->initial_matrix.tx;
285 pmat->ty = xdev->initial_matrix.ty;
286}
287
288/* Synchronize the display with the commands already given */
289private int
290x_sync(register gx_device *dev)
291{ update_flush(dev);
292 XSync(xdev->dpy, 0);
293 return 0;
294}
295
296/* Send event to ghostview process */
297private void
298send_event(gx_device *dev, Atom msg)
299{ XEvent event;
300 event.xclient.type = ClientMessage;
301 event.xclient.display = xdev->dpy;
302 event.xclient.window = xdev->win;
303 event.xclient.message_type = msg;
304 event.xclient.format = 32;
305 event.xclient.data.l[0] = xdev->mwin;
306 event.xclient.data.l[1] = xdev->dest;
307 XSendEvent(xdev->dpy, xdev->win, False, 0, &event);
308}
309
310/* Output "page" */
311private int
312x_output_page(gx_device *dev, int num_copies, int flush)
313{ x_sync(dev);
314
315 /* Send ghostview a "page" client event */
316 /* Wait for a "next" client event */
317 if ( xdev->ghostview )
318 { XEvent event;
319 send_event(dev, xdev->page);
320 XNextEvent(xdev->dpy, &event);
321 while (event.type != ClientMessage ||
322 event.xclient.message_type != xdev->next)
323 { XNextEvent(xdev->dpy, &event);
324 }
325 }
326 return 0;
327}
328
329/* Fill a rectangle with a color. */
330private int
331x_fill_rectangle(register gx_device *dev,
332 int x, int y, int w, int h, gx_color_index color)
333{ check_rect();
334 set_fill_style(FillSolid);
335 set_fore_color(color_index_to_pixel(color));
336 set_function(GXcopy);
337 XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
338 /* If we are filling the entire screen, reset */
339 /* colors_or and colors_and. It's wasteful to do this */
340 /* on every operation, but there's no separate driver routine */
341 /* for erasepage (yet). */
342 if ( x == 0 && y == 0 && w == xdev->width && h == xdev->height )
343 { xdev->colors_or = xdev->colors_and = color_index_to_pixel(color);
344 }
345 if ( xdev->bpixmap != (Pixmap)0 )
346 { update_add(dev, x, y, w, h);
347 }
348#ifdef DEBUG
349if ( gs_debug['F'] )
350 dprintf5("[F] fill (%d,%d):(%d,%d) %ld\n",
351 x, y, w, h, (long)color);
352#endif
353 return 0;
354}
355
356/* Tile a rectangle. */
357private int
358x_tile_rectangle(register gx_device *dev, gx_bitmap *tile,
359 int x, int y, int w, int h, gx_color_index zero, gx_color_index one,
360 int px, int py)
361{ x_pixel
362 p_zero = color_index_to_pixel(zero),
363 p_one = color_index_to_pixel(one);
364 check_rect();
365
366 /* Check for a colored tile. We should implement this */
367 /* properly someday, since X can handle it. */
368
369 if ( one == gx_no_color_index && zero == gx_no_color_index )
370 return -1;
371
372 /* For the moment, give up if the phase is non-zero. */
373 if ( px | py )
374 return -1;
375
376 /*
377 * Remember, an X tile is already filled with particular
378 * pixel values (i.e., colors). Therefore if we are changing
379 * fore/background color, we must invalidate the tile (using
380 * the same technique as in set_tile). This problem only
381 * bites when using grayscale -- you may want to change
382 * fg/bg but use the same halftone screen.
383 */
384 if ( (p_zero != xdev->ht.back_c) || (p_one != xdev->ht.fore_c) )
385 xdev->ht.id = ~tile->id; /* force reload */
386
387 set_back_color(p_zero);
388 set_fore_color(p_one);
389 if ( !set_tile(dev, tile) )
390 { /* Bad news. Fall back to the default algorithm. */
391 return gx_default_tile_rectangle(dev, tile, x, y, w, h, zero, one, px, py);
392 }
393 else
394 { /* Use the tile to fill the rectangle */
395 set_fill_style(FillTiled);
396 set_function(GXcopy);
397 XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
398 if ( xdev->bpixmap != (Pixmap)0 )
399 { update_add(dev, x, y, w, h);
400 }
401 }
402#ifdef DEBUG
403if ( gs_debug['F'] )
404 dprintf6("[F] tile (%d,%d):(%d,%d) %ld,%ld\n",
405 x, y, w, h, (long)zero, (long)one);
406#endif
407 return 0;
408}
409
410/* Set up with a specified tile. */
411/* Return false if we can't do it for some reason. */
412private int
413set_tile(register gx_device *dev, register gx_bitmap *tile)
414{
415#ifdef DEBUG
416if ( gs_debug['T'] )
417 return 0;
418#endif
419 if ( tile->id == xdev->ht.id && tile->id != gx_no_bitmap_id )
420 return use_XSetTile;
421 /* Set up the tile Pixmap */
422 if ( tile->size.x != xdev->ht.width ||
423 tile->size.y != xdev->ht.height ||
424 xdev->ht.pixmap == (Pixmap)0
425 )
426 { if ( xdev->ht.pixmap != (Pixmap)0 )
427 XFreePixmap(xdev->dpy, xdev->ht.pixmap);
428 xdev->ht.pixmap = XCreatePixmap(xdev->dpy, xdev->win,
429 tile->size.x, tile->size.y,
430 xdev->vinfo->depth);
431 if ( xdev->ht.pixmap == (Pixmap)0 )
432 return 0;
433 xdev->ht.width = tile->size.x, xdev->ht.height = tile->size.y;
434 xdev->ht.raster = tile->raster;
435 }
436 xdev->ht.fore_c = xdev->fore_color;
437 xdev->ht.back_c = xdev->back_color;
438 /* Copy the tile into the Pixmap */
439 xdev->image.data = (char *)tile->data;
440 xdev->image.width = tile->size.x;
441 xdev->image.height = tile->size.y;
442 xdev->image.bytes_per_line = tile->raster;
443 xdev->image.format = XYBitmap;
444 set_fill_style(FillSolid);
445#ifdef DEBUG
446if ( gs_debug['H'] )
447 { int i;
448 dprintf4("[H] 0x%x: width=%d height=%d raster=%d\n",
449 tile->data, tile->size.x, tile->size.y, tile->raster);
450 for ( i = 0; i < tile->raster * tile->size.y; i++ )
451 dprintf1(" %02x", tile->data[i]);
452 dputc('\n');
453 }
454#endif
455 XSetTile(xdev->dpy, xdev->gc, xdev->ht.no_pixmap); /* *** X bug *** */
456 set_function(GXcopy);
457 put_image(xdev->dpy, xdev->ht.pixmap, xdev->gc, &xdev->image,
458 0, 0, 0, 0, tile->size.x, tile->size.y);
459 XSetTile(xdev->dpy, xdev->gc, xdev->ht.pixmap);
460 xdev->ht.id = tile->id;
461 return use_XSetTile;
462}
463
464/* Copy a monochrome bitmap. */
465private int
466x_copy_mono(register gx_device *dev,
467 byte *base, int sourcex, int raster, gx_bitmap_id id,
468 int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
469/*
470 * X doesn't directly support the simple operation of writing a color
471 * through a mask specified by an image. The plot is the following:
472 * If neither color is gx_no_color_index ("transparent"),
473 * use XPutImage with the "copy" function as usual.
474 * If the color either bitwise-includes or is bitwise-included-in
475 * every color written to date
476 * (a special optimization for writing black/white on color displays),
477 * use XPutImage with an appropriate Boolean function.
478 * Otherwise, do the following complicated stuff:
479 * Create pixmap of depth 1 if necessary.
480 * If foreground color is "transparent" then
481 * invert the raster data.
482 * Use XPutImage to copy the raster image to the newly
483 * created Pixmap.
484 * Install the Pixmap as the clip_mask in the X GC and
485 * tweak the clip origin.
486 * Do an XFillRectangle, fill style=solid, specifying a
487 * rectangle the same size as the original raster data.
488 * De-install the clip_mask.
489 */
490{ int function = GXcopy;
491 x_pixel
492 p_zero = color_index_to_pixel(zero),
493 p_one = color_index_to_pixel(one);
494 x_pixel
495 bc = p_zero,
496 fc = p_one;
497
498 /* We need a different version of check_rect, because */
499 /* we have to adjust the source coordinates too. */
500 if ( x < 0 ) w += x, sourcex -= x, x = 0;
501 if ( y < 0 ) h += y, base -= y * raster, y = 0;
502 check_rect_extent();
503
504 xdev->image.width = raster << 3;
505 xdev->image.height = h;
506 xdev->image.data = (char *)base;
507 xdev->image.bytes_per_line = raster;
508 set_fill_style(FillSolid);
509
510 /* Check for null, easy 1-color, hard 1-color, and 2-color cases. */
511 if ( zero != gx_no_color_index )
512 { if ( one != gx_no_color_index )
513 { /* 2-color case. */
514 /* Simply replace existing bits with what's in the image. */
515 }
516 else if ( !(~xdev->colors_and & bc) )
517 function = GXand,
518 fc = ~(x_pixel)0;
519 else if ( !(~bc & xdev->colors_or) )
520 function = GXor,
521 fc = 0;
522 else
523 goto hard;
524 }
525 else
526 { if ( one == gx_no_color_index ) /* no-op */
527 return 0;
528 else if ( !(~xdev->colors_and & fc) )
529 function = GXand,
530 bc = ~(x_pixel)0;
531 else if ( !(~fc & xdev->colors_or) )
532 function = GXor,
533 bc = 0;
534 else
535 goto hard;
536 }
537 xdev->image.format = XYBitmap;
538 set_function(function);
539 if ( bc != xdev->back_color )
540 XSetBackground(xdev->dpy, xdev->gc, (xdev->back_color = bc));
541 if ( fc != xdev->fore_color )
542 XSetForeground(xdev->dpy, xdev->gc, (xdev->fore_color = fc));
543 if ( zero != gx_no_color_index )
544 note_color(p_zero);
545 if ( one != gx_no_color_index )
546 note_color(p_one);
547 put_image(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
548 sourcex, 0, x, y, w, h);
549
550 goto out;
551
552hard: /* Handle the hard 1-color case. */
553 if ( raster > xdev->cp.raster || h > xdev->cp.height )
554 { /* Must allocate a new pixmap and GC. */
555 /* Release the old ones first. */
556 free_cp(dev);
557
558 /* Create the clipping pixmap, depth must be 1. */
559 xdev->cp.pixmap =
560 XCreatePixmap(xdev->dpy, xdev->win, raster << 3, h, 1);
561 if ( xdev->cp.pixmap == (Pixmap)0 )
562 { lprintf("x_copy_mono: can't allocate pixmap\n");
563 exit(1);
564 }
565 xdev->cp.gc = XCreateGC(xdev->dpy, xdev->cp.pixmap, 0, 0);
566 if ( xdev->cp.gc == (GC)0 )
567 { lprintf("x_copy_mono: can't allocate GC\n");
568 exit(1);
569 }
570 xdev->cp.raster = raster;
571 xdev->cp.height = h;
572 }
573
574 /* Initialize static mask image params */
575 xdev->image.format = ZPixmap;
576
577 /* Select polarity based on fg/bg transparency. */
578 if ( one == gx_no_color_index ) /* invert */
579 { XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel)1);
580 XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel)0);
581 set_fore_color(p_zero);
582 }
583 else
584 { XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel)0);
585 XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel)1);
586 set_fore_color(p_one);
587 }
588 put_image(xdev->dpy, xdev->cp.pixmap, xdev->cp.gc,
589 &xdev->image, sourcex, 0, 0, 0, w, h);
590
591 /* Install as clipmask. */
592 XSetClipMask(xdev->dpy, xdev->gc, xdev->cp.pixmap);
593 XSetClipOrigin(xdev->dpy, xdev->gc, x, y);
594
595 /*
596 * Draw a solid rectangle through the raster clip mask.
597 * Note fill style is guaranteed to be solid from above.
598 */
599 XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
600
601 /* Tidy up. Free the pixmap if it's big. */
602 XSetClipMask(xdev->dpy, xdev->gc, None);
603 if ( raster * h > max_temp_pixmap )
604 free_cp(dev);
605
606out: if ( xdev->bpixmap != (Pixmap)0 )
607 { /* We wrote to the pixmap, so update the display now. */
608 update_add(dev, x, y, w, h);
609 }
610
611 return 0;
612}
613
614/* Internal routine to free the GC and pixmap used for copying. */
615private void
616free_cp(register gx_device *dev)
617{ if ( xdev->cp.gc != NULL )
618 { XFreeGC(xdev->dpy, xdev->cp.gc);
619 xdev->cp.gc = NULL;
620 }
621 if ( xdev->cp.pixmap != (Pixmap)0 )
622 { XFreePixmap(xdev->dpy, xdev->cp.pixmap);
623 xdev->cp.pixmap = (Pixmap)0;
624 }
625 xdev->cp.raster = -1; /* mark as unallocated */
626}
627
628/* Copy a "color" bitmap. Since "color" is the same as monochrome, */
629/* this just reduces to copying a monochrome bitmap. */
630/****** THIS ROUTINE IS COMPLETELY WRONG, SINCE WE DO SUPPORT COLOR. ******/
631/* Fortunately, no one uses it at the moment. */
632private int
633x_copy_color(register gx_device *dev,
634 byte *base, int sourcex, int raster, gx_bitmap_id id,
635 int x, int y, int w, int h)
636{ return x_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
637 pixel_to_color_index(pixel_black),
638 pixel_to_color_index(pixel_white));
639}
640
641/* Draw a line */
642private int
643x_draw_line(register gx_device *dev,
644 int x0, int y0, int x1, int y1, gx_color_index color)
645{ set_fore_color(color_index_to_pixel(color));
646 set_fill_style(FillSolid);
647 set_function(GXcopy);
648 XDrawLine(xdev->dpy, xdev->dest, xdev->gc, x0, y0, x1, y1);
649 if ( xdev->bpixmap != (Pixmap)0 )
650 { int x = x0, y = y0, w = x1 - x0, h = y1 - y0;
651 if ( w < 0 ) x = x1, w = - w;
652 if ( h < 0 ) y = y1, h = - h;
653 w++; h++;
654 check_rect();
655 update_add(dev, x, y, w, h);
656 }
657 return 0;
658}
659
660/* ------ Screen update procedures ------ */
661
662/* Flush updates to the screen if needed. */
663private void
664update_do_flush(register gx_device *dev)
665{ int xo = xdev->update.xo, yo = xdev->update.yo;
666 set_function(GXcopy);
667 XCopyArea(xdev->dpy, xdev->bpixmap, xdev->win, xdev->gc,
668 xo, yo, xdev->update.xe - xo, xdev->update.ye - yo,
669 xo, yo);
670 update_init(dev);
671}
672
673/* Add a region to be updated. */
674/* This is only called if xdev->bpixmap != 0. */
675private void
676update_add(register gx_device *dev, int xo, int yo, int w, int h)
677{ int xe = xo + w, ye = yo + h;
678 long new_area = (long)w * h;
679 ++xdev->up_count;
680 if ( xdev->up_area != 0 )
681 { /* See whether adding this rectangle */
682 /* would result in too much being copied unnecessarily. */
683 long old_area = xdev->up_area;
684 long new_up_area;
685 rect u;
686 u.xo = min(xo, xdev->update.xo);
687 u.yo = min(yo, xdev->update.yo);
688 u.xe = max(xe, xdev->update.xe);
689 u.ye = max(ye, xdev->update.ye);
690 new_up_area = (long)(u.xe - u.xo) * (u.ye - u.yo);
691 if ( new_up_area > 100 &&
692 old_area + new_area < new_up_area * 2 / 3 ||
693 xdev->up_count >= 200
694 )
695 update_do_flush(dev);
696 else
697 { xdev->update = u;
698 xdev->up_area = new_up_area;
699 return;
700 }
701 }
702 xdev->update.xo = xo;
703 xdev->update.yo = yo;
704 xdev->update.xe = xe;
705 xdev->update.ye = ye;
706 xdev->up_area = new_area;
707}
708
709/* ------ Internal procedures ------ */
710
711/* Substitute for XPutImage using XFillRectangle. */
712/* This is a total hack to get around an apparent bug */
713/* in some X servers. It only works with the specific */
714/* parameters (bit/byte order, padding) used above. */
715private void
716alt_put_image(gx_device *dev, Display *dpy, Drawable win, GC gc,
717 XImage *pi, int sx, int sy, int dx, int dy, unsigned w, unsigned h)
718{ int raster = pi->bytes_per_line;
719 byte *data = (byte *)pi->data + sy * raster + (sx >> 3);
720 int init_mask = 0x80 >> (sx & 7);
721 int invert;
722 int yi;
723#define nrects 40
724 XRectangle rects[nrects];
725 XRectangle *rp = rects;
726 if ( xdev->fore_color != gx_no_color_index )
727 { if ( xdev->back_color != gx_no_color_index )
728 { XSetForeground(dpy, gc, xdev->back_color);
729 XFillRectangle(dpy, win, gc, dx, dy, w, h);
730 }
731 XSetForeground(dpy, gc, xdev->fore_color);
732 invert = 0;
733 }
734 else if ( xdev->back_color != gx_no_color_index )
735 { XSetForeground(dpy, gc, xdev->back_color);
736 invert = 0xff;
737 }
738 else
739 return;
740 for ( yi = 0; yi < h; yi++, data += raster )
741 { register int mask = init_mask;
742 register byte *dp = data;
743 register int xi = 0;
744 while ( xi < w )
745 { if ( (*dp ^ invert) & mask )
746 { int xleft = xi;
747 if ( rp == &rects[nrects] )
748 { XFillRectangles(dpy, win, gc, rects, nrects);
749 rp = rects;
750 }
751 /* Scan over a run of 1-bits */
752 rp->x = dx + xi, rp->y = dy + yi;
753 do
754 { if ( !(mask >>= 1) ) mask = 0x80, dp++;
755 xi++;
756 }
757 while ( xi < w && (*dp & mask) );
758 rp->width = xi - xleft, rp->height = 1;
759 rp++;
760 }
761 else
762 { if ( !(mask >>= 1) ) mask = 0x80, dp++;
763 xi++;
764 }
765 }
766 }
767 XFillRectangles(dpy, win, gc, rects, rp - rects);
768}