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 * This file defines some "XImage" manipulation functions for X11.
15 * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
17 * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
19 * Major fixes and cleanup by Ben Harrison (benh@phial.com).
21 * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
22 * which will have already "included" several relevant header files.
27 #if defined(USE_X11) || defined(USE_XAW) || defined(USE_XPJ) || defined(USE_GTK)
29 #ifndef __MAKEDEPEND__
31 #include <X11/Xutil.h>
32 #include <X11/keysym.h>
33 #include <X11/keysymdef.h>
34 #endif /* __MAKEDEPEND__ */
36 /* Include our headers */
41 static bool gamma_table_ready
= FALSE
;
42 static int gamma_val
= 0;
43 #endif /* SUPPORT_GAMMA */
47 * Hack -- Convert an RGB value to an X11 Pixel, or die.
49 u32b
create_pixel(Display
*dpy
, byte red
, byte green
, byte blue
)
51 Colormap cmap
= DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy
));
57 if (!gamma_table_ready
)
59 cptr str
= getenv("ANGBAND_X11_GAMMA");
60 if (str
!= NULL
) gamma_val
= atoi(str
);
62 gamma_table_ready
= TRUE
;
64 /* Only need to build the table if gamma exists */
65 if (gamma_val
) build_gamma_table(gamma_val
);
68 /* Hack -- Gamma Correction */
71 red
= gamma_table
[red
];
72 green
= gamma_table
[green
];
73 blue
= gamma_table
[blue
];
76 #endif /* SUPPORT_GAMMA */
80 xcolour
.red
= red
* 255;
81 xcolour
.green
= green
* 255;
82 xcolour
.blue
= blue
* 255;
83 xcolour
.flags
= DoRed
| DoGreen
| DoBlue
;
85 /* Attempt to Allocate the Parsed color */
86 if (!(XAllocColor(dpy
, cmap
, &xcolour
)))
88 quit_fmt("Couldn't allocate bitmap color #%04x%04x%04x\n",
89 xcolour
.red
, xcolour
.green
, xcolour
.blue
);
92 return (xcolour
.pixel
);
97 * Get the name of the default font to use for the term.
99 cptr
get_default_font(int term_num
)
105 /* Window specific font name */
106 strnfmt(buf
, sizeof(buf
), "ANGBAND_X11_FONT_%d", term_num
);
108 /* Check environment for that font */
111 /* Check environment for "base" font */
112 if (!font
) font
= getenv("ANGBAND_X11_FONT");
114 /* No environment variables, use default font */
121 font
= DEFAULT_X11_FONT_0
;
126 font
= DEFAULT_X11_FONT_1
;
131 font
= DEFAULT_X11_FONT_2
;
136 font
= DEFAULT_X11_FONT_3
;
141 font
= DEFAULT_X11_FONT_4
;
146 font
= DEFAULT_X11_FONT_5
;
151 font
= DEFAULT_X11_FONT_6
;
156 font
= DEFAULT_X11_FONT_7
;
161 font
= DEFAULT_X11_FONT
;
173 * The Win32 "BITMAPFILEHEADER" type.
175 typedef struct BITMAPFILEHEADER
186 * The Win32 "BITMAPINFOHEADER" type.
188 typedef struct BITMAPINFOHEADER
197 u32b biXPelsPerMeter
;
198 u32b biYPelsPerMeter
;
204 * The Win32 "RGBQUAD" type.
206 typedef struct RGBQUAD
208 unsigned char b
, g
, r
;
209 unsigned char filler
;
213 /*** Helper functions for system independent file loading. ***/
215 static byte
get_byte(FILE *fff
)
217 /* Get a character, and return it */
218 return (getc(fff
) & 0xFF);
221 static void rd_byte(FILE *fff
, byte
*ip
)
226 static void rd_u16b(FILE *fff
, u16b
*ip
)
228 (*ip
) = get_byte(fff
);
229 (*ip
) |= ((u16b
)(get_byte(fff
)) << 8);
232 static void rd_u32b(FILE *fff
, u32b
*ip
)
234 (*ip
) = get_byte(fff
);
235 (*ip
) |= ((u32b
)(get_byte(fff
)) << 8);
236 (*ip
) |= ((u32b
)(get_byte(fff
)) << 16);
237 (*ip
) |= ((u32b
)(get_byte(fff
)) << 24);
242 * Read a Win32 BMP file.
244 * This function replaces the old ReadRaw and RemapColors functions.
246 * Assumes that the bitmap has a size such that no padding is needed in
247 * various places. Currently only handles bitmaps with 3 to 256 colors.
249 XImage
*ReadBMP(Display
*dpy
, char *Name
)
251 Visual
*visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
253 int depth
= DefaultDepth(dpy
, DefaultScreen(dpy
));
257 BITMAPFILEHEADER fileheader
;
258 BITMAPINFOHEADER infoheader
;
272 unsigned long clr_pixels
[256];
275 /* Open the BMP file */
276 f
= fopen(Name
, "r");
284 /* Read the "BITMAPFILEHEADER" */
285 rd_u16b(f
, &(fileheader
.bfType
));
286 rd_u32b(f
, &(fileheader
.bfSize
));
287 rd_u16b(f
, &(fileheader
.bfReserved1
));
288 rd_u16b(f
, &(fileheader
.bfReserved2
));
289 rd_u32b(f
, &(fileheader
.bfOffBits
));
291 /* Read the "BITMAPINFOHEADER" */
292 rd_u32b(f
, &(infoheader
.biSize
));
293 rd_u32b(f
, &(infoheader
.biWidth
));
294 rd_u32b(f
, &(infoheader
.biHeight
));
295 rd_u16b(f
, &(infoheader
.biPlanes
));
296 rd_u16b(f
, &(infoheader
.biBitCount
));
297 rd_u32b(f
, &(infoheader
.biCompresion
));
298 rd_u32b(f
, &(infoheader
.biSizeImage
));
299 rd_u32b(f
, &(infoheader
.biXPelsPerMeter
));
300 rd_u32b(f
, &(infoheader
.biYPelsPerMeter
));
301 rd_u32b(f
, &(infoheader
.biClrUsed
));
302 rd_u32b(f
, &(infoheader
.biClrImportand
));
304 /* Verify the header */
306 (fileheader
.bfType
!= 19778) ||
307 (infoheader
.biSize
!= 40))
309 quit_fmt("Incorrect BMP file format %s", Name
);
312 /* The two headers above occupy 54 bytes total */
313 /* The "bfOffBits" field says where the data starts */
314 /* The "biClrUsed" field does not seem to be reliable */
315 /* Compute number of colors recorded */
316 ncol
= (fileheader
.bfOffBits
- 54) / 4;
318 for (i
= 0; i
< ncol
; i
++)
322 /* Read an "RGBQUAD" */
323 rd_byte(f
, &(clrg
.b
));
324 rd_byte(f
, &(clrg
.g
));
325 rd_byte(f
, &(clrg
.r
));
326 rd_byte(f
, &(clrg
.filler
));
328 /* Analyze the color */
329 clr_pixels
[i
] = create_pixel(dpy
, clrg
.r
, clrg
.g
, clrg
.b
);
332 /* Determine total bytes needed for image */
334 j
= (depth
- 1) >> 2;
335 while (j
>>= 1) i
<<= 1;
336 total
= infoheader
.biWidth
* infoheader
.biHeight
* i
;
338 /* Allocate image memory */
339 C_MAKE(Data
, total
, char);
341 Res
= XCreateImage(dpy
, visual
, depth
, ZPixmap
, 0 /*offset*/,
342 Data
, infoheader
.biWidth
, infoheader
.biHeight
,
343 32 /*bitmap_pad*/, 0 /*bytes_per_line*/);
353 for (y
= 0; y
< infoheader
.biHeight
; y
++)
355 u32b y2
= infoheader
.biHeight
- y
- 1;
357 for (x
= 0; x
< infoheader
.biWidth
; x
++)
361 /* Verify not at end of file XXX XXX */
362 if (feof(f
)) quit_fmt("Unexpected end of file in %s", Name
);
364 if (infoheader
.biBitCount
== 24)
366 int c3
, c2
= getc(f
);
368 /* Verify not at end of file XXX XXX */
369 if (feof(f
)) quit_fmt("Unexpected end of file in %s", Name
);
373 /* Verify not at end of file XXX XXX */
374 if (feof(f
)) quit_fmt("Unexpected end of file in %s", Name
);
376 XPutPixel(Res
, x
, y2
, create_pixel(dpy
, ch
, c2
, c3
));
378 else if (infoheader
.biBitCount
== 8)
380 XPutPixel(Res
, x
, y2
, clr_pixels
[ch
]);
382 else if (infoheader
.biBitCount
== 4)
384 XPutPixel(Res
, x
, y2
, clr_pixels
[ch
/ 16]);
386 XPutPixel(Res
, x
, y2
, clr_pixels
[ch
% 16]);
390 /* Technically 1 bit is legal too */
391 quit_fmt("Illegal biBitCount %d in %s",
392 infoheader
.biBitCount
, Name
);
403 /* ========================================================*/
404 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
405 /* ========================================================*/
408 * to save ourselves some labour, define a maximum expected icon width here:
410 #define MAX_ICON_WIDTH 32
413 /* some static variables for composing and decomposing pixel values into
414 * red, green and blue values
416 static unsigned long redMask
, greenMask
, blueMask
;
417 static int redShift
, greenShift
, blueShift
;
421 * Use smooth rescaling?
423 bool smoothRescaling
= TRUE
;
427 * GetScaledRow reads a scan from the given XImage, scales it smoothly
428 * and returns the red, green and blue values in arrays.
429 * The values in this arrays must be divided by a certain value that is
430 * calculated in ScaleIcon.
431 * x, y is the position, iw is the input width and ow the output width
432 * redScan, greenScan and blueScan must be sufficiently sized
434 static void GetScaledRow(XImage
*Im
, int x
, int y
, int iw
, int ow
,
435 unsigned long *redScan
, unsigned long *greenScan
,
436 unsigned long *blueScan
)
438 int xi
, si
, sifrac
, ci
, cifrac
, addWhole
, addFrac
;
440 int prevRed
, prevGreen
, prevBlue
, nextRed
, nextGreen
, nextBlue
;
446 for (xi
= 0; xi
< ow
; xi
++)
448 pix
= XGetPixel(Im
, x
+ xi
, y
);
449 redScan
[xi
] = (pix
>> redShift
) & redMask
;
450 greenScan
[xi
] = (pix
>> greenShift
) & greenMask
;
451 blueScan
[xi
] = (pix
>> blueShift
) & blueMask
;
456 /* scaling by subsampling (grow) */
459 /* read first pixel: */
460 pix
= XGetPixel(Im
, x
, y
);
461 nextRed
= (pix
>> redShift
) & redMask
;
462 nextGreen
= (pix
>> greenShift
) & greenMask
;
463 nextBlue
= (pix
>> blueShift
) & blueMask
;
465 prevGreen
= nextGreen
;
467 /* si and sifrac give the subsampling position: */
470 /* getNextPix tells us, that we need the next pixel */
473 for (xi
= 0; xi
<= ow
; xi
++)
478 prevGreen
= nextGreen
;
482 /* only get next pixel if in same icon */
483 pix
= XGetPixel(Im
, si
+ 1, y
);
484 nextRed
= (pix
>> redShift
) & redMask
;
485 nextGreen
= (pix
>> greenShift
) & greenMask
;
486 nextBlue
= (pix
>> blueShift
) & blueMask
;
490 /* calculate subsampled color values: */
491 /* division by ow occurs in ScaleIcon */
492 redScan
[xi
] = prevRed
* (ow
- sifrac
) + nextRed
* sifrac
;
493 greenScan
[xi
] = prevGreen
* (ow
- sifrac
) + nextGreen
* sifrac
;
494 blueScan
[xi
] = prevBlue
* (ow
- sifrac
) + nextBlue
* sifrac
;
496 /* advance sampling position: */
513 /* scaling by averaging (shrink) */
514 /* width of an output pixel in input pixels: */
517 /* start position of the first output pixel: */
520 /* get first input pixel: */
521 pix
= XGetPixel(Im
, x
, y
);
522 nextRed
= (pix
>> redShift
) & redMask
;
523 nextGreen
= (pix
>> greenShift
) & greenMask
;
524 nextBlue
= (pix
>> blueShift
) & blueMask
;
525 for (xi
= 0; xi
< ow
; xi
++)
527 /* find endpoint of the current output pixel: */
529 cifrac
= sifrac
+ addFrac
;
535 /* take fraction of current input pixel (starting segment): */
536 redScan
[xi
] = nextRed
* (ow
- sifrac
);
537 greenScan
[xi
] = nextGreen
* (ow
- sifrac
);
538 blueScan
[xi
] = nextBlue
* (ow
- sifrac
);
540 /* add values for whole pixels: */
543 pix
= XGetPixel(Im
, si
, y
);
544 redScan
[xi
] += ((pix
>> redShift
) & redMask
) *ow
;
545 greenScan
[xi
] += ((pix
>> greenShift
) & greenMask
) *ow
;
546 blueScan
[xi
] += ((pix
>> blueShift
) & blueMask
) *ow
;
549 /* add fraction of current input pixel (ending segment): */
552 /* only get next pixel if still in icon: */
553 pix
= XGetPixel(Im
, si
, y
);
554 nextRed
= (pix
>> redShift
) & redMask
;
555 nextGreen
= (pix
>> greenShift
) & greenMask
;
556 nextBlue
= (pix
>> blueShift
) & blueMask
;
561 redScan
[xi
] += nextRed
* sifrac
;
562 greenScan
[xi
] += nextGreen
* sifrac
;
563 blueScan
[xi
] += nextBlue
* sifrac
;
571 * PutRGBScan takes arrays for red, green and blue and writes pixel values
572 * according to this values in the XImage-structure. w is the number of
573 * pixels to write and div is the value by which all red/green/blue values
576 static void PutRGBScan(XImage
*Im
, int x
, int y
, int w
, int div
,
577 unsigned long *redScan
, unsigned long *greenScan
,
578 unsigned long *blueScan
)
582 unsigned long adj
= div
/ 2;
583 for (xi
= 0; xi
< w
; xi
++)
585 pix
= (((((redScan
[xi
] + adj
) / div
) & redMask
) << redShift
) +
586 ((((greenScan
[xi
] + adj
) / div
) & greenMask
) << greenShift
) +
587 ((((blueScan
[xi
] + adj
) / div
) & blueMask
) << blueShift
));
588 XPutPixel(Im
, x
+ xi
, y
, pix
);
594 * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
596 * Source size is (ix, iy) and destination size is (ox, oy).
597 * It does this by getting icon scan line from GetScaledScan and handling
598 * them the same way as pixels are handled in GetScaledScan.
599 * This even allows icons to be scaled differently in horizontal and
600 * vertical directions (eg. shrink horizontal, grow vertical).
602 static void ScaleIcon(XImage
*ImIn
, XImage
*ImOut
,
603 int x1
, int y1
, int x2
, int y2
,
604 int ix
, int iy
, int ox
, int oy
)
607 int xi
, yi
, si
, sifrac
, ci
, cifrac
, addWhole
, addFrac
;
609 /* buffers for pixel rows: */
610 unsigned long prevRed
[MAX_ICON_WIDTH
];
611 unsigned long prevGreen
[MAX_ICON_WIDTH
];
612 unsigned long prevBlue
[MAX_ICON_WIDTH
];
613 unsigned long nextRed
[MAX_ICON_WIDTH
];
614 unsigned long nextGreen
[MAX_ICON_WIDTH
];
615 unsigned long nextBlue
[MAX_ICON_WIDTH
];
616 unsigned long tempRed
[MAX_ICON_WIDTH
];
617 unsigned long tempGreen
[MAX_ICON_WIDTH
];
618 unsigned long tempBlue
[MAX_ICON_WIDTH
];
622 /* get divider value for the horizontal scaling: */
632 /* no scaling needed vertically: */
633 for (yi
= 0; yi
< oy
; yi
++)
635 GetScaledRow(ImIn
, x1
, y1
+ yi
, ix
, ox
,
636 tempRed
, tempGreen
, tempBlue
);
637 PutRGBScan(ImOut
, x2
, y2
+ yi
, ox
, div
,
638 tempRed
, tempGreen
, tempBlue
);
643 /* scaling by subsampling (grow): */
649 GetScaledRow(ImIn
, x1
, y1
, ix
, ox
, nextRed
, nextGreen
, nextBlue
);
651 /* si and sifrac give the subsampling position: */
655 /* getNextRow tells us, that we need the next row */
658 for (yi
= 0; yi
<= oy
; yi
++)
662 for (xi
= 0; xi
< ox
; xi
++)
664 prevRed
[xi
] = nextRed
[xi
];
665 prevGreen
[xi
] = nextGreen
[xi
];
666 prevBlue
[xi
] = nextBlue
[xi
];
671 /* only get next row if in same icon */
672 GetScaledRow(ImIn
, x1
, si
+ 1, ix
, ox
,
673 nextRed
, nextGreen
, nextBlue
);
677 /* calculate subsampled color values: */
678 /* division by oy occurs in PutRGBScan */
679 for (xi
= 0; xi
< ox
; xi
++)
681 tempRed
[xi
] = (prevRed
[xi
] * (oy
- sifrac
) +
682 nextRed
[xi
] * sifrac
);
683 tempGreen
[xi
] = (prevGreen
[xi
] * (oy
- sifrac
) +
684 nextGreen
[xi
] * sifrac
);
685 tempBlue
[xi
] = (prevBlue
[xi
] * (oy
- sifrac
) +
686 nextBlue
[xi
] * sifrac
);
689 /* write row to output image: */
690 PutRGBScan(ImOut
, x2
, y2
+ yi
, ox
, div
,
691 tempRed
, tempGreen
, tempBlue
);
693 /* advance sampling position: */
709 /* scaling by averaging (shrink) */
712 /* height of a output row in input rows: */
716 /* start position of the first output row: */
720 /* get first input row: */
721 GetScaledRow(ImIn
, x1
, y1
, ix
, ox
, nextRed
, nextGreen
, nextBlue
);
723 for (yi
= 0; yi
< oy
; yi
++)
725 /* find endpoint of the current output row: */
727 cifrac
= sifrac
+ addFrac
;
735 /* take fraction of current input row (starting segment): */
736 for (xi
= 0; xi
< ox
; xi
++)
738 tempRed
[xi
] = nextRed
[xi
] * (oy
- sifrac
);
739 tempGreen
[xi
] = nextGreen
[xi
] * (oy
- sifrac
);
740 tempBlue
[xi
] = nextBlue
[xi
] * (oy
- sifrac
);
745 /* add values for whole pixels: */
748 GetScaledRow(ImIn
, x1
, si
, ix
, ox
,
749 nextRed
, nextGreen
, nextBlue
);
751 for (xi
= 0; xi
< ox
; xi
++)
753 tempRed
[xi
] += nextRed
[xi
] * oy
;
754 tempGreen
[xi
] += nextGreen
[xi
] * oy
;
755 tempBlue
[xi
] += nextBlue
[xi
] * oy
;
760 /* add fraction of current input row (ending segment): */
763 /* only get next row if still in icon: */
764 GetScaledRow(ImIn
, x1
, si
, ix
, ox
,
765 nextRed
, nextGreen
, nextBlue
);
770 for (xi
= 0; xi
< ox
; xi
++)
772 tempRed
[xi
] += nextRed
[xi
] * sifrac
;
773 tempGreen
[xi
] += nextGreen
[xi
] * sifrac
;
774 tempBlue
[xi
] += nextBlue
[xi
] * sifrac
;
777 /* write row to output image: */
778 PutRGBScan(ImOut
, x2
, y2
+ yi
, ox
, div
,
779 tempRed
, tempGreen
, tempBlue
);
786 static XImage
*ResizeImageSmooth(Display
*dpy
, XImage
*Im
,
787 int ix
, int iy
, int ox
, int oy
)
789 Visual
*visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
791 int width1
, height1
, width2
, height2
;
799 height1
= Im
->height
;
801 width2
= ox
* width1
/ ix
;
802 height2
= oy
* height1
/ iy
;
804 Data
= (char *)malloc(width2
* height2
* Im
->bits_per_pixel
/ 8);
806 Tmp
= XCreateImage(dpy
, visual
,
807 Im
->depth
, ZPixmap
, 0, Data
, width2
, height2
,
810 /* compute values for decomposing pixel into color values: */
811 redMask
= Im
->red_mask
;
814 while ((redMask
& 1) == 0)
820 greenMask
= Im
->green_mask
;
823 while ((greenMask
& 1) == 0)
829 blueMask
= Im
->blue_mask
;
832 while ((blueMask
& 1) == 0)
838 /* scale each icon: */
839 for (y1
= 0, y2
= 0; (y1
< height1
) && (y2
< height2
); y1
+= iy
, y2
+= oy
)
841 for (x1
= 0, x2
= 0; (x1
< width1
) && (x2
< width2
); x1
+= ix
, x2
+= ox
)
843 ScaleIcon(Im
, Tmp
, x1
, y1
, x2
, y2
,
855 XImage
*ResizeImage(Display
*dpy
, XImage
*Im
,
856 int ix
, int iy
, int ox
, int oy
)
858 Visual
*visual
= DefaultVisual(dpy
, DefaultScreen(dpy
));
860 int width1
, height1
, width2
, height2
;
861 int x1
, x2
, y1
, y2
, Tx
, Ty
;
862 int *px1
, *px2
, *dx1
, *dx2
;
863 int *py1
, *py2
, *dy1
, *dy2
;
869 if (smoothRescaling
&& (ix
!= ox
|| iy
!= oy
) &&
870 (visual
->class == TrueColor
))
872 return ResizeImageSmooth(dpy
, Im
, ix
, iy
, ox
, oy
);
876 height1
= Im
->height
;
878 width2
= ox
* width1
/ ix
;
879 height2
= oy
* height1
/ iy
;
881 Data
= (char *)malloc(width2
* height2
* Im
->bits_per_pixel
/ 8);
883 Tmp
= XCreateImage(dpy
, visual
,
884 Im
->depth
, ZPixmap
, 0, Data
, width2
, height2
,
919 for (y1
= 0, y2
= 0; (y1
< height1
) && (y2
< height2
); )
923 for (x1
= 0, x2
= 0; (x1
< width1
) && (x2
< width2
); )
925 XPutPixel(Tmp
, x2
, y2
, XGetPixel(Im
, x1
, y1
));
950 #endif /* USE_GRAPHICS */
952 #endif /* USE_X11 || USE_XAW || USE_XPJ || USE_GTK */