1 /* File: maid-x11.c */
4 * Copyright (c) 1997 Ben Harrison, and others
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies.
14 #include "main/x11-gamma-builder.h"
17 * This file defines some "XImage" manipulation functions for X11.
19 * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
21 * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
23 * Major fixes and cleanup by Ben Harrison (benh@phial.com).
25 * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
26 * which will have already "included" several relevant header files.
34 * Keysym macros, used on Keysyms to test for classes of symbols
35 * These were stolen from one of the X11 header files
37 * Also appears in "main-x11.c".
40 #define IsKeypadKey(keysym) \
41 (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
43 #define IsCursorKey(keysym) \
44 (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
46 #define IsPFKey(keysym) \
47 (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
49 #define IsFunctionKey(keysym) \
50 (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
52 #define IsMiscFunctionKey(keysym) \
53 (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
55 #define IsModifierKey(keysym) \
56 (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
58 #endif /* IsModifierKey */
62 * Checks if the keysym is a special key or a normal key
63 * Assume that XK_MISCELLANY keysyms are special
65 * Also appears in "main-x11.c".
67 #define IsSpecialKey(keysym) \
68 ((unsigned)(keysym) >= 0xFF00)
71 static bool gamma_table_ready = FALSE;
72 static int gamma_val = 0;
76 * Hack -- Convert an RGB value to an X11 Pixel, or die.
79 static XftColor create_pixel(Display *dpy, byte red, byte green, byte blue)
81 static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
84 Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
86 if (!gamma_table_ready)
88 concptr str = getenv("ANGBAND_X11_GAMMA");
89 if (str != NULL) gamma_val = atoi(str);
91 gamma_table_ready = TRUE;
93 /* Only need to build the table if gamma exists */
94 if (gamma_val) build_gamma_table(gamma_val);
97 /* Hack -- Gamma Correction */
100 red = gamma_table[red];
101 green = gamma_table[green];
102 blue = gamma_table[blue];
105 /* Build the color */
107 xcolour.red = red * 255;
108 xcolour.green = green * 255;
109 xcolour.blue = blue * 255;
110 xcolour.flags = DoRed | DoGreen | DoBlue;
115 xcol.red = xcolour.red;
116 xcol.green = xcolour.green;
117 xcol.blue = xcolour.blue;
118 if (!XftColorAllocValue(dpy, DefaultVisual(dpy, 0), cmap, &xcol, &color))
120 quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n",
126 /* Attempt to Allocate the Parsed color */
127 if (!(XAllocColor(dpy, cmap, &xcolour)))
129 quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n",
133 return (xcolour.pixel);
141 * The Win32 "BITMAPFILEHEADER" type.
143 typedef struct BITMAPFILEHEADER
153 * The Win32 "BITMAPINFOHEADER" type.
155 typedef struct BITMAPINFOHEADER
164 u32b biXPelsPerMeter;
165 u32b biYPelsPerMeter;
171 * The Win32 "RGBQUAD" type.
173 typedef struct RGBQUAD
175 unsigned char b, g, r;
176 unsigned char filler;
180 /*** Helper functions for system independent file loading. ***/
182 static byte get_byte(FILE *fff)
184 /* Get a character, and return it */
185 return (getc(fff) & 0xFF);
188 static void rd_byte(FILE *fff, byte *ip)
193 static void rd_u16b(FILE *fff, u16b *ip)
195 (*ip) = get_byte(fff);
196 (*ip) |= ((u16b)(get_byte(fff)) << 8);
199 static void rd_u32b(FILE *fff, u32b *ip)
201 (*ip) = get_byte(fff);
202 (*ip) |= ((u32b)(get_byte(fff)) << 8);
203 (*ip) |= ((u32b)(get_byte(fff)) << 16);
204 (*ip) |= ((u32b)(get_byte(fff)) << 24);
209 * Read a Win32 BMP file.
211 * This function replaces the old ReadRaw and RemapColors functions.
213 * Assumes that the bitmap has a size such that no padding is needed in
214 * various places. Currently only handles bitmaps with 3 to 256 colors.
216 static XImage *ReadBMP(Display *dpy, char *Name)
218 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
220 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
224 BITMAPFILEHEADER fileheader;
225 BITMAPINFOHEADER infoheader;
239 unsigned long clr_pixels[256];
242 /* Open the BMP file */
243 f = fopen(Name, "r");
251 /* Read the "BITMAPFILEHEADER" */
252 rd_u16b(f, &(fileheader.bfType));
253 rd_u32b(f, &(fileheader.bfSize));
254 rd_u16b(f, &(fileheader.bfReserved1));
255 rd_u16b(f, &(fileheader.bfReserved2));
256 rd_u32b(f, &(fileheader.bfOffBits));
258 /* Read the "BITMAPINFOHEADER" */
259 rd_u32b(f, &(infoheader.biSize));
260 rd_u32b(f, &(infoheader.biWidth));
261 rd_u32b(f, &(infoheader.biHeight));
262 rd_u16b(f, &(infoheader.biPlanes));
263 rd_u16b(f, &(infoheader.biBitCount));
264 rd_u32b(f, &(infoheader.biCompresion));
265 rd_u32b(f, &(infoheader.biSizeImage));
266 rd_u32b(f, &(infoheader.biXPelsPerMeter));
267 rd_u32b(f, &(infoheader.biYPelsPerMeter));
268 rd_u32b(f, &(infoheader.biClrUsed));
269 rd_u32b(f, &(infoheader.biClrImportand));
271 /* Verify the header */
273 (fileheader.bfType != 19778) ||
274 (infoheader.biSize != 40))
276 quit_fmt("Incorrect BMP file format %s", Name);
279 /* The two headers above occupy 54 bytes total */
280 /* The "bfOffBits" field says where the data starts */
281 /* The "biClrUsed" field does not seem to be reliable */
282 /* Compute number of colors recorded */
283 ncol = (fileheader.bfOffBits - 54) / 4;
285 for (i = 0; i < ncol; i++)
289 /* Read an "RGBQUAD" */
290 rd_byte(f, &(clrg.b));
291 rd_byte(f, &(clrg.g));
292 rd_byte(f, &(clrg.r));
293 rd_byte(f, &(clrg.filler));
295 /* Analyze the color */
296 clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
299 /* Determine total bytes needed for image */
301 j = (depth - 1) >> 2;
302 while (j >>= 1) i <<= 1;
303 total = infoheader.biWidth * infoheader.biHeight * i;
305 /* Allocate image memory */
306 C_MAKE(Data, total, char);
308 Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/,
309 Data, infoheader.biWidth, infoheader.biHeight,
310 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
315 C_KILL(Data, total, char);
320 for (y = 0; y < infoheader.biHeight; y++)
322 int y2 = infoheader.biHeight - y - 1;
324 for (x = 0; x < infoheader.biWidth; x++)
328 /* Verify not at end of file XXX XXX */
329 if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
331 if (infoheader.biBitCount == 24)
336 /* Verify not at end of file XXX XXX */
337 if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
339 XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
341 else if (infoheader.biBitCount == 8)
343 XPutPixel(Res, x, y2, clr_pixels[ch]);
345 else if (infoheader.biBitCount == 4)
347 XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
349 XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
353 /* Technically 1 bit is legal too */
354 quit_fmt("Illegal biBitCount %d in %s",
355 infoheader.biBitCount, Name);
366 /* ========================================================*/
367 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
368 /* ========================================================*/
371 * to save ourselves some labour, define a maximum expected icon width here:
373 #define MAX_ICON_WIDTH 32
376 /* some static variables for composing and decomposing pixel values into
377 * red, green and blue values
379 static unsigned long redMask, greenMask, blueMask;
380 static int redShift, greenShift, blueShift;
384 * Use smooth rescaling?
386 static bool smoothRescaling = TRUE;
390 * GetScaledRow reads a scan from the given XImage, scales it smoothly
391 * and returns the red, green and blue values in arrays.
392 * The values in this arrays must be divided by a certain value that is
393 * calculated in ScaleIcon.
394 * x, y is the position, iw is the input width and ow the output width
395 * redScan, greenScan and blueScan must be sufficiently sized
397 static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow,
398 unsigned long *redScan, unsigned long *greenScan,
399 unsigned long *blueScan)
401 int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
403 int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
409 for (xi = 0; xi < ow; xi++)
411 pix = XGetPixel(Im, x + xi, y);
412 redScan [xi] = (pix >> redShift) & redMask;
413 greenScan [xi] = (pix >> greenShift) & greenMask;
414 blueScan [xi] = (pix >> blueShift) & blueMask;
419 /* scaling by subsampling (grow) */
422 /* read first pixel: */
423 pix = XGetPixel(Im, x, y);
424 nextRed = (pix >> redShift) & redMask;
425 nextGreen = (pix >> greenShift) & greenMask;
426 nextBlue = (pix >> blueShift) & blueMask;
428 prevGreen = nextGreen;
430 /* si and sifrac give the subsampling position: */
433 /* getNextPix tells us, that we need the next pixel */
436 for (xi = 0; xi <= ow; xi++)
441 prevGreen = nextGreen;
445 /* only get next pixel if in same icon */
446 pix = XGetPixel(Im, si + 1, y);
447 nextRed = (pix >> redShift) & redMask;
448 nextGreen = (pix >> greenShift) & greenMask;
449 nextBlue = (pix >> blueShift) & blueMask;
453 /* calculate subsampled color values: */
454 /* division by ow occurs in ScaleIcon */
455 redScan [xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
456 greenScan [xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
457 blueScan [xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
459 /* advance sampling position: */
476 /* scaling by averaging (shrink) */
477 /* width of an output pixel in input pixels: */
480 /* start position of the first output pixel: */
483 /* get first input pixel: */
484 pix = XGetPixel(Im, x, y);
485 nextRed = (pix >> redShift) & redMask;
486 nextGreen = (pix >> greenShift) & greenMask;
487 nextBlue = (pix >> blueShift) & blueMask;
488 for (xi = 0; xi < ow; xi++)
490 /* find endpoint of the current output pixel: */
492 cifrac = sifrac + addFrac;
498 /* take fraction of current input pixel (starting segment): */
499 redScan[xi] = nextRed * (ow - sifrac);
500 greenScan[xi] = nextGreen * (ow - sifrac);
501 blueScan[xi] = nextBlue * (ow - sifrac);
503 /* add values for whole pixels: */
506 pix = XGetPixel(Im, si, y);
507 redScan[xi] += ((pix >> redShift) & redMask) *ow;
508 greenScan[xi] += ((pix >> greenShift) & greenMask) *ow;
509 blueScan[xi] += ((pix >> blueShift) & blueMask) *ow;
512 /* add fraction of current input pixel (ending segment): */
515 /* only get next pixel if still in icon: */
516 pix = XGetPixel(Im, si, y);
517 nextRed = (pix >> redShift) & redMask;
518 nextGreen = (pix >> greenShift) & greenMask;
519 nextBlue = (pix >> blueShift) & blueMask;
524 redScan[xi] += nextRed * sifrac;
525 greenScan[xi] += nextGreen * sifrac;
526 blueScan[xi] += nextBlue * sifrac;
534 * PutRGBScan takes arrays for red, green and blue and writes pixel values
535 * according to this values in the XImage-structure. w is the number of
536 * pixels to write and div is the value by which all red/green/blue values
539 static void PutRGBScan(XImage *Im, int x, int y, int w, int div,
540 unsigned long *redScan, unsigned long *greenScan,
541 unsigned long *blueScan)
545 unsigned long adj = div / 2;
546 for (xi = 0; xi < w; xi++)
548 pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) +
549 ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) +
550 ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
551 XPutPixel(Im, x + xi, y, pix);
557 * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
559 * Source size is (ix, iy) and destination size is (ox, oy).
560 * It does this by getting icon scan line from GetScaledScan and handling
561 * them the same way as pixels are handled in GetScaledScan.
562 * This even allows icons to be scaled differently in horizontal and
563 * vertical directions (eg. shrink horizontal, grow vertical).
565 static void ScaleIcon(XImage *ImIn, XImage *ImOut,
566 int x1, int y1, int x2, int y2,
567 int ix, int iy, int ox, int oy)
570 int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
572 /* buffers for pixel rows: */
573 unsigned long prevRed [MAX_ICON_WIDTH];
574 unsigned long prevGreen [MAX_ICON_WIDTH];
575 unsigned long prevBlue [MAX_ICON_WIDTH];
576 unsigned long nextRed [MAX_ICON_WIDTH];
577 unsigned long nextGreen [MAX_ICON_WIDTH];
578 unsigned long nextBlue [MAX_ICON_WIDTH];
579 unsigned long tempRed [MAX_ICON_WIDTH];
580 unsigned long tempGreen [MAX_ICON_WIDTH];
581 unsigned long tempBlue [MAX_ICON_WIDTH];
585 /* get divider value for the horizontal scaling: */
595 /* no scaling needed vertically: */
596 for (yi = 0; yi < oy; yi++)
598 GetScaledRow(ImIn, x1, y1 + yi, ix, ox,
599 tempRed, tempGreen, tempBlue);
600 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
601 tempRed, tempGreen, tempBlue);
606 /* scaling by subsampling (grow): */
611 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
612 /* si and sifrac give the subsampling position: */
615 /* getNextRow tells us, that we need the next row */
617 for (yi = 0; yi <= oy; yi++)
621 for (xi = 0; xi < ox; xi++)
623 prevRed[xi] = nextRed[xi];
624 prevGreen[xi] = nextGreen[xi];
625 prevBlue[xi] = nextBlue[xi];
629 /* only get next row if in same icon */
630 GetScaledRow(ImIn, x1, si + 1, ix, ox,
631 nextRed, nextGreen, nextBlue);
635 /* calculate subsampled color values: */
636 /* division by oy occurs in PutRGBScan */
637 for (xi = 0; xi < ox; xi++)
639 tempRed[xi] = (prevRed[xi] * (oy - sifrac) +
640 nextRed[xi] * sifrac);
641 tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) +
642 nextGreen[xi] * sifrac);
643 tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) +
644 nextBlue[xi] * sifrac);
647 /* write row to output image: */
648 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
649 tempRed, tempGreen, tempBlue);
651 /* advance sampling position: */
668 /* scaling by averaging (shrink) */
670 /* height of a output row in input rows: */
673 /* start position of the first output row: */
676 /* get first input row: */
677 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
678 for (yi = 0; yi < oy; yi++)
680 /* find endpoint of the current output row: */
682 cifrac = sifrac + addFrac;
688 /* take fraction of current input row (starting segment): */
689 for (xi = 0; xi < ox; xi++)
691 tempRed[xi] = nextRed[xi] * (oy - sifrac);
692 tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
693 tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
696 /* add values for whole pixels: */
699 GetScaledRow(ImIn, x1, si, ix, ox,
700 nextRed, nextGreen, nextBlue);
701 for (xi = 0; xi < ox; xi++)
703 tempRed[xi] += nextRed[xi] * oy;
704 tempGreen[xi] += nextGreen[xi] * oy;
705 tempBlue[xi] += nextBlue[xi] * oy;
709 /* add fraction of current input row (ending segment): */
712 /* only get next row if still in icon: */
713 GetScaledRow(ImIn, x1, si, ix, ox,
714 nextRed, nextGreen, nextBlue);
717 for (xi = 0; xi < ox; xi++)
719 tempRed[xi] += nextRed[xi] * sifrac;
720 tempGreen[xi] += nextGreen[xi] * sifrac;
721 tempBlue[xi] += nextBlue[xi] * sifrac;
723 /* write row to output image: */
724 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
725 tempRed, tempGreen, tempBlue);
732 static XImage *ResizeImageSmooth(Display *dpy, XImage *Im,
733 int ix, int iy, int ox, int oy)
735 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
737 int width1, height1, width2, height2;
745 height1 = Im->height;
747 width2 = ox * width1 / ix;
748 height2 = oy * height1 / iy;
750 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
752 Tmp = XCreateImage(dpy, visual,
753 Im->depth, ZPixmap, 0, Data, width2, height2,
756 /* compute values for decomposing pixel into color values: */
757 redMask = Im->red_mask;
759 while ((redMask & 1) == 0)
764 greenMask = Im->green_mask;
766 while ((greenMask & 1) == 0)
771 blueMask = Im->blue_mask;
773 while ((blueMask & 1) == 0)
779 /* scale each icon: */
780 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
782 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
784 ScaleIcon(Im, Tmp, x1, y1, x2, y2,
794 * Resize an image. XXX XXX XXX
796 * Also appears in "main-xaw.c".
798 static XImage *ResizeImage(Display *dpy, XImage *Im,
799 int ix, int iy, int ox, int oy)
801 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
803 int width1, height1, width2, height2;
804 int x1, x2, y1, y2, Tx, Ty;
805 int *px1, *px2, *dx1, *dx2;
806 int *py1, *py2, *dy1, *dy2;
812 if (smoothRescaling && (ix != ox || iy != oy) &&
813 visual->class == TrueColor)
815 return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
819 height1 = Im->height;
821 width2 = ox * width1 / ix;
822 height2 = oy * height1 / iy;
824 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
826 Tmp = XCreateImage(dpy, visual,
827 Im->depth, ZPixmap, 0, Data, width2, height2,
862 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); )
866 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); )
868 XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
893 #endif /* !USE_XFT */