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.
13 #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.
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) (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
40 #define IsCursorKey(keysym) (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
42 #define IsPFKey(keysym) (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
44 #define IsFunctionKey(keysym) (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
46 #define IsMiscFunctionKey(keysym) (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
48 #define IsModifierKey(keysym) (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
50 #endif /* IsModifierKey */
53 * Checks if the keysym is a special key or a normal key
54 * Assume that XK_MISCELLANY keysyms are special
56 * Also appears in "main-x11.c".
58 #define IsSpecialKey(keysym) ((unsigned)(keysym) >= 0xFF00)
60 static bool gamma_table_ready = true;
61 static int gamma_val = 0;
64 * Hack -- Convert an RGB value to an X11 Pixel, or die.
67 static XftColor create_pixel(Display *dpy, byte red, byte green, byte blue)
69 static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
72 Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
74 if (!gamma_table_ready) {
75 concptr str = getenv("ANGBAND_X11_GAMMA");
77 gamma_val = atoi(str);
79 gamma_table_ready = true;
81 /* Only need to build the table if gamma exists */
83 build_gamma_table(gamma_val);
86 /* Hack -- Gamma Correction */
88 red = gamma_table[red];
89 green = gamma_table[green];
90 blue = gamma_table[blue];
95 xcolour.red = red * 255;
96 xcolour.green = green * 255;
97 xcolour.blue = blue * 255;
98 xcolour.flags = DoRed | DoGreen | DoBlue;
103 xcol.red = xcolour.red;
104 xcol.green = xcolour.green;
105 xcol.blue = xcolour.blue;
107 if (!XftColorAllocValue(dpy, DefaultVisual(dpy, 0), cmap, &xcol, &color)) {
108 quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n", red, green, blue);
113 /* Attempt to Allocate the Parsed color */
114 if (!(XAllocColor(dpy, cmap, &xcolour))) {
115 quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n", red, green, blue);
118 return (xcolour.pixel);
125 * The Win32 "BITMAPFILEHEADER" type.
127 typedef struct BITMAPFILEHEADER {
130 uint16_t bfReserved1;
131 uint16_t bfReserved2;
136 * The Win32 "BITMAPINFOHEADER" type.
138 typedef struct BITMAPINFOHEADER {
144 uint32_t biCompresion;
145 uint32_t biSizeImage;
146 uint32_t biXPelsPerMeter;
147 uint32_t biYPelsPerMeter;
149 uint32_t biClrImportand;
153 * The Win32 "RGBQUAD" type.
155 typedef struct RGBQUAD {
156 unsigned char b, g, r;
157 unsigned char filler;
160 /*** Helper functions for system independent file loading. ***/
162 static byte get_byte(FILE *fff)
164 /* Get a character, and return it */
165 return (getc(fff) & 0xFF);
168 static void rd_byte(FILE *fff, byte *ip)
173 static void rd_u16b(FILE *fff, uint16_t *ip)
175 (*ip) = get_byte(fff);
176 (*ip) |= ((uint16_t)(get_byte(fff)) << 8);
179 static void rd_u32b(FILE *fff, uint32_t *ip)
181 (*ip) = get_byte(fff);
182 (*ip) |= ((uint32_t)(get_byte(fff)) << 8);
183 (*ip) |= ((uint32_t)(get_byte(fff)) << 16);
184 (*ip) |= ((uint32_t)(get_byte(fff)) << 24);
188 * Read a Win32 BMP file.
190 * This function replaces the old ReadRaw and RemapColors functions.
192 * Assumes that the bitmap has a size such that no padding is needed in
193 * various places. Currently only handles bitmaps with 3 to 256 colors.
195 static XImage *ReadBMP(Display *dpy, char *Name)
197 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
199 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
203 BITMAPFILEHEADER fileheader;
204 BITMAPINFOHEADER infoheader;
206 XImage *Res = nullptr;
218 unsigned long clr_pixels[256];
220 /* Open the BMP file */
221 f = fopen(Name, "r");
228 /* Read the "BITMAPFILEHEADER" */
229 rd_u16b(f, &(fileheader.bfType));
230 rd_u32b(f, &(fileheader.bfSize));
231 rd_u16b(f, &(fileheader.bfReserved1));
232 rd_u16b(f, &(fileheader.bfReserved2));
233 rd_u32b(f, &(fileheader.bfOffBits));
235 /* Read the "BITMAPINFOHEADER" */
236 rd_u32b(f, &(infoheader.biSize));
237 rd_u32b(f, &(infoheader.biWidth));
238 rd_u32b(f, &(infoheader.biHeight));
239 rd_u16b(f, &(infoheader.biPlanes));
240 rd_u16b(f, &(infoheader.biBitCount));
241 rd_u32b(f, &(infoheader.biCompresion));
242 rd_u32b(f, &(infoheader.biSizeImage));
243 rd_u32b(f, &(infoheader.biXPelsPerMeter));
244 rd_u32b(f, &(infoheader.biYPelsPerMeter));
245 rd_u32b(f, &(infoheader.biClrUsed));
246 rd_u32b(f, &(infoheader.biClrImportand));
248 /* Verify the header */
249 if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) {
250 quit_fmt("Incorrect BMP file format %s", Name);
253 /* The two headers above occupy 54 bytes total */
254 /* The "bfOffBits" field says where the data starts */
255 /* The "biClrUsed" field does not seem to be reliable */
256 /* Compute number of colors recorded */
257 ncol = (fileheader.bfOffBits - 54) / 4;
259 for (i = 0; i < ncol; i++) {
262 /* Read an "RGBQUAD" */
263 rd_byte(f, &(clrg.b));
264 rd_byte(f, &(clrg.g));
265 rd_byte(f, &(clrg.r));
266 rd_byte(f, &(clrg.filler));
268 /* Analyze the color */
269 clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
272 /* Determine total bytes needed for image */
274 j = (depth - 1) >> 2;
277 total = infoheader.biWidth * infoheader.biHeight * i;
279 /* Allocate image memory */
280 Data = (char*)malloc(total);
282 Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/, Data, infoheader.biWidth, infoheader.biHeight, 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
285 if (Res == nullptr) {
291 for (y = 0; y < static_cast<int>(infoheader.biHeight); y++) {
292 int y2 = infoheader.biHeight - y - 1;
294 for (x = 0; x < static_cast<int>(infoheader.biWidth); x++) {
297 /* Verify not at end of file XXX XXX */
299 quit_fmt("Unexpected end of file in %s", Name);
301 if (infoheader.biBitCount == 24) {
305 /* Verify not at end of file XXX XXX */
307 quit_fmt("Unexpected end of file in %s", Name);
309 XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
310 } else if (infoheader.biBitCount == 8) {
311 XPutPixel(Res, x, y2, clr_pixels[ch]);
312 } else if (infoheader.biBitCount == 4) {
313 XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
315 XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
317 /* Technically 1 bit is legal too */
318 quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name);
328 /* ========================================================*/
329 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
330 /* ========================================================*/
333 * to save ourselves some labour, define a maximum expected icon width here:
335 #define MAX_ICON_WIDTH 32
337 /* some static variables for composing and decomposing pixel values into
338 * red, green and blue values
340 static unsigned long redMask, greenMask, blueMask;
341 static int redShift, greenShift, blueShift;
344 * Use smooth rescaling?
346 static bool smoothRescaling = true;
349 * GetScaledRow reads a scan from the given XImage, scales it smoothly
350 * and returns the red, green and blue values in arrays.
351 * The values in this arrays must be divided by a certain value that is
352 * calculated in ScaleIcon.
353 * x, y is the position, iw is the input width and ow the output width
354 * redScan, greenScan and blueScan must be sufficiently sized
356 static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
358 int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
360 int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
365 for (xi = 0; xi < ow; xi++) {
366 pix = XGetPixel(Im, x + xi, y);
367 redScan[xi] = (pix >> redShift) & redMask;
368 greenScan[xi] = (pix >> greenShift) & greenMask;
369 blueScan[xi] = (pix >> blueShift) & blueMask;
371 } else if (iw < ow) {
372 /* scaling by subsampling (grow) */
375 /* read first pixel: */
376 pix = XGetPixel(Im, x, y);
377 nextRed = (pix >> redShift) & redMask;
378 nextGreen = (pix >> greenShift) & greenMask;
379 nextBlue = (pix >> blueShift) & blueMask;
381 prevGreen = nextGreen;
383 /* si and sifrac give the subsampling position: */
386 /* getNextPix tells us, that we need the next pixel */
389 for (xi = 0; xi <= ow; xi++) {
392 prevGreen = nextGreen;
395 /* only get next pixel if in same icon */
396 pix = XGetPixel(Im, si + 1, y);
397 nextRed = (pix >> redShift) & redMask;
398 nextGreen = (pix >> greenShift) & greenMask;
399 nextBlue = (pix >> blueShift) & blueMask;
403 /* calculate subsampled color values: */
404 /* division by ow occurs in ScaleIcon */
405 redScan[xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
406 greenScan[xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
407 blueScan[xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
409 /* advance sampling position: */
420 /* scaling by averaging (shrink) */
421 /* width of an output pixel in input pixels: */
424 /* start position of the first output pixel: */
427 /* get first input pixel: */
428 pix = XGetPixel(Im, x, y);
429 nextRed = (pix >> redShift) & redMask;
430 nextGreen = (pix >> greenShift) & greenMask;
431 nextBlue = (pix >> blueShift) & blueMask;
432 for (xi = 0; xi < ow; xi++) {
433 /* find endpoint of the current output pixel: */
435 cifrac = sifrac + addFrac;
440 /* take fraction of current input pixel (starting segment): */
441 redScan[xi] = nextRed * (ow - sifrac);
442 greenScan[xi] = nextGreen * (ow - sifrac);
443 blueScan[xi] = nextBlue * (ow - sifrac);
445 /* add values for whole pixels: */
447 pix = XGetPixel(Im, si, y);
448 redScan[xi] += ((pix >> redShift) & redMask) * ow;
449 greenScan[xi] += ((pix >> greenShift) & greenMask) * ow;
450 blueScan[xi] += ((pix >> blueShift) & blueMask) * ow;
453 /* add fraction of current input pixel (ending segment): */
455 /* only get next pixel if still in icon: */
456 pix = XGetPixel(Im, si, y);
457 nextRed = (pix >> redShift) & redMask;
458 nextGreen = (pix >> greenShift) & greenMask;
459 nextBlue = (pix >> blueShift) & blueMask;
463 redScan[xi] += nextRed * sifrac;
464 greenScan[xi] += nextGreen * sifrac;
465 blueScan[xi] += nextBlue * sifrac;
472 * PutRGBScan takes arrays for red, green and blue and writes pixel values
473 * according to this values in the XImage-structure. w is the number of
474 * pixels to write and div is the value by which all red/green/blue values
477 static void PutRGBScan(XImage *Im, int x, int y, int w, int div, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
481 unsigned long adj = div / 2;
482 for (xi = 0; xi < w; xi++) {
483 pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) + ((((greenScan[xi] + adj) / div) & greenMask) << greenShift)
484 + ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
485 XPutPixel(Im, x + xi, y, pix);
490 * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
492 * Source size is (ix, iy) and destination size is (ox, oy).
493 * It does this by getting icon scan line from GetScaledScan and handling
494 * them the same way as pixels are handled in GetScaledScan.
495 * This even allows icons to be scaled differently in horizontal and
496 * vertical directions (eg. shrink horizontal, grow vertical).
498 static void ScaleIcon(XImage *ImIn, XImage *ImOut, int x1, int y1, int x2, int y2, int ix, int iy, int ox, int oy)
501 int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
503 /* buffers for pixel rows: */
504 unsigned long prevRed[MAX_ICON_WIDTH];
505 unsigned long prevGreen[MAX_ICON_WIDTH];
506 unsigned long prevBlue[MAX_ICON_WIDTH];
507 unsigned long nextRed[MAX_ICON_WIDTH];
508 unsigned long nextGreen[MAX_ICON_WIDTH];
509 unsigned long nextBlue[MAX_ICON_WIDTH];
510 unsigned long tempRed[MAX_ICON_WIDTH];
511 unsigned long tempGreen[MAX_ICON_WIDTH];
512 unsigned long tempBlue[MAX_ICON_WIDTH];
516 /* get divider value for the horizontal scaling: */
525 /* no scaling needed vertically: */
526 for (yi = 0; yi < oy; yi++) {
527 GetScaledRow(ImIn, x1, y1 + yi, ix, ox, tempRed, tempGreen, tempBlue);
528 PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
530 } else if (iy < oy) {
531 /* scaling by subsampling (grow): */
536 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
537 /* si and sifrac give the subsampling position: */
540 /* getNextRow tells us, that we need the next row */
542 for (yi = 0; yi <= oy; yi++) {
544 for (xi = 0; xi < ox; xi++) {
545 prevRed[xi] = nextRed[xi];
546 prevGreen[xi] = nextGreen[xi];
547 prevBlue[xi] = nextBlue[xi];
550 /* only get next row if in same icon */
551 GetScaledRow(ImIn, x1, si + 1, ix, ox, nextRed, nextGreen, nextBlue);
555 /* calculate subsampled color values: */
556 /* division by oy occurs in PutRGBScan */
557 for (xi = 0; xi < ox; xi++) {
558 tempRed[xi] = (prevRed[xi] * (oy - sifrac) + nextRed[xi] * sifrac);
559 tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) + nextGreen[xi] * sifrac);
560 tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) + nextBlue[xi] * sifrac);
563 /* write row to output image: */
564 PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
566 /* advance sampling position: */
577 /* scaling by averaging (shrink) */
579 /* height of a output row in input rows: */
582 /* start position of the first output row: */
585 /* get first input row: */
586 GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
587 for (yi = 0; yi < oy; yi++) {
588 /* find endpoint of the current output row: */
590 cifrac = sifrac + addFrac;
595 /* take fraction of current input row (starting segment): */
596 for (xi = 0; xi < ox; xi++) {
597 tempRed[xi] = nextRed[xi] * (oy - sifrac);
598 tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
599 tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
602 /* add values for whole pixels: */
604 GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue);
605 for (xi = 0; xi < ox; xi++) {
606 tempRed[xi] += nextRed[xi] * oy;
607 tempGreen[xi] += nextGreen[xi] * oy;
608 tempBlue[xi] += nextBlue[xi] * oy;
612 /* add fraction of current input row (ending segment): */
614 /* only get next row if still in icon: */
615 GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue);
618 for (xi = 0; xi < ox; xi++) {
619 tempRed[xi] += nextRed[xi] * sifrac;
620 tempGreen[xi] += nextGreen[xi] * sifrac;
621 tempBlue[xi] += nextBlue[xi] * sifrac;
623 /* write row to output image: */
624 PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
629 static XImage *ResizeImageSmooth(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
631 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
633 int width1, height1, width2, height2;
641 height1 = Im->height;
643 width2 = ox * width1 / ix;
644 height2 = oy * height1 / iy;
646 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
648 Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
650 /* compute values for decomposing pixel into color values: */
651 redMask = Im->red_mask;
653 while ((redMask & 1) == 0) {
657 greenMask = Im->green_mask;
659 while ((greenMask & 1) == 0) {
663 blueMask = Im->blue_mask;
665 while ((blueMask & 1) == 0) {
670 /* scale each icon: */
671 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy) {
672 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox) {
673 ScaleIcon(Im, Tmp, x1, y1, x2, y2, ix, iy, ox, oy);
681 * Resize an image. XXX XXX XXX
683 * Also appears in "main-xaw.c".
685 static XImage *ResizeImage(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
687 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
689 int width1, height1, width2, height2;
690 volatile int x1, x2, y1, y2, Tx, Ty;
691 volatile int *px1, *px2, *dx1, *dx2;
692 volatile int *py1, *py2, *dy1, *dy2;
698 if (smoothRescaling && (ix != ox || iy != oy) && visual->c_class == TrueColor) {
699 return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
703 height1 = Im->height;
705 width2 = ox * width1 / ix;
706 height2 = oy * height1 / iy;
708 Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
710 Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
738 for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2);) {
741 for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2);) {
742 XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
765 #endif /* !USE_XFT */