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