Commit | Line | Data |
---|---|---|
769a6dd7 WJ |
1 | /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
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 */ | |
32 | private int use_XPutImage = 1; | |
33 | private 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!) */ | |
38 | private int max_temp_pixmap = 20000; | |
39 | ||
40 | /* Forward references */ | |
41 | private int set_tile(P2(gx_device *, gx_bitmap *)); | |
42 | private 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) | |
49 | private void update_do_flush(P1(gx_device *)); | |
50 | private void update_add(P5(gx_device *, int, int, int, int)); | |
51 | private void send_event(P2(gx_device *, Atom)); | |
52 | ||
53 | /* Procedures */ | |
54 | ||
55 | extern int gdev_x_open(P1(gx_device_X *)); | |
56 | private dev_proc_open_device(x_open); | |
57 | private dev_proc_get_initial_matrix(x_get_initial_matrix); | |
58 | private dev_proc_sync_output(x_sync); | |
59 | private dev_proc_output_page(x_output_page); | |
60 | private dev_proc_close_device(x_close); | |
61 | private dev_proc_map_rgb_color(x_map_rgb_color); | |
62 | private dev_proc_map_color_rgb(x_map_color_rgb); | |
63 | private dev_proc_fill_rectangle(x_fill_rectangle); | |
64 | private dev_proc_tile_rectangle(x_tile_rectangle); | |
65 | private dev_proc_copy_mono(x_copy_mono); | |
66 | private dev_proc_copy_color(x_copy_color); | |
67 | private dev_proc_draw_line(x_draw_line); | |
68 | ||
69 | /* The device descriptor */ | |
70 | private 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. */ | |
89 | gx_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. */ | |
165 | private 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. */ | |
172 | private int | |
173 | x_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. */ | |
181 | private int | |
182 | x_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. */ | |
191 | private gx_color_index | |
192 | x_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. */ | |
229 | private int | |
230 | x_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 */ | |
278 | private void | |
279 | x_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 */ | |
289 | private int | |
290 | x_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 */ | |
297 | private void | |
298 | send_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" */ | |
311 | private int | |
312 | x_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. */ | |
330 | private int | |
331 | x_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 | |
349 | if ( 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. */ | |
357 | private int | |
358 | x_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 | |
403 | if ( 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. */ | |
412 | private int | |
413 | set_tile(register gx_device *dev, register gx_bitmap *tile) | |
414 | { | |
415 | #ifdef DEBUG | |
416 | if ( 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 | |
446 | if ( 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. */ | |
465 | private int | |
466 | x_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 | ||
552 | hard: /* 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 | ||
606 | out: 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. */ | |
615 | private void | |
616 | free_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. */ | |
632 | private int | |
633 | x_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 */ | |
642 | private int | |
643 | x_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. */ | |
663 | private void | |
664 | update_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. */ | |
675 | private void | |
676 | update_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. */ | |
715 | private void | |
716 | alt_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 | } |