1 /*
2 * A Z-Machine
3 * Copyright (C) 2000 Andrew Hunter
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /*
21 * Routines for turning images into XImages
22 *
23 * Groan, moan, grah, mutter, X.
24 */
25
26 /*
27 * Translated, that means that dealing with X format Images is a pain
28 * in the arse. You can use the X-supplied routines, but that doesn't
29 * solve the problem of calculating pixels, and is dog slow. So, we
30 * have this rather nasty piece of code to deal with the various ways
31 * we can create these images.
32 *
33 * If the X consortium knew what they were up to, they might have
34 * provided a simple RGB image format, and required servers to support
35 * it. But they didn't. They provided us with a crappy image format
36 * and verrry slow functions for accessing it.
37 */
38
39 #include "../config.h"
40
41 #if WINDOW_SYSTEM == 1
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "ztypes.h"
48 #include "zmachine.h"
49 #include "image.h"
50 #include "image_ximage.h"
51
52 #include <X11/Xlib.h>
53
54 #ifdef HAVE_XRENDER
55 # include <X11/extensions/Xrender.h>
56 #endif
57
58 struct x_data
59 {
60 Display* display;
61
62 XImage* image;
63 XImage* mask;
64
65 #ifdef HAVE_XRENDER
66 XImage* render;
67
68 /*
69 Pixmap pmap;
70 Picture piccy;
71 */
72 #endif
73 };
74
75 /*
76 * 16 or 32-bit truecolour images.
77 */
bottombit(unsigned long mask)78 static inline int bottombit(unsigned long mask)
79 {
80 int bit;
81
82 bit = 3000;
83
84 if ((mask&0xf))
85 bit = 0;
86 else if ((mask&0xf0))
87 bit = 4;
88 else if ((mask&0xf00))
89 bit = 8;
90 else if ((mask&0xf000))
91 bit = 12;
92 else if ((mask&0xf0000))
93 bit = 16;
94 else if ((mask&0xf00000))
95 bit = 20;
96 else if ((mask&0xf000000))
97 bit = 24;
98 else
99 bit = 28;
100 mask >>= bit;
101 mask &= 0xf;
102
103 if ((mask&0x1))
104 return bit;
105 else if ((mask&0x2))
106 return bit+1;
107 else if ((mask&0x4))
108 return bit+2;
109 else if ((mask&0x8))
110 return bit+3;
111
112 return 0;
113 }
114
topbit(unsigned long mask)115 static inline int topbit(unsigned long mask)
116 {
117 int bit;
118
119 if (mask >= 0x10000000)
120 bit = 28;
121 else if (mask >= 0x1000000)
122 bit = 24;
123 else if (mask >= 0x100000)
124 bit = 20;
125 else if (mask >= 0x10000)
126 bit = 16;
127 else if (mask >= 0x1000)
128 bit = 12;
129 else if (mask >= 0x100)
130 bit = 8;
131 else if (mask >= 0x10)
132 bit = 4;
133 else
134 bit = 0;
135
136 mask >>= bit;
137
138 if ((mask&0x8))
139 return bit+3;
140 else if ((mask&0x4))
141 return bit+2;
142 else if ((mask&0x2))
143 return bit+1;
144 else if ((mask&0x1))
145 return bit;
146
147 return 0;
148 }
149
150 #ifdef HAVE_XRENDER
151 static XRenderPictFormat* format = NULL;
152
image_to_ximage_render(image_data * img,Display * display,Visual * visual)153 XImage* image_to_ximage_render(image_data* img,
154 Display* display,
155 Visual* visual)
156 {
157 XImage* xim;
158
159 int rshift, gshift, bshift, ashift;
160 int rshift2, gshift2, bshift2, ashift2;
161
162 int width, height;
163
164 int x,y;
165 unsigned char* imgdata;
166
167 int bytes_per_pixel;
168
169 /* Find a suitable format... */
170 if (format == NULL)
171 {
172 XRenderPictFormat pf;
173
174 pf.depth = 32;
175 pf.type = PictTypeDirect;
176
177 format = XRenderFindFormat(display,
178 PictFormatType|PictFormatDepth,
179 &pf, 0);
180 if (format == NULL)
181 {
182 zmachine_fatal("Unable to find a suitable format for XRender");
183 return NULL;
184 }
185 }
186
187 width = image_width(img);
188 height = image_height(img);
189
190 /* Create an XImage of that format... */
191 xim = XCreateImage(display, visual,
192 format->depth,
193 ZPixmap,
194 0, NULL,
195 width, height,
196 32,
197 0);
198
199 /* This algorithm limits us to byte-boundaries, and a maximum of 32bpp */
200 if (xim->bits_per_pixel != 16 &&
201 xim->bits_per_pixel != 24 &&
202 xim->bits_per_pixel != 32)
203 {
204 zmachine_warning("Unable to anything useful with your display: switch to a 16- or 32-bpp display (images won't display)");
205 return xim;
206 }
207
208 /* Work out the shifts required to build our image... */
209 rshift = format->direct.red;
210 gshift = format->direct.green;
211 bshift = format->direct.blue;
212 ashift = format->direct.alpha;
213
214 /*
215 * ... I think. Damn Xrender spec is ambiguous on the meanings of the
216 * masks (and it's trivial for 8888 formats)
217 */
218 rshift2 = 8 - (topbit(format->direct.redMask)+1);
219 gshift2 = 8 - (topbit(format->direct.greenMask)+1);
220 bshift2 = 8 - (topbit(format->direct.blueMask)+1);
221 ashift2 = 8 - (topbit(format->direct.alphaMask)+1);
222
223 /* Allocate image data */
224 xim->data = malloc(xim->bytes_per_line*xim->height);
225
226 imgdata = image_rgb(img);
227 bytes_per_pixel = xim->bits_per_pixel/8;
228
229 /* Two iterators, depending on byte order */
230 if (xim->byte_order == LSBFirst)
231 {
232 for (y=0; y<height; y++)
233 {
234 /* Line iterator */
235 unsigned char* row;
236
237 row = xim->data;
238 row += y * xim->bytes_per_line;
239
240 for (x=0; x<width; x++)
241 {
242 /* Row iterator */
243 long int pixel;
244 int z;
245
246 pixel =
247 ((imgdata[0]>>rshift2)<<rshift)|
248 ((imgdata[1]>>gshift2)<<gshift)|
249 ((imgdata[2]>>bshift2)<<bshift)|
250 ((imgdata[3]>>ashift2)<<ashift);;
251
252 imgdata += 4;
253
254 for (z=0; z<bytes_per_pixel; z++)
255 {
256 *(row++) = pixel;
257 pixel >>= 8;
258 }
259 }
260 }
261 }
262 else
263 {
264 int s;
265
266 s = bytes_per_pixel-1;
267
268 for (y=0; y<height; y++)
269 {
270 /* Line iterator */
271 unsigned char* row;
272
273 row = xim->data;
274 row += y * xim->bytes_per_line;
275
276 for (x=0; x<width; x++)
277 {
278 /* Row iterator */
279 long int pixel;
280 int z;
281
282 pixel =
283 ((imgdata[0]>>rshift2)<<rshift)|
284 ((imgdata[1]>>gshift2)<<gshift)|
285 ((imgdata[2]>>bshift2)<<bshift)|
286 ((imgdata[3]>>ashift2)<<ashift);
287
288 imgdata += 4;
289
290 for (z=s; z>=0; z--)
291 {
292 row[z] = pixel;
293 pixel >>= 8;
294 }
295 row += bytes_per_pixel;
296 }
297 }
298 }
299
300 return xim;
301 }
302 #endif
303
image_to_ximage_truecolour(image_data * img,Display * display,Visual * visual)304 XImage* image_to_ximage_truecolour(image_data* img,
305 Display* display,
306 Visual* visual)
307 {
308 XImage* xim;
309 int depth;
310
311 int rshift, gshift, bshift;
312 int rshift2, gshift2, bshift2;
313
314 int width, height;
315
316 int x,y;
317 unsigned char* imgdata;
318
319 int bytes_per_pixel;
320
321 depth = DefaultDepth(display, (DefaultScreen(display)));
322
323 width = image_width(img);
324 height = image_height(img);
325
326 xim = XCreateImage(display, visual,
327 depth,
328 ZPixmap,
329 0, NULL,
330 width, height,
331 32,
332 0);
333
334 /*
335 * People with 15-bit displays that really *are* 15-bit can go stuff
336 * themselves (or they could write a new imaging routine to sort out
337 * their problems)
338 */
339 if (xim->bits_per_pixel != 16 &&
340 xim->bits_per_pixel != 24 &&
341 xim->bits_per_pixel != 32)
342 {
343 zmachine_warning("Unable to anything useful with your display: switch to a 16- or 32-bpp display (images won't display)");
344 return xim;
345 }
346
347 /* Work out the shifts required to build our image... */
348 rshift = bottombit(xim->red_mask);
349 gshift = bottombit(xim->green_mask);
350 bshift = bottombit(xim->blue_mask);
351
352 rshift2 = 8 - (topbit(xim->red_mask)+1-rshift);
353 gshift2 = 8 - (topbit(xim->green_mask)+1-gshift);
354 bshift2 = 8 - (topbit(xim->blue_mask)+1-bshift);
355
356 /* Allocate image data */
357 xim->data = malloc(xim->bytes_per_line * xim->height);
358
359 imgdata = image_rgb(img);
360 bytes_per_pixel = xim->bits_per_pixel/8;
361
362 /* Two iterators, depending on byte order */
363 if (xim->byte_order == LSBFirst)
364 {
365 for (y=0; y<height; y++)
366 {
367 /* Line iterator */
368 unsigned char* row;
369
370 row = xim->data;
371 row += y * xim->bytes_per_line;
372
373 for (x=0; x<width; x++)
374 {
375 /* Row iterator */
376 long int pixel;
377 int z;
378
379 pixel =
380 ((imgdata[0]>>rshift2)<<rshift)|
381 ((imgdata[1]>>gshift2)<<gshift)|
382 ((imgdata[2]>>bshift2)<<bshift);
383
384 imgdata += 4;
385
386 for (z=0; z<bytes_per_pixel; z++)
387 {
388 *(row++) = pixel;
389 pixel >>= 8;
390 }
391 }
392 }
393 }
394 else
395 {
396 int s;
397
398 s = bytes_per_pixel-1;
399
400 for (y=0; y<height; y++)
401 {
402 /* Line iterator */
403 unsigned char* row;
404
405 row = xim->data;
406 row += y * xim->bytes_per_line;
407
408 for (x=0; x<width; x++)
409 {
410 /* Row iterator */
411 long int pixel;
412 int z;
413
414 pixel =
415 ((imgdata[0]>>rshift2)<<rshift)|
416 ((imgdata[1]>>gshift2)<<gshift)|
417 ((imgdata[2]>>bshift2)<<bshift);
418
419 imgdata += 4;
420
421 for (z=s; z>=0; z--)
422 {
423 row[z] = pixel;
424 pixel >>= 8;
425 }
426 row += bytes_per_pixel;
427 }
428 }
429 }
430
431 return xim;
432 }
433
image_to_mask_truecolour(XImage * orig,image_data * img,Display * display,Visual * visual)434 XImage* image_to_mask_truecolour(XImage* orig,
435 image_data* img,
436 Display* display,
437 Visual* visual)
438 {
439 XImage* xim;
440
441 int depth, width, height;
442 int bytes_per_pixel;
443
444 int x,y;
445
446 unsigned char* imgdata;
447
448 depth = DefaultDepth(display, DefaultScreen(display));
449
450 width = image_width(img);
451 height = image_height(img);
452
453 xim = XCreateImage(display, visual,
454 depth,
455 ZPixmap,
456 0, NULL,
457 width, height,
458 32,
459 0);
460
461 if (xim->bits_per_pixel != 16 &&
462 xim->bits_per_pixel != 24 &&
463 xim->bits_per_pixel != 32)
464 {
465 zmachine_warning("Unable to anything useful with your display: switch to a 16- or 32-bpp display (images won't display)");
466 return xim;
467 }
468
469 xim->data = malloc(xim->bytes_per_line * xim->height);
470
471 imgdata = image_rgb(img);
472 bytes_per_pixel = xim->bits_per_pixel/8;
473
474 for (y=0; y<height; y++)
475 {
476 unsigned char* row, *oldrow;
477
478 row = xim->data + (y*xim->bytes_per_line);
479 oldrow = orig->data + (y*orig->bytes_per_line);
480
481 for (x=0; x<width; x++)
482 {
483 int z;
484
485 if (imgdata[3] > 128)
486 {
487 for (z=0; z<bytes_per_pixel; z++)
488 {
489 *(row++) = 0;
490 oldrow++;
491 }
492 }
493 else
494 {
495 for (z=0; z<bytes_per_pixel; z++)
496 {
497 *(row++) = 255;
498 *(oldrow++) = 0;
499 }
500 }
501
502 imgdata += 4;
503 }
504 }
505
506 return xim;
507 }
508
x_destruct(image_data * img,void * data)509 static void x_destruct(image_data* img, void* data)
510 {
511 struct x_data* d;
512
513 d = data;
514
515 if (d->image != NULL)
516 {
517 free(d->image->data);
518 d->image->data = NULL;
519 XDestroyImage(d->image);
520 }
521 if (d->mask != NULL)
522 {
523 free(d->mask->data);
524 d->mask->data = NULL;
525 XDestroyImage(d->mask);
526 }
527 #ifdef HAVE_XRENDER
528 if (d->render != NULL)
529 {
530 free(d->render->data);
531 d->render->data = NULL;
532 XDestroyImage(d->render);
533 }
534 #endif
535
536 free(d);
537 }
538
image_plot_X(image_data * img,Display * display,Drawable draw,GC gc,int x,int y,int n,int d)539 void image_plot_X(image_data* img,
540 Display* display,
541 Drawable draw,
542 GC gc,
543 int x, int y,
544 int n, int d)
545 {
546 struct x_data* data;
547
548 data = image_get_data(img);
549
550 if (data == NULL)
551 {
552 data = malloc(sizeof(struct x_data));
553 data->image = NULL;
554 data->mask = NULL;
555 data->display = display;
556
557 #ifdef HAVE_XRENDER
558 data->render = NULL;
559 #endif
560
561 image_set_data(img, data, x_destruct);
562 }
563
564 if (data->image == NULL)
565 {
566 image_unload_rgb(img);
567 if (n != d)
568 image_resample(img, n, d);
569
570 data->image = image_to_ximage_truecolour(img,
571 display,
572 DefaultVisual(display, DefaultScreen(display)));
573 }
574 if (data->mask == NULL)
575 {
576 data->mask = image_to_mask_truecolour(data->image,
577 img, display,
578 DefaultVisual(display, DefaultScreen(display)));
579 }
580 image_unload_rgb(img);
581
582 XSetFunction(display, gc, GXand);
583 XPutImage(display, draw, gc, data->mask, 0,0,x,y,
584 image_width(img), image_height(img));
585 XSetFunction(display, gc, GXor);
586 XPutImage(display, draw, gc, data->image, 0,0,x,y,
587 image_width(img), image_height(img));
588 XSetFunction(display, gc, GXcopy);
589 }
590
591 #ifdef HAVE_XRENDER
592 #define RENDER_TILE 200
593
594 static Pixmap r_pix;
595 static Picture r_pict;
596
image_plot_Xrender(image_data * img,Display * display,Picture pic,int x,int y,int n,int d)597 void image_plot_Xrender(image_data* img,
598 Display* display,
599 Picture pic,
600 int x, int y,
601 int n, int d)
602 {
603 struct x_data* data;
604 int xpos, ypos;
605
606 GC agc;
607
608 data = image_get_data(img);
609
610 if (data == NULL)
611 {
612 data = malloc(sizeof(struct x_data));
613 data->image = NULL;
614 data->mask = NULL;
615 data->display = display;
616
617 data->render = NULL;
618
619 image_set_data(img, data, x_destruct);
620 }
621
622 /* Get the format if necessary */
623 if (format == NULL)
624 {
625 XRenderPictFormat pf;
626
627 pf.depth = 32;
628 pf.type = PictTypeDirect;
629
630 format = XRenderFindFormat(display,
631 PictFormatType|PictFormatDepth,
632 &pf, 0);
633 if (format == NULL)
634 {
635 zmachine_fatal("Unable to find a suitable format for XRender");
636 return;
637 }
638 }
639
640 /* Render the image, if necessary */
641 if (data->render == NULL)
642 {
643 image_unload_rgb(img);
644 if (n != d)
645 image_resample(img, n, d);
646
647 data->render = image_to_ximage_render(img, display,
648 DefaultVisual(display, DefaultScreen(display)));
649
650 image_unload_rgb(img);
651 }
652
653 /*
654 * Why do we things this roundabout way? Because Xrender (at least
655 * with my Nvidia drivers) goes... odd... with 'large' composites,
656 * so we transfer images a bit at a time. Yup, this sucks a bit for
657 * performance. Live with it, is about the best I can say...
658 */
659 if (r_pix == None)
660 {
661 r_pix = XCreatePixmap(display,
662 RootWindow(display, DefaultScreen(display)),
663 RENDER_TILE, RENDER_TILE,
664 format->depth);
665 }
666 if (r_pict == None)
667 {
668 r_pict = XRenderCreatePicture(display,
669 r_pix,
670 format, 0, 0);
671 }
672
673 agc = XCreateGC(display, r_pix, 0, NULL);
674 XSetFunction(display, agc, GXcopy);
675
676 xpos = 0; ypos = 0;
677 for (xpos = 0; xpos < image_width(img); xpos+=RENDER_TILE)
678 {
679 int w = RENDER_TILE;
680
681 if (xpos + w > image_width(img))
682 w = image_width(img)-xpos;
683
684 for (ypos = 0; ypos < image_height(img); ypos+=RENDER_TILE)
685 {
686 int h = RENDER_TILE;
687
688 if (ypos + h > image_height(img))
689 h = image_height(img)-ypos;
690
691 XPutImage(display, r_pix, agc,
692 data->render,
693 xpos, ypos, 0,0, w, h);
694 XRenderComposite(display, PictOpOver,
695 r_pict,
696 None,
697 pic,
698 0,0,0,0,
699 x+xpos,y+ypos,
700 w,h);
701 }
702 }
703
704 XFlush(display);
705 XFreeGC(display, agc);
706 }
707 #endif
708
709 #endif
710
711