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