1 /* The Ace of Penguins - table.c
2    Copyright (C) 1998, 2001 DJ Delorie
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <math.h>
23 
24 #include <png.h>
25 
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/keysym.h>
29 #include <X11/Xatom.h>
30 
31 typedef struct {
32   Pixmap image_pixmap;
33   Pixmap image_mask;
34   Pixmap rotated_pixmap;
35   Pixmap rotated_mask;
36   Pixmap inverted_pixmap;
37 } pixels_type;
38 #define PIXELS_TYPE pixels_type *
39 
40 #define ROT(a,b) if (xrotate) { int t = a; a = b; b = t; }
41 
42 int pixel_for (int r, int g, int b);
43 
44 #include "imagelib.h"
45 #include "cards.h"
46 #include "xwin.h"
47 
48 static char AOP[] = "The Ace of Penguins - ";
49 static char *type_names[] = {"mono", "grey", "color"};
50 
51 int xrotate = 0;
52 int visual_id = 0;
53 OptionDesc xwin_options_list[] = {
54   { "-rotate", OPTION_BOOLEAN, &xrotate },
55   { "-visual", OPTION_INTEGER, &visual_id },
56   { 0, 0, 0 }
57 };
58 OptionDesc *xwin_options = xwin_options_list;
59 
60 Display *display=0;
61 int screen=0;
62 Visual *visual=0;
63 Colormap cmap=0;
64 Window window=0;
65 Window rootwin=0;
66 GC gc=0, imggc=0, maskgc=0;
67 XFontStruct *font;
68 int font_width, font_height;
69 XVisualInfo vi, *vip;
70 
71 static int broken_xserver = 0;
72 
73 static image_list static_display_image_list;
74 static image static_display_image;
75 image *display_image;
76 
77 static Atom wm_protocols_atom=0;
78 static Atom delete_atom=0;
79 static Atom paste_atom=0;
80 static Atom mwm_atom=0;
81 static XEvent event;
82 static XRectangle clip_rect;
83 
84 int table_background;
85 int help_background;
86 
87 #define ButtonMask (Button1Mask | Button2Mask | Button3Mask)
88 
89 /* Motif window hints */
90 typedef struct
91 {
92   unsigned flags;
93   unsigned functions;
94   unsigned decorations;
95   int inputMode;
96 } PropMotifWmHints;
97 
98 typedef PropMotifWmHints        PropMwmHints;
99 
100 /* Motif window hints */
101 #define MWM_HINTS_FUNCTIONS           (1L << 0)
102 #define MWM_HINTS_DECORATIONS         (1L << 1)
103 
104 /* bit definitions for MwmHints.functions */
105 #define MWM_FUNC_ALL            (1L << 0)
106 #define MWM_FUNC_RESIZE         (1L << 1)
107 #define MWM_FUNC_MOVE           (1L << 2)
108 #define MWM_FUNC_MINIMIZE       (1L << 3)
109 #define MWM_FUNC_MAXIMIZE       (1L << 4)
110 #define MWM_FUNC_CLOSE          (1L << 5)
111 
112 /* bit definitions for MwmHints.decorations */
113 #define MWM_DECOR_ALL                 (1L << 0)
114 #define MWM_DECOR_BORDER              (1L << 1)
115 #define MWM_DECOR_RESIZEH             (1L << 2)
116 #define MWM_DECOR_TITLE               (1L << 3)
117 #define MWM_DECOR_MENU                (1L << 4)
118 #define MWM_DECOR_MINIMIZE            (1L << 5)
119 #define MWM_DECOR_MAXIMIZE            (1L << 6)
120 
121 #define PROP_MOTIF_WM_HINTS_ELEMENTS  4
122 #define PROP_MWM_HINTS_ELEMENTS       PROP_MOTIF_WM_HINTS_ELEMENTS
123 
124 
break_here()125 void break_here(){}
126 
127 static char *name;
128 
129 int
xwin_init(int argc,char ** argv)130 xwin_init(int argc, char **argv)
131 {
132   char *sl;
133   XColor color;
134   int i;
135 
136   name = argv[0];
137 
138   atexit(break_here);
139 
140   sl = strrchr(name, '/');
141   if (sl) name = sl+1;
142 
143   display = XOpenDisplay(0);
144   if (display == NULL) {
145     fprintf(stderr, "Error: Can't open display!\n");
146     return 1;
147   };
148   screen = XDefaultScreen(display);
149   rootwin = XDefaultRootWindow(display);
150 
151   /* The Agenda's X server can't use a bitmap for a mask.  If/when it
152      gets fixed, find out the version and update the test below. */
153   if (strcmp(XServerVendor(display), "Keith Packard") == 0)
154     broken_xserver = 1;
155 
156   visual = XDefaultVisual(display, screen);
157   vi.visualid = XVisualIDFromVisual(visual);
158   if (visual_id)
159     vi.visualid = visual_id;
160   vip = XGetVisualInfo (display, VisualIDMask, &vi, &i);
161   if (i != 1)
162     abort();
163   visual = vip->visual;
164 
165   if (visual_id)
166     {
167       cmap = XCreateColormap(display, rootwin, visual, AllocNone);
168     }
169   else
170     cmap = XDefaultColormap(display, screen);
171 
172   gc = XCreateGC(display, rootwin, 0, 0);
173   imggc = XCreateGC(display, rootwin, 0, 0);
174 
175   _Xdebug=999;
176 
177   display_width = DisplayWidth(display, screen);
178   display_height = DisplayHeight(display, screen);
179   ROT(display_width, display_height);
180 
181   switch (vip->class)
182     {
183     case StaticGray:
184     case GrayScale:
185       if (vip->depth == 1)
186 	table_type = TABLE_MONO;
187       else
188 	table_type = TABLE_GRAY;
189       break;
190     case StaticColor:
191     case PseudoColor:
192     case TrueColor:
193     case DirectColor:
194       table_type = TABLE_COLOR;
195       break;
196     }
197 
198   if (vip->class == DirectColor)
199     {
200       int i, scale = 0xffff / ((1<<vip->depth)-1);
201       int step = 1 << (vip->depth - vip->bits_per_rgb);
202       XColor c;
203       for (i=0; i < (1<<vip->depth); i += step)
204 	{
205 	  c.red = c.blue = c.green = i * scale;
206 	  c.pixel = i;
207 	  XStoreColor(display, cmap, &c);
208 	}
209     }
210 
211   wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", 0);
212   delete_atom = XInternAtom(display, "WM_DELETE_WINDOW", 0);
213   paste_atom = XInternAtom(display, "PASTE_DATA", 0);
214   mwm_atom = XInternAtom(display, "_MOTIF_WM_HINTS", 0);
215 
216   table_background = pixel_for(0x00, 0x66, 0x00);
217 
218   font = XLoadQueryFont(display, "6x13bold");
219   if (!font) font = XLoadQueryFont(display, "6x10");
220   if (!font) font = XLoadQueryFont(display, "fixed");
221   font_width = font->max_bounds.width;
222   font_height = font->ascent + font->descent;
223 
224   return 0;
225 }
226 
227 void
xwin_create(int width,int height)228 xwin_create(int width, int height)
229 {
230   char *sl;
231   XSizeHints size_hints;
232   XTextProperty xtp;
233   XSetWindowAttributes attributes;
234 
235   ROT(width, height);
236 
237   size_hints.flags = PSize;
238   size_hints.width = width;
239   size_hints.height = height;
240   size_hints.x = 0;
241   size_hints.y = 0;
242 #if 0
243   size_hints.min_width = width;
244   size_hints.min_height = height;
245   size_hints.max_width = width;
246   size_hints.max_height = height;
247 #endif
248 
249   attributes.colormap = cmap;
250   window = XCreateWindow(display,
251 			 rootwin,
252 			 size_hints.x, size_hints.y,
253 			 size_hints.width, size_hints.height,
254 			 0,
255 			 vip->depth,
256 			 InputOutput,
257 			 visual,
258 			 CWColormap, &attributes);
259 
260   XSetWMNormalHints(display, window, &size_hints);
261 
262   sl = (char *)malloc(strlen(name) + strlen(AOP)+1);
263   sprintf(sl, "%s%s", AOP, name);
264   XStringListToTextProperty(&sl, 1, &xtp);
265   XSetWMName(display, window, &xtp);
266   XFree(xtp.value);
267 
268   XSetWMProtocols(display, window, &delete_atom, 1);
269 
270   attributes.event_mask = (  ExposureMask
271 			   | ButtonPressMask
272 			   | ButtonReleaseMask
273 			   | ButtonMotionMask
274 			   | KeyPressMask
275 			   | StructureNotifyMask
276 			   | PointerMotionHintMask );
277   XChangeWindowAttributes(display, window, CWEventMask, &attributes);
278 
279   display_image = &static_display_image;
280   if (xrotate)
281     {
282       static_display_image.width = height;
283       static_display_image.height = width;
284     }
285   else
286     {
287       static_display_image.width = width;
288       static_display_image.height = height;
289     }
290   static_display_image.list = &static_display_image_list;
291   static_display_image.pixels = (pixels_type *) malloc (sizeof(pixels_type));
292   static_display_image.pixels->image_pixmap = (Pixmap)window;
293   static_display_image.pixels->image_mask = 0;
294 
295   static_display_image_list.name = "X Window";
296   static_display_image_list.across = 1;
297   static_display_image_list.down = 1;
298 
299   XMapWindow(display, window);
300   XFlush(display);
301 }
302 
303 void
xwin_fixed_size(int width,int height)304 xwin_fixed_size (int width, int height)
305 {
306   XSizeHints size_hints;
307   PropMwmHints mwm_hints;
308 
309   ROT(width, height);
310 
311   XResizeWindow(display, window, width, height);
312 
313   size_hints.flags = PMinSize|PMaxSize;
314   size_hints.min_width = width;
315   size_hints.min_height = height;
316   size_hints.max_width = width;
317   size_hints.max_height = height;
318   XSetWMNormalHints(display, window, &size_hints);
319 
320   mwm_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
321   mwm_hints.functions = MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE;
322   mwm_hints.decorations = MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE;
323   XChangeProperty(display, window, mwm_atom, mwm_atom, 32, PropModeReplace,
324 		  (char *)&mwm_hints, PROP_MWM_HINTS_ELEMENTS);
325 }
326 
327 static struct {
328   KeySym sym;
329   int key;
330 } key_mappings[] = {
331   { XK_F1,		KEY_F(1) },
332   { XK_F2,		KEY_F(2) },
333   { XK_F3,		KEY_F(3) },
334   { XK_F4,		KEY_F(4) },
335   { XK_F5,		KEY_F(5) },
336   { XK_F6,		KEY_F(6) },
337   { XK_F7,		KEY_F(7) },
338   { XK_F8,		KEY_F(8) },
339   { XK_F9,		KEY_F(9) },
340   { XK_F10,		KEY_F(10) },
341   { XK_F11,		KEY_F(11) },
342   { XK_F12,		KEY_F(12) },
343   { XK_Delete,		KEY_DELETE },
344   { XK_KP_Delete,	KEY_DELETE },
345   { XK_BackSpace,	KEY_DELETE },
346   { XK_Up,		KEY_UP },
347   { XK_KP_Up,		KEY_UP },
348   { XK_Down,		KEY_DOWN },
349   { XK_KP_Down,		KEY_DOWN },
350   { XK_Left,		KEY_LEFT },
351   { XK_KP_Left,		KEY_LEFT },
352   { XK_Right,		KEY_RIGHT },
353   { XK_KP_Right,	KEY_RIGHT },
354   { XK_Page_Up,		KEY_PGUP },
355   { XK_KP_Page_Up,	KEY_PGUP },
356   { XK_Page_Down,	KEY_PGDN },
357   { XK_KP_Page_Down,	KEY_PGDN },
358   { XK_Home,		KEY_HOME },
359   { XK_KP_Home,		KEY_HOME }
360 };
361 #define NUM_KEYMAPS (sizeof(key_mappings)/sizeof(key_mappings[0]))
362 
363 #define WROT(x,y,w,h) if (xrotate) { int t = x; x = table_width-y-h; y = t; \
364 				     t = w; w = h; h = t; }
365 #define PROT(x,y) if (xrotate) { int t = x; x = table_width-y; y = t; }
366 #define WROT2(x,y,w,h) if (xrotate) { int t = y; y = table_width-x-w; x = t; \
367 				     t = w; w = h; h = t; }
368 #define PROT2(x,y) if (xrotate) { int t = y; y = table_width-x; x = t; }
369 
370 int
xwin_nextevent(XWin_Event * ev)371 xwin_nextevent (XWin_Event *ev)
372 {
373   int root_x, root_y, pos_x, pos_y;
374   unsigned int keys_buttons;
375   int i;
376   Window root, child;
377   KeySym keysym;
378   char c;
379   static int click_button;
380   static int last_resize_w=-1, last_resize_h=-1;
381   static int need_expose = 0;
382   static int window_is_mapped = 0;
383 
384   if (need_expose && window_is_mapped)
385     {
386       ev->type = ev_expose;
387       ev->x = 0;
388       ev->y = 0;
389       ev->w = last_resize_w;
390       ev->h = last_resize_h;
391       need_expose = 0;
392       return ev_expose;
393     }
394 
395   while (1)
396   {
397     XNextEvent(display, &event);
398     if (event.xany.window == window)
399     {
400       switch (event.type)
401       {
402       case ConfigureNotify:
403 	ev->type = ev_resize;
404 	WROT (event.xconfigure.x, event.xconfigure.y,
405 	      event.xconfigure.width, event.xconfigure.height);
406 	ev->x = event.xconfigure.x;
407 	ev->y = event.xconfigure.y;
408 	ev->w = event.xconfigure.width;
409 	ev->h = event.xconfigure.height;
410 	if (ev->w == last_resize_w && ev->h == last_resize_h)
411 	  break;
412 	last_resize_w = ev->w;
413 	last_resize_h = ev->h;
414 	need_expose = 1;
415 	return ev_resize;
416 
417       case Expose:
418 	ev->type = ev_expose;
419 	WROT (event.xexpose.x, event.xexpose.y,
420 	      event.xexpose.width, event.xexpose.height);
421 	ev->x = event.xexpose.x;
422 	ev->y = event.xexpose.y;
423 	ev->w = event.xexpose.width;
424 	ev->h = event.xexpose.height;
425 	window_is_mapped = 1;
426 	need_expose = 0;
427 	return ev_expose;
428 
429       case ButtonPress:
430 	ev->type = ev_buttondown;
431 	PROT(event.xbutton.x, event.xbutton.y);
432 	ev->x = event.xbutton.x;
433 	ev->y = event.xbutton.y;
434 	click_button = event.xbutton.button;
435 	if (event.xbutton.state & ShiftMask)
436 	  click_button ++;
437 	ev->button = click_button;
438 	ev->time = event.xbutton.time;
439 	ev->shifts = 0;
440 	return ev_buttondown;
441 
442       case MotionNotify:
443 
444 	while (XCheckMaskEvent(display, ButtonMotionMask, &event));
445 	if (!XQueryPointer(display, event.xmotion.window,
446 			   &root, &child, &root_x, &root_y,
447 			   &pos_x, &pos_y, &keys_buttons))
448 	  break;
449 	ev->type = ev_motion;
450 	PROT(pos_x, pos_y);
451 	ev->x = pos_x;
452 	ev->y = pos_y;
453 	ev->button = click_button;
454 	ev->time = event.xmotion.time;
455 	return ev_motion;
456 
457       case ButtonRelease:
458 	i = event.xbutton.state & ButtonMask;
459 	if ((i & (i>>1)) == 0)
460 	  {
461 	    ev->type = ev_buttonup;
462 	    PROT(event.xbutton.x, event.xbutton.y);
463 	    ev->x = event.xbutton.x;
464 	    ev->y = event.xbutton.y;
465 	    ev->button = click_button;
466 	    ev->time = event.xbutton.time;
467 	    ev->shifts = 0;
468 	    return ev_buttonup;
469 	  }
470 	break;
471 
472       case KeyPress:
473 
474 	ev->key = 0;
475 	if (XLookupString(&event.xkey, &c, 1, &keysym, 0) == 1)
476 	  ev->key = c;
477 	else
478 	  for (i=0; i<NUM_KEYMAPS; i++)
479 	    if (key_mappings[i].sym == keysym)
480 	      ev->key = key_mappings[i].key;
481 	if (!ev->key)
482 	  break;
483 
484 	ev->type = ev_keypress;
485 	PROT(event.xkey.x, event.xkey.y);
486 	ev->x = event.xkey.x;
487 	ev->y = event.xkey.y;
488 	ev->time = event.xkey.time;
489 	ev->shifts = 0;
490 	return ev_keypress;
491 
492       case ClientMessage:
493 	if (event.xclient.message_type == wm_protocols_atom
494 	    && event.xclient.data.l[0] == delete_atom)
495 	  {
496 	    ev->type = ev_quit;
497 	    return ev_quit;
498 	  }
499 	break;
500       }
501     }
502   }
503 }
504 
505 void
flush()506 flush()
507 {
508   XFlush(display);
509 }
510 
511 void
flushsync()512 flushsync()
513 {
514   XSync(display, False);
515 }
516 
517 void
beep()518 beep()
519 {
520   XBell(display, 0);
521 }
522 
523 static int have_clip = 0;
524 
525 void
xwin_clip(int x,int y,int w,int h)526 xwin_clip (int x, int y, int w, int h)
527 {
528   WROT2(x, y, w, h);
529   clip_rect.x = x;
530   clip_rect.y = y;
531   clip_rect.width = w;
532   clip_rect.height = h;
533   XSetClipRectangles(display, gc, 0, 0, &clip_rect, 1, YXBanded);
534   have_clip = 1;
535 }
536 
537 void
xwin_noclip()538 xwin_noclip ()
539 {
540   if (!have_clip)
541     return;
542   have_clip = 0;
543   XSetClipMask(display, gc, None);
544 }
545 
546 static void
xwin_restore_clip()547 xwin_restore_clip ()
548 {
549   if (have_clip)
550     XSetClipRectangles(display, gc, 0, 0, &clip_rect, 1, YXBanded);
551   else
552     XSetClipMask(display, gc, None);
553 }
554 
555 extern int help_is_showing;
556 
557 void
clear(int x,int y,int w,int h)558 clear(int x, int y, int w, int h)
559 {
560   WROT2(x, y, w, h);
561   XSetForeground(display, gc, help_is_showing ?
562 		 help_background : table_background);
563   XFillRectangle(display, window, gc, x, y, w, h);
564 }
565 
566 void
text(char * t,int x,int y)567 text(char *t, int x, int y)
568 {
569   PROT2(x, y);
570   XSetBackground(display, gc, table_background);
571   XSetForeground(display, gc, pixel_for(255, 255, 255));
572   if (font) XSetFont(display, gc, font->fid);
573   XDrawImageString(display, window, gc, x, y-font->descent, t, strlen(t));
574 }
575 
576 /************************************************************************/
577 /*	Image conversion routines					*/
578 /************************************************************************/
579 
580 static XImage *exemplar_image = 0;
581 
582 static void
png_reader(png_structp png_ptr,png_bytep data,png_uint_32 length)583 png_reader (png_structp png_ptr, png_bytep data, png_uint_32 length)
584 {
585   char **bytes = (char **)png_get_io_ptr (png_ptr);
586   memcpy (data, *bytes, length);
587   *bytes += length;
588 }
589 
590 static png_structp png_ptr;
591 static png_infop info_ptr;
592 static png_uint_32 width, height;
593 static int bit_depth, obit_depth, color_type, interlace_type;
594 static unsigned char *pixel_data;
595 static XImage *ximage, *xmask;
596 
597 static int
shift_for(int depth,int mask)598 shift_for (int depth, int mask)
599 {
600   int v = 1 << (depth-1);
601   int s = 0;
602   while (v < mask)
603     {
604       v <<= 1;
605       s ++;
606     }
607   while (v > mask)
608     {
609       v >>= 1;
610       s --;
611     }
612   return s;
613 }
614 
615 static int
do_shift_mask(int v,int s,int m)616 do_shift_mask (int v, int s, int m)
617 {
618   if (s<0) v >>= -s;
619   if (s>0) v <<= s;
620   return v & m;
621 }
622 
623 static int
do_gamma(int p)624 do_gamma(int p)
625 {
626   static unsigned char *gamma_table = 0;
627   if (gamma_table == 0)
628     {
629       int i;
630       gamma_table = (unsigned char *)malloc(256);
631       for (i=0; i<256; i++)
632 	gamma_table[i] = (unsigned char)(pow((double)i / 255.0, 0.45) * 255.0 + 0.5);
633     }
634   return gamma_table[p];
635 }
636 
637 static int ppixels[6][6][6];
638 #define NO_PIXEL -2
639 static int ppixels_initted = 0;
640 
641 int
pixel_for(int r,int g,int b)642 pixel_for (int r, int g, int b)
643 {
644   static int rs=0, gs=0, bs=0;
645   int p, i;
646 
647   if (table_type != TABLE_COLOR)
648     {
649       p = (r*77+g*150+b*29) >> 8;
650       if (vip->class != StaticGray && vip->class != GrayScale)
651 	p = do_gamma(p);
652       r = g = b = p;
653     }
654   switch (vip->class)
655     {
656     case TrueColor:
657     case DirectColor:
658       if (rs == 0)
659 	{
660 	  rs = shift_for (8, vip->red_mask);
661 	  gs = shift_for (8, vip->green_mask);
662 	  bs = shift_for (8, vip->blue_mask);
663 	}
664       p = 0;
665       p |= do_shift_mask (r, rs, vip->red_mask);
666       p |= do_shift_mask (g, gs, vip->green_mask);
667       p |= do_shift_mask (b, bs, vip->blue_mask);
668       return p;
669 
670     case StaticGray:
671       return (r*77+g*150+b*29) >> (16 - vip->depth);
672 
673     case GrayScale:
674     case StaticColor:
675     case PseudoColor:
676       if (!ppixels_initted)
677 	{
678 	  for (i=0; i<6*6*6; i++)
679 	    ((int *)ppixels)[i] = NO_PIXEL;
680 	  ppixels_initted = 1;
681 	}
682       rs = (r+25) / 51;
683       gs = (g+25) / 51;
684       bs = (b+25) / 51;
685       if (ppixels[rs][gs][bs] == NO_PIXEL)
686 	{
687 	  XColor c;
688 	  c.red = rs * 13107;
689 	  c.green = gs * 13107;
690 	  c.blue = bs * 13107;
691 	  if (XAllocColor (display, cmap, &c))
692 	    {
693 	      ppixels[rs][gs][bs] = c.pixel;
694 	      return c.pixel;
695 	    }
696 	}
697       return ppixels[rs][gs][bs];
698     }
699   fprintf(stderr, "Don't know how to make a pixel!\n");
700   abort();
701 }
702 
703 void
cvt_rgbt(image * img)704 cvt_rgbt (image *img)
705 {
706   int r, g, b, a=255, x, y;
707   unsigned char *pp;
708   int has_alpha = color_type & PNG_COLOR_MASK_ALPHA;
709 
710   pp = pixel_data;
711   for (y=0; y<height; y++)
712     for (x=0; x<width; x++)
713       {
714 	r = *pp++;
715 	g = *pp++;
716 	b = *pp++;
717 	if (has_alpha)
718 	  {
719 	    a = *pp++;
720 	    if (xrotate)
721 	      XPutPixel(xmask, y, width-x-1, a > 128 ? 1 : 0);
722 	    else
723 	      XPutPixel(xmask, x, y, a > 128 ? 1 : 0);
724 	  }
725 	if (xrotate)
726 	  XPutPixel(ximage, y, width-x-1, pixel_for(r, g, b));
727 	else
728 	  XPutPixel(ximage, x, y, pixel_for(r, g, b));
729       }
730 }
731 
732 void
cvt_cpt(image * img)733 cvt_cpt (image *img)
734 {
735   int rs, gs, bs, i, x, y;
736   unsigned char *pp;
737   png_colorp palette;
738   png_bytep trans=0;
739   int num_palette, num_trans=0;
740   unsigned *palette_xcolors;
741   png_color_16p trans_color;
742   char *istrans;
743   /* We have a color palette image, and we need a truecolor image. */
744 
745   png_get_PLTE (png_ptr, info_ptr, &palette, &num_palette);
746   if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
747     png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, &trans_color);
748   istrans = (char *)malloc (num_palette);
749   memset(istrans, ~0, num_palette);
750   for (i=0; i<num_trans; i++)
751     istrans[trans[i]] = 0;
752   palette_xcolors = (unsigned *)malloc(num_palette * sizeof(int));
753   for (i=0; i<num_palette; i++)
754     palette_xcolors[i] = pixel_for(palette[i].red, palette[i].green, palette[i].blue);
755   pp = pixel_data;
756   for (y=0; y<height; y++)
757     for (x=0; x<width; x++)
758       {
759 	int idx = *pp++;
760 	if (bit_depth > 8) idx = idx*256 + *pp++;
761 	if (xrotate)
762 	  {
763 	    XPutPixel(ximage, y, width-x-1, palette_xcolors[idx]);
764 	  }
765 	else
766 	  XPutPixel(ximage, x, y, palette_xcolors[idx]);
767 	if (xmask)
768 	  {
769 	    if (xrotate)
770 	      XPutPixel(xmask, y, width-x-1, istrans[idx]);
771 	    else
772 	      XPutPixel(xmask, x, y, istrans[idx]);
773 	  }
774       }
775 }
776 
777 void
cvt_gt(image * img)778 cvt_gt (image *img)
779 {
780   int rs, gs, bs, i, x, y;
781   unsigned char *pp;
782   /* We have a greyscale image, and we need a truecolor image. */
783 
784   pp = pixel_data;
785   for (y=0; y<height; y++)
786     for (x=0; x<width; x++)
787       {
788 	int idx = *pp++;
789 	if (bit_depth > 8) idx = *pp++;
790 	if (obit_depth < bit_depth)
791 	  idx <<= (bit_depth-obit_depth);
792 	i = pixel_for (idx, idx, idx);
793 	if (xrotate)
794 	  XPutPixel(ximage, y, width-x-1, i);
795 	else
796 	  XPutPixel(ximage, x, y, i);
797       }
798 }
799 
800 struct {
801   int png_image_type;
802   void (*converter)(image *);
803 } image_converters[] = {
804   { PNG_COLOR_TYPE_RGB, cvt_rgbt },
805   { PNG_COLOR_TYPE_RGB_ALPHA, cvt_rgbt },
806   { PNG_COLOR_TYPE_PALETTE, cvt_cpt },
807   { PNG_COLOR_TYPE_GRAY, cvt_gt },
808 };
809 #define NUM_CONVERTERS (sizeof(image_converters)/sizeof(image_converters[0]))
810 
811 static void
build_image(image * src)812 build_image (image *src)
813 {
814   const char *file_bytes;
815   unsigned char **row_pointers;
816   int i, number_of_passes, num_trans;
817 
818   if (!exemplar_image)
819     exemplar_image = XGetImage(display, window, 0, 0, 8, 8, ~0, ZPixmap);
820 
821   src->pixels = (pixels_type *)malloc(sizeof(pixels_type));
822   memset (src->pixels, 0, sizeof(pixels_type));
823 
824   if (xrotate)
825     src->pixels->image_pixmap = XCreatePixmap (display, window,
826 					       src->height, src->width,
827 					       DefaultDepth(display, screen));
828   else
829     src->pixels->image_pixmap = XCreatePixmap (display, window,
830 					       src->width, src->height,
831 					       DefaultDepth(display, screen));
832 
833   if (src->synth_func)
834     {
835       src->synth_func (src);
836       return;
837     }
838   if (src->file_data == 0)
839     return;
840 
841   png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0);
842   info_ptr = png_create_info_struct (png_ptr);
843 
844   if (setjmp (png_jmpbuf(png_ptr))) {
845     fprintf(stderr, "Invalid PNG image!\n");
846     return;
847   }
848 
849   file_bytes = src->file_data;
850   png_set_read_fn (png_ptr, (png_voidp)&file_bytes, (png_rw_ptr)png_reader);
851 
852   png_read_info (png_ptr, info_ptr);
853 
854   if (interlace_type == PNG_INTERLACE_NONE)
855     number_of_passes = 1;
856   else
857     number_of_passes = png_set_interlace_handling(png_ptr);
858 
859   png_get_IHDR (png_ptr, info_ptr, &width, &height, &obit_depth, &color_type, &interlace_type, 0, 0);
860 #if 0
861   fprintf(stderr, "width %d height %d bit_depth %d color_type %s %s %s %s(%s)\n", width, height, obit_depth,
862 	  color_type & PNG_COLOR_MASK_PALETTE ? "palette" : "true",
863 	  color_type & PNG_COLOR_MASK_COLOR ? "color" : "grey",
864 	  color_type & PNG_COLOR_MASK_ALPHA ? "alpha" : "solid",
865 	  interlace_type == PNG_INTERLACE_NONE ? "" : "interlaced ",
866 	  src->list->name);
867 #endif
868 
869   if (obit_depth < 8)
870     png_set_packing(png_ptr);
871 
872   png_read_update_info (png_ptr, info_ptr);
873   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0);
874 #if 0
875   fprintf(stderr, "width %d height %d bit_depth %d color_type %s %s %s %s(%s)\n",
876 	  width, height, bit_depth,
877 	  color_type & PNG_COLOR_MASK_PALETTE ? "palette" : "true",
878 	  color_type & PNG_COLOR_MASK_COLOR ? "color" : "grey",
879 	  color_type & PNG_COLOR_MASK_ALPHA ? "alpha" : "solid",
880 	  interlace_type == PNG_INTERLACE_NONE ? "" : "interlaced ",
881 	  src->list->name);
882 #endif
883 
884   pixel_data = (unsigned char *)malloc(height * png_get_rowbytes (png_ptr, info_ptr));
885   row_pointers = (unsigned char **)malloc(height*sizeof(unsigned char *));
886 
887   for (i=0; i<height; i++)
888     row_pointers[i] = pixel_data + i * png_get_rowbytes (png_ptr, info_ptr);
889 
890   while (number_of_passes--)
891     png_read_rows(png_ptr, row_pointers, NULL, height);
892 
893   png_read_end (png_ptr, 0);
894 
895   if (xrotate)
896     ximage = XCreateImage (display, visual, exemplar_image->depth,
897 			   exemplar_image->format, 0, 0, height, width,
898 			   exemplar_image->bitmap_pad, 0);
899   else
900     ximage = XCreateImage (display, visual, exemplar_image->depth,
901 			   exemplar_image->format, 0, 0, width, height,
902 			   exemplar_image->bitmap_pad, 0);
903   ximage->data = (unsigned char *)malloc(ximage->bytes_per_line * (xrotate ? width : height));
904 
905   if (color_type & PNG_COLOR_MASK_ALPHA
906       || png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
907     {
908       if (xrotate)
909 	xmask = XCreateImage (display, visual, 1,
910 			      XYPixmap, 0, 0, height, width,
911 			      8, 0);
912       else
913 	xmask = XCreateImage (display, visual, 1,
914 			      XYPixmap, 0, 0, width, height,
915 			      8, 0);
916       xmask->data = (unsigned char *)malloc(xmask->bytes_per_line * (xrotate ? width : height));
917 
918       if (xrotate)
919 	src->pixels->image_mask = XCreatePixmap (display, window,
920 						 src->height, src->width, 1);
921       else
922 	src->pixels->image_mask = XCreatePixmap (display, window,
923 						 src->width, src->height, 1);
924       if (maskgc == 0)
925 	{
926 	  maskgc = XCreateGC(display, src->pixels->image_mask, 0, 0);
927 	  XSetClipMask(display, maskgc, None);
928 	}
929     }
930   else
931     xmask = 0;
932 
933   for (i=0; i<NUM_CONVERTERS; i++)
934     {
935       if (color_type == image_converters[i].png_image_type)
936 	{
937 	  image_converters[i].converter(src);
938 	  break;
939 	}
940     }
941   if (i == NUM_CONVERTERS)
942     fprintf(stderr, "No converter for %s\n", src->list->name);
943 
944   XSetClipMask(display, gc, None);
945   if (xrotate)
946     XPutImage (display, src->pixels->image_pixmap, gc, ximage, 0, 0, 0, 0, height, width);
947   else
948     XPutImage (display, src->pixels->image_pixmap, gc, ximage, 0, 0, 0, 0, width, height);
949   if (xmask)
950     {
951       if (xrotate)
952 	XPutImage (display, src->pixels->image_mask, maskgc, xmask, 0, 0, 0, 0, height, width);
953       else
954 	XPutImage (display, src->pixels->image_mask, maskgc, xmask, 0, 0, 0, 0, width, height);
955     }
956   xwin_restore_clip ();
957 
958   png_destroy_read_struct(&png_ptr, &info_ptr, 0);
959 
960   XDestroyImage (ximage); ximage = 0;
961   if (xmask) XDestroyImage (xmask); xmask = 0;
962   free(pixel_data); pixel_data = 0;
963   free(row_pointers); row_pointers = 0;
964 }
965 
966 void
put_image(image * src,int x,int y,int w,int h,image * dest,int dx,int dy,int flags)967 put_image (image *src, int x, int y, int w, int h,
968 	   image *dest, int dx, int dy, int flags)
969 {
970   Pixmap which, mask;
971   GC pgc;
972   int sw, sh, dw, dh;
973   if (dest == &static_display_image)
974     pgc = gc;
975   else
976     pgc = imggc;
977 
978 #if 0
979   printf("put_image %s.%s.%dx%d (%dx%d+%d+%d) to %s at %d,%d%s%s\n",
980 	 src->list->name, type_names[src->type], src->width, src->height,
981 	 w, h, x, y, dest->list->name, dx, dy,
982 	 flags & PUT_INVERTED ? " inverted" : "",
983 	 flags & PUT_ROTATED ? " rotated" : "");
984 #endif
985 
986   if (!src->pixels)
987     build_image (src);
988   if (!dest->pixels)
989     build_image (dest);
990 
991   if (!src->pixels->image_pixmap)
992     return;
993   which = src->pixels->image_pixmap;
994   mask = src->pixels->image_mask;
995 
996   if (xrotate)
997     {
998       int t = x;
999       x = y;
1000       y = src->width - t - w;
1001       t = w;
1002       w = h;
1003       h = t;
1004       t = dy;
1005       dy = dest->width - dx - src->width;
1006       dx = t;
1007       sw = src->height;
1008       sh = src->width;
1009       dw = dest->height;
1010       dh = dest->width;
1011     }
1012   else
1013     {
1014       sw = src->width;
1015       sh = src->height;
1016       dw = dest->width;
1017       dh = dest->height;
1018     }
1019 
1020   if (flags & PUT_ROTATED)
1021     {
1022       Pixmap temp;
1023       int rx, ry, nx, ny;
1024       if (!src->pixels->rotated_pixmap)
1025 	{
1026 	  temp = XCreatePixmap(display, window, sw, sh,
1027 			       DefaultDepth(display, screen));
1028 	  src->pixels->rotated_pixmap = XCreatePixmap(display, window, sw, sh,
1029 						      DefaultDepth(display, screen));
1030 	  for (rx=0; rx<sw; rx++)
1031 	    XCopyArea (display, which, temp, pgc,
1032 		       rx, 0, 1, sh, sw-rx-1, 0);
1033 	  for (ry=0; ry<sh; ry++)
1034 	    XCopyArea (display, temp, src->pixels->rotated_pixmap, pgc,
1035 		       0, ry, sw, 1, 0, sh-ry-1);
1036 	  XFreePixmap(display, temp);
1037 	}
1038       if (src->pixels->image_mask && !src->pixels->rotated_mask)
1039 	{
1040 	  temp = XCreatePixmap(display, window, sw, sh, 1);
1041 	  src->pixels->rotated_mask = XCreatePixmap(display, window, sw, sh, 1);
1042 	  for (rx=0; rx<sw; rx++)
1043 	    XCopyArea (display, mask, temp, maskgc,
1044 		       rx, 0, 1, sh, sw-rx-1, 0);
1045 	  for (ry=0; ry<sh; ry++)
1046 	    XCopyArea (display, temp, src->pixels->rotated_mask, maskgc,
1047 		       0, ry, sw, 1, 0, sh-ry-1);
1048 	  XFreePixmap(display, temp);
1049 	}
1050 
1051       which = src->pixels->rotated_pixmap;
1052       mask = src->pixels->rotated_mask;
1053       nx = sw - x - w;
1054       ny = sh - y - h;
1055       dx += x-nx;
1056       dy += y-ny;
1057       x = nx;
1058       y = ny;
1059     }
1060 
1061   if (flags & PUT_INVERTED)
1062     {
1063       XImage *img;
1064       int x, y, b = pixel_for(0, 0, 0), w = pixel_for(255, 255, 255);
1065       if (!src->pixels->inverted_pixmap)
1066 	{
1067 	  src->pixels->inverted_pixmap
1068 	    = XCreatePixmap(display, window, sw, sh,
1069 			    DefaultDepth(display, screen));
1070 	  XSetClipMask(display, pgc, None);
1071 	  img = XGetImage (display, src->pixels->image_pixmap,
1072 			   0, 0, sw, sh, ~0, ZPixmap);
1073 	  for (x=0; x<sw; x++)
1074 	    for (y=0; y<sh; y++)
1075 	      {
1076 		int p = XGetPixel(img, x, y);
1077 		if (vip->class != PseudoColor)
1078 		  {
1079 		    p = ~p & 0xffffff;
1080 		  }
1081 		else
1082 		  {
1083 		    if (p == w)
1084 		      p = b;
1085 		    else if (p == b)
1086 		      p = w;
1087 		  }
1088 		XPutPixel(img, x, y, p);
1089 	      }
1090 	  XPutImage (display, src->pixels->inverted_pixmap, pgc, img,
1091 		     0, 0, 0, 0, sw, sh);
1092 	  xwin_restore_clip();
1093 	}
1094 
1095       which = src->pixels->inverted_pixmap;
1096       mask = src->pixels->image_mask;
1097     }
1098 
1099   if (mask && !broken_xserver)
1100     {
1101       XSetClipMask(display, pgc, mask);
1102       XSetClipOrigin(display, pgc, dx, dy);
1103     }
1104   XCopyArea(display, which, dest->pixels->image_pixmap, pgc,
1105 	    x, y, w, h, dx+x, dy+y);
1106   XSync(display, 0);
1107   if (mask && !broken_xserver)
1108     {
1109       if (pgc == gc)
1110 	xwin_restore_clip();
1111       else
1112 	XSetClipMask(display, pgc, None);
1113     }
1114 }
1115 
1116 void
put_mask(image * src,int x,int y,int w,int h,image * dest,int dx,int dy,int flags)1117 put_mask (image *src, int x, int y, int w, int h,
1118 	  image *dest, int dx, int dy, int flags)
1119 {
1120   Pixmap which, mask;
1121 #if 0
1122   printf("put_mask %s.%s.%dx%d (%dx%d+%d+%d) to %s at %d,%d%s%s\n",
1123 	 src->list->name, type_names[src->type], src->width, src->height,
1124 	 w, h, x, y, dest->list->name, dx, dy,
1125 	 flags & PUT_INVERTED ? " inverted" : "",
1126 	 flags & PUT_ROTATED ? " rotated" : "");
1127 #endif
1128 
1129   if (!src->pixels)
1130     build_image (src);
1131   if (!dest->pixels)
1132     build_image (src);
1133 
1134   if (!src->pixels->image_pixmap)
1135     return;
1136   if (!src->pixels->image_mask)
1137     return;
1138 
1139   if (xrotate)
1140     {
1141       int t = x;
1142       x = y;
1143       y = src->width - t - w;
1144       t = w;
1145       w = h;
1146       h = t;
1147       t = dy;
1148       dy = table_height - dx - src->width;
1149       dx = t;
1150     }
1151 
1152   if (!dest->pixels->image_mask)
1153     {
1154       dest->pixels->image_mask = XCreatePixmap(display, window, dest->width, dest->height, 1);
1155       if (maskgc == 0)
1156 	{
1157 	  maskgc = XCreateGC(display, dest->pixels->image_mask, 0, 0);
1158 	  XSetClipMask(display, maskgc, None);
1159 	}
1160       XSetForeground(display, maskgc, 1);
1161       XFillRectangle(display, dest->pixels->image_mask, maskgc, 0, 0, dest->width, dest->height);
1162     }
1163 
1164   XCopyArea(display, src->pixels->image_mask, dest->pixels->image_mask, maskgc,
1165 	    x, y, w, h, dx+x, dy+y);
1166 }
1167 
fill_image(image * dest,int x,int y,int w,int h,int r,int g,int b)1168 void fill_image (image *dest, int x, int y, int w, int h,
1169 		 int r, int g, int b)
1170 {
1171   GC pgc;
1172   if (dest == &static_display_image)
1173     pgc = gc;
1174   else
1175     pgc = imggc;
1176 
1177   if (!dest->pixels)
1178     build_image (dest);
1179   if (!dest->pixels->image_pixmap)
1180     return;
1181 
1182   if (xrotate)
1183     {
1184       int t = x;
1185       x = dest->height - y - h;
1186       y = t;
1187       t = w;
1188       w = h;
1189       h = t;
1190     }
1191 
1192   XSetForeground (display, pgc, pixel_for (r, g, b));
1193   XFillRectangle (display, dest->pixels->image_pixmap, pgc, x, y, w, h);
1194 }
1195 
1196