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.
15 * This file defines some "XImage" manipulation functions for X11.
17 * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
19 * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
21 * Major fixes and cleanup by Ben Harrison (benh@phial.com).
23 * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
24 * which will have already "included" several relevant header files.
32 * Keysym macros, used on Keysyms to test for classes of symbols
33 * These were stolen from one of the X11 header files
35 * Also appears in "main-x11.c".
38 #define IsKeypadKey(keysym) \
39 (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
41 #define IsCursorKey(keysym) \
42 (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
44 #define IsPFKey(keysym) \
45 (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
47 #define IsFunctionKey(keysym) \
48 (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
50 #define IsMiscFunctionKey(keysym) \
51 (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
53 #define IsModifierKey(keysym) \
54 (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
56 #endif /* IsModifierKey */
60 * Checks if the keysym is a special key or a normal key
61 * Assume that XK_MISCELLANY keysyms are special
63 * Also appears in "main-x11.c".
65 #define IsSpecialKey(keysym) \
66 ((unsigned)(keysym) >= 0xFF00)
70 static bool gamma_table_ready = FALSE;
71 static int gamma_val = 0;
72 #endif /* SUPPORT_GAMMA */
76 * Hack -- Convert an RGB value to an X11 Pixel, or die.
78 static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
80 Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
85 if (!gamma_table_ready)
87 cptr str = getenv("ANGBAND_X11_GAMMA");
88 if (str != NULL) gamma_val = atoi(str);
90 gamma_table_ready = TRUE;
92 /* Only need to build the table if gamma exists */
93 if (gamma_val) build_gamma_table(gamma_val);
96 /* Hack -- Gamma Correction */
99 red = gamma_table[red];
100 green = gamma_table[green];
101 blue = gamma_table[blue];
104 #endif /* SUPPORT_GAMMA */
106 /* Build the color */
108 xcolour.red = red * 255;
109 xcolour.green = green * 255;
110 xcolour.blue = blue * 255;
111 xcolour.flags = DoRed | DoGreen | DoBlue;
113 /* Attempt to Allocate the Parsed color */
114 if (!(XAllocColor(dpy, cmap, &xcolour)))
116 quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n",
120 return (xcolour.pixel);
128 * The Win32 "BITMAPFILEHEADER" type.
130 * Note the "bfAlign" field, which is a complete hack to ensure that the
131 * "u32b" fields in the structure get aligned. Thus, when reading this
132 * header from the file, we must be careful to skip this field.
134 typedef struct BITMAPFILEHEADER
136 u16b bfAlign; /* HATE this */
145 * The Win32 "BITMAPINFOHEADER" type.
147 typedef struct BITMAPINFOHEADER
156 u32b biXPelsPerMeter;
157 u32b biYPelsPerMeter;
163 * The Win32 "RGBQUAD" type.
165 typedef struct RGBQUAD
167 unsigned char b, g, r;
168 unsigned char filler;
173 * Read a Win32 BMP file.
175 * This function replaces the old ReadRaw and RemapColors functions.
177 * Assumes that the bitmap has a size such that no padding is needed in
178 * various places. Currently only handles bitmaps with 3 to 256 colors.
180 static XImage *ReadBMP(Display *dpy, char *Name)
182 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
184 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
188 BITMAPFILEHEADER fileheader;
189 BITMAPINFOHEADER infoheader;
191 vptr fileheaderhack = (vptr)((char *)(&fileheader) + 2);
205 unsigned long clr_pixels[256];
208 /* Open the BMP file */
209 f = fopen(Name, "r");
217 /* Read the "BITMAPFILEHEADER" */
218 fread(fileheaderhack, sizeof(fileheader) - 2, 1, f);
220 /* Read the "BITMAPINFOHEADER" */
221 fread(&infoheader, sizeof(infoheader), 1, f);
223 /* Verify the header */
225 (fileheader.bfType != 19778) ||
226 (infoheader.biSize != 40))
228 quit_fmt("Incorrect BMP file format %s", Name);
231 /* The two headers above occupy 54 bytes total */
232 /* The "bfOffBits" field says where the data starts */
233 /* The "biClrUsed" field does not seem to be reliable */
234 /* Compute number of colors recorded */
235 ncol = (fileheader.bfOffBits - 54) / 4;
237 for (i = 0; i < ncol; i++)
241 fread(&clrg, 4, 1, f);
243 /* Analyze the color */
244 clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
247 /* Determine total bytes needed for image */
249 j = (depth - 1) >> 2;
250 while (j >>= 1) i <<= 1;
251 total = infoheader.biWidth * infoheader.biHeight * i;
253 /* Allocate image memory */
254 C_MAKE(Data, total, char);
256 Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/,
257 Data, infoheader.biWidth, infoheader.biHeight,
258 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
263 C_KILL(Data, total, char);
268 for (y = 0; y < infoheader.biHeight; y++)
270 int y2 = infoheader.biHeight - y - 1;
272 for (x = 0; x < infoheader.biWidth; x++)
276 /* Verify not at end of file XXX XXX */
277 if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
279 if (infoheader.biBitCount == 24)
284 /* Verify not at end of file XXX XXX */
285 if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
287 XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
289 else if (infoheader.biBitCount == 8)
291 XPutPixel(Res, x, y2, clr_pixels[ch]);
293 else if (infoheader.biBitCount == 4)
295 XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
297 XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
301 /* Technically 1 bit is legal too */
302 quit_fmt("Illegal biBitCount %d in %s",
303 infoheader.biBitCount, Name);
314 /* ========================================================*/
315 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
316 /* ========================================================*/
319 * to save ourselves some labour, define a maximum expected icon width here:
321 #define MAX_ICON_WIDTH 32
324 /* some static variables for composing and decomposing pixel values into
325 * red, green and blue values
327 static unsigned long redMask, greenMask, blueMask;
328 static int redShift, greenShift, blueShift;
332 * Use smooth rescaling?
334 static bool smoothRescaling = TRUE;
338 * GetScaledRow reads a scan from the given XImage, scales it smoothly
339 * and returns the red, green and blue values in arrays.
340 * The values in this arrays must be divided by a certain value that is
341 * calculated in ScaleIcon.
342 * x, y is the position, iw is the input width and ow the output width
343 * redScan, greenScan and blueScan must be sufficiently sized
345 static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow,
346 unsigned long *redScan, unsigned long *greenScan,
347 unsigned long *blueScan)
349 int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
351 int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
357 for (xi = 0; xi < ow; xi++)
359 pix = XGetPixel(Im, x + xi, y);
360 redScan [xi] = (pix >> redShift) & redMask;
361 greenScan [xi] = (pix >> greenShift) & greenMask;
362 blueScan [xi] = (pix >> blueShift) & blueMask;
367 /* scaling by subsampling (grow) */
370 /* read first pixel: */
371 pix = XGetPixel(Im, x, y);
372 nextRed = (pix >> redShift) & redMask;
373 nextGreen = (pix >> greenShift) & greenMask;
374 nextBlue = (pix >> blueShift) & blueMask;
376 prevGreen = nextGreen;
378 /* si and sifrac give the subsampling position: */
381 /* getNextPix tells us, that we need the next pixel */
384 for (xi = 0; xi <= ow; xi++)
389 prevGreen = nextGreen;
393 /* only get next pixel if in same icon */
394 pix = XGetPixel(Im, si + 1, y);
395 nextRed = (pix >> redShift) & redMask;
396 nextGreen = (pix >> greenShift) & greenMask;
397 nextBlue = (pix >> blueShift) & blueMask;
401 /* calculate subsampled color values: */
402 /* division by ow occurs in ScaleIcon */
403 redScan [xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
404 greenScan [xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
405 blueScan [xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
407 /* advance sampling position: */
424 /* scaling by averaging (shrink) */
425 /* width of an output pixel in input pixels: */
428 /* start position of the first output pixel: */
431 /* get first input pixel: */
432 pix = XGetPixel(Im, x, y);
433 nextRed = (pix >> redShift) & redMask;
434 nextGreen = (pix >> greenShift) & greenMask;
435 nextBlue = (pix >> blueShift) & blueMask;
436 for (xi = 0; xi < ow; xi++)
438 /* find endpoint of the current output pixel: */
440 cifrac = sifrac + addFrac;
446 /* take fraction of current input pixel (starting segment): */
447 redScan[xi] = nextRed * (ow - sifrac);
448 greenScan[xi] = nextGreen * (ow - sifrac);
449 blueScan[xi] = nextBlue * (ow - sifrac);
451 /* add values for whole pixels: */
454 pix = XGetPixel(Im, si, y);
455 redScan[xi] += ((pix >> redShift) & redMask) *ow;
456 greenScan[xi] += ((pix >> greenShift) & greenMask) *ow;
457 blueScan[xi] += ((pix >> blueShift) & blueMask) *ow;
460 /* add fraction of current input pixel (ending segment): */
463 /* only get next pixel if still in icon: */
464 pix = XGetPixel(Im, si, y);
465 nextRed = (pix >> redShift) & redMask;
466 nextGreen = (pix >> greenShift) & greenMask;
467 nextBlue = (pix >> blueShift) & blueMask;
472 redScan[xi] += nextRed * sifrac;
473 greenScan[xi] += nextGreen * sifrac;
474 blueScan[xi] += nextBlue * sifrac;
482 * PutRGBScan takes arrays for red, green and blue and writes pixel values
483 * according to this values in the XImage-structure. w is the number of
484 * pixels to write and div is the value by which all red/green/blue values
487 static void PutRGBScan(XImage *Im, int x, int y, int w, int div,
488 unsigned long *redScan, unsigned long *greenScan,
489 unsigned long *blueScan)
493 unsigned long adj = div / 2;
494 for (xi = 0; xi < w; xi++)
496 pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) +
497 ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) +
498 ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
499 XPutPixel(Im, x + xi, y, pix);
505 * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
507 * Source size is (ix, iy) and destination size is (ox, oy).
508 * It does this by getting icon scan line from GetScaledScan and handling
509 * them the same way as pixels are handled in GetScaledScan.
510 * This even allows icons to be scaled differently in horizontal and
511 * vertical directions (eg. shrink horizontal, grow vertical).
513 static void ScaleIcon(XImage *ImIn, XImage *ImOut,
514 int x1, int y1, int x2, int y2,
515 int ix, int iy, int ox, int oy)
518 int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
520 /* buffers for pixel rows: */
521 unsigned long prevRed [MAX_ICON_WIDTH];
522 unsigned long prevGreen [MAX_ICON_WIDTH];
523 unsigned long prevBlue [MAX_ICON_WIDTH];
524 unsigned long nextRed [MAX_ICON_WIDTH];
525 unsigned long nextGreen [MAX_ICON_WIDTH];
526 unsigned long nextBlue [MAX_ICON_WIDTH];
527 unsigned long tempRed [MAX_ICON_WIDTH];
528 unsigned long tempGreen [MAX_ICON_WIDTH];
529 unsigned long tempBlue [MAX_ICON_WIDTH];
533 /* get divider value for the horizontal scaling: */
543 /* no scaling needed vertically: */
544 for (yi = 0; yi < oy; yi++)
546 GetScaledRow(ImIn, x1, y1 + yi, ix, ox,
547 tempRed, tempGreen, tempBlue);
548 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
549 tempRed, tempGreen, tempBlue);
554 /* scaling by subsampling (grow): */
559 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
560 /* si and sifrac give the subsampling position: */
563 /* getNextRow tells us, that we need the next row */
565 for (yi = 0; yi <= oy; yi++)
569 for (xi = 0; xi < ox; xi++)
571 prevRed[xi] = nextRed[xi];
572 prevGreen[xi] = nextGreen[xi];
573 prevBlue[xi] = nextBlue[xi];
577 /* only get next row if in same icon */
578 GetScaledRow(ImIn, x1, si + 1, ix, ox,
579 nextRed, nextGreen, nextBlue);
583 /* calculate subsampled color values: */
584 /* division by oy occurs in PutRGBScan */
585 for (xi = 0; xi < ox; xi++)
587 tempRed[xi] = (prevRed[xi] * (oy - sifrac) +
588 nextRed[xi] * sifrac);
589 tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) +
590 nextGreen[xi] * sifrac);
591 tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) +
592 nextBlue[xi] * sifrac);
595 /* write row to output image: */
596 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
597 tempRed, tempGreen, tempBlue);
599 /* advance sampling position: */
616 /* scaling by averaging (shrink) */
618 /* height of a output row in input rows: */
621 /* start position of the first output row: */
624 /* get first input row: */
625 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
626 for (yi = 0; yi < oy; yi++)
628 /* find endpoint of the current output row: */
630 cifrac = sifrac + addFrac;
636 /* take fraction of current input row (starting segment): */
637 for (xi = 0; xi < ox; xi++)
639 tempRed[xi] = nextRed[xi] * (oy - sifrac);
640 tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
641 tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
644 /* add values for whole pixels: */
647 GetScaledRow(ImIn, x1, si, ix, ox,
648 nextRed, nextGreen, nextBlue);
649 for (xi = 0; xi < ox; xi++)
651 tempRed[xi] += nextRed[xi] * oy;
652 tempGreen[xi] += nextGreen[xi] * oy;
653 tempBlue[xi] += nextBlue[xi] * oy;
657 /* add fraction of current input row (ending segment): */
660 /* only get next row if still in icon: */
661 GetScaledRow(ImIn, x1, si, ix, ox,
662 nextRed, nextGreen, nextBlue);
665 for (xi = 0; xi < ox; xi++)
667 tempRed[xi] += nextRed[xi] * sifrac;
668 tempGreen[xi] += nextGreen[xi] * sifrac;
669 tempBlue[xi] += nextBlue[xi] * sifrac;
671 /* write row to output image: */
672 PutRGBScan(ImOut, x2, y2 + yi, ox, div,
673 tempRed, tempGreen, tempBlue);
680 static XImage *ResizeImageSmooth(Display *dpy, XImage *Im,
681 int ix, int iy, int ox, int oy)
683 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
685 int width1, height1, width2, height2;
693 height1 = Im->height;
695 width2 = ox * width1 / ix;
696 height2 = oy * height1 / iy;
698 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
700 Tmp = XCreateImage(dpy, visual,
701 Im->depth, ZPixmap, 0, Data, width2, height2,
704 /* compute values for decomposing pixel into color values: */
705 redMask = Im->red_mask;
707 while ((redMask & 1) == 0)
712 greenMask = Im->green_mask;
714 while ((greenMask & 1) == 0)
719 blueMask = Im->blue_mask;
721 while ((blueMask & 1) == 0)
727 /* scale each icon: */
728 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
730 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
732 ScaleIcon(Im, Tmp, x1, y1, x2, y2,
742 * Resize an image. XXX XXX XXX
744 * Also appears in "main-xaw.c".
746 static XImage *ResizeImage(Display *dpy, XImage *Im,
747 int ix, int iy, int ox, int oy)
749 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
751 int width1, height1, width2, height2;
752 int x1, x2, y1, y2, Tx, Ty;
753 int *px1, *px2, *dx1, *dx2;
754 int *py1, *py2, *dy1, *dy2;
760 if (smoothRescaling && (ix != ox || iy != oy) &&
761 visual->class == TrueColor)
763 return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
767 height1 = Im->height;
769 width2 = ox * width1 / ix;
770 height2 = oy * height1 / iy;
772 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
774 Tmp = XCreateImage(dpy, visual,
775 Im->depth, ZPixmap, 0, Data, width2, height2,
810 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); )
814 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); )
816 XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
841 #endif /* USE_GRAPHICS */