OSDN Git Service

Merge pull request #2241 from sikabane-works/release/3.0.0Alpha53
[hengbandforosx/hengbandosx.git] / src / maid-x11.cpp
1 /* File: maid-x11.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
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.
9  */
10
11 #ifdef USE_X11
12
13 #include "main/x11-gamma-builder.h"
14 #include <math.h>
15
16 /*
17  * This file defines some "XImage" manipulation functions for X11.
18  *
19  * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
20  *
21  * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
22  *
23  * Major fixes and cleanup by Ben Harrison (benh@phial.com).
24  *
25  * This file is designed to be "included" by "main-x11.c",
26  * which will have already "included" several relevant header files.
27  */
28
29 #ifndef IsModifierKey
30
31 /*
32  * Keysym macros, used on Keysyms to test for classes of symbols
33  * These were stolen from one of the X11 header files
34  *
35  * Also appears in "main-x11.c".
36  */
37
38 #define IsKeypadKey(keysym) (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
39
40 #define IsCursorKey(keysym) (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
41
42 #define IsPFKey(keysym) (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
43
44 #define IsFunctionKey(keysym) (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
45
46 #define IsMiscFunctionKey(keysym) (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
47
48 #define IsModifierKey(keysym) (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
49
50 #endif /* IsModifierKey */
51
52 /*
53  * Checks if the keysym is a special key or a normal key
54  * Assume that XK_MISCELLANY keysyms are special
55  *
56  * Also appears in "main-x11.c".
57  */
58 #define IsSpecialKey(keysym) ((unsigned)(keysym) >= 0xFF00)
59
60 static bool gamma_table_ready = true;
61 static int gamma_val = 0;
62
63 /*
64  * Hack -- Convert an RGB value to an X11 Pixel, or die.
65  */
66 #ifdef USE_XFT
67 static XftColor create_pixel(Display *dpy, byte red, byte green, byte blue)
68 #else
69 static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
70 #endif
71 {
72     Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
73     XColor xcolour;
74     if (!gamma_table_ready) {
75         concptr str = getenv("ANGBAND_X11_GAMMA");
76         if (str != nullptr)
77             gamma_val = atoi(str);
78
79         gamma_table_ready = true;
80
81         /* Only need to build the table if gamma exists */
82         if (gamma_val)
83             build_gamma_table(gamma_val);
84     }
85
86     /* Hack -- Gamma Correction */
87     if (gamma_val > 0) {
88         red = gamma_table[red];
89         green = gamma_table[green];
90         blue = gamma_table[blue];
91     }
92
93     /* Build the color */
94
95     xcolour.red = red * 255;
96     xcolour.green = green * 255;
97     xcolour.blue = blue * 255;
98     xcolour.flags = DoRed | DoGreen | DoBlue;
99
100 #ifdef USE_XFT
101     XftColor color;
102     XRenderColor xcol;
103     xcol.red = xcolour.red;
104     xcol.green = xcolour.green;
105     xcol.blue = xcolour.blue;
106     xcol.alpha = 0xFFFF;
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);
109     }
110
111     return color;
112 #else
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);
116     }
117
118     return xcolour.pixel;
119 #endif
120 }
121
122 #ifndef USE_XFT
123
124 /*
125  * The Win32 "BITMAPFILEHEADER" type.
126  */
127 struct BITMAPFILEHEADER {
128     uint16_t bfType;
129     uint32_t bfSize;
130     uint16_t bfReserved1;
131     uint16_t bfReserved2;
132     uint32_t bfOffBits;
133 };
134
135 /*
136  * The Win32 "BITMAPINFOHEADER" type.
137  */
138 struct BITMAPINFOHEADER {
139     uint32_t biSize;
140     uint32_t biWidth;
141     uint32_t biHeight;
142     uint16_t biPlanes;
143     uint16_t biBitCount;
144     uint32_t biCompresion;
145     uint32_t biSizeImage;
146     uint32_t biXPelsPerMeter;
147     uint32_t biYPelsPerMeter;
148     uint32_t biClrUsed;
149     uint32_t biClrImportand;
150 };
151
152 /*
153  * The Win32 "RGBQUAD" type.
154  */
155 struct RGBQUAD {
156     unsigned char b, g, r;
157     unsigned char filler;
158 };
159
160 /*** Helper functions for system independent file loading. ***/
161
162 static byte get_byte(FILE *fff)
163 {
164     /* Get a character, and return it */
165     return getc(fff) & 0xFF;
166 }
167
168 static void rd_byte(FILE *fff, byte *ip)
169 {
170     *ip = get_byte(fff);
171 }
172
173 static void rd_u16b(FILE *fff, uint16_t *ip)
174 {
175     (*ip) = get_byte(fff);
176     (*ip) |= ((uint16_t)(get_byte(fff)) << 8);
177 }
178
179 static void rd_u32b(FILE *fff, uint32_t *ip)
180 {
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);
185 }
186
187 /*
188  * Read a Win32 BMP file.
189  *
190  * This function replaces the old ReadRaw and RemapColors functions.
191  *
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.
194  */
195 static XImage *ReadBMP(Display *dpy, char *Name)
196 {
197     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
198
199     int depth = DefaultDepth(dpy, DefaultScreen(dpy));
200
201     FILE *f;
202
203     BITMAPFILEHEADER fileheader;
204     BITMAPINFOHEADER infoheader;
205
206     XImage *Res = nullptr;
207
208     char *Data;
209
210     int ncol;
211
212     int total;
213
214     int i, j;
215
216     int x, y;
217
218     unsigned long clr_pixels[256];
219
220     /* Open the BMP file */
221     f = fopen(Name, "r");
222
223     /* No such file */
224     if (f == nullptr) {
225         return nullptr;
226     }
227
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));
234
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));
247
248     /* Verify the header */
249     if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) {
250         quit_fmt("Incorrect BMP file format %s", Name);
251     }
252
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;
258
259     for (i = 0; i < ncol; i++) {
260         RGBQUAD clrg;
261
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));
267
268         /* Analyze the color */
269         clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
270     }
271
272     /* Determine total bytes needed for image */
273     i = 1;
274     j = (depth - 1) >> 2;
275     while (j >>= 1)
276         i <<= 1;
277     total = infoheader.biWidth * infoheader.biHeight * i;
278
279     /* Allocate image memory */
280     Data = (char*)malloc(total);
281
282     Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/, Data, infoheader.biWidth, infoheader.biHeight, 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
283
284     /* Failure */
285     if (Res == nullptr) {
286         free(Data);
287         fclose(f);
288         return nullptr;
289     }
290
291     for (y = 0; y < static_cast<int>(infoheader.biHeight); y++) {
292         int y2 = infoheader.biHeight - y - 1;
293
294         for (x = 0; x < static_cast<int>(infoheader.biWidth); x++) {
295             int ch = getc(f);
296
297             /* Verify not at end of file XXX XXX */
298             if (feof(f))
299                 quit_fmt("Unexpected end of file in %s", Name);
300
301             if (infoheader.biBitCount == 24) {
302                 int c2 = getc(f);
303                 int c3 = getc(f);
304
305                 /* Verify not at end of file XXX XXX */
306                 if (feof(f))
307                     quit_fmt("Unexpected end of file in %s", Name);
308
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]);
314                 x++;
315                 XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
316             } else {
317                 /* Technically 1 bit is legal too */
318                 quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name);
319             }
320         }
321     }
322
323     fclose(f);
324
325     return Res;
326 }
327
328 /* ========================================================*/
329 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
330 /* ========================================================*/
331
332 /*
333  * to save ourselves some labour, define a maximum expected icon width here:
334  */
335 #define MAX_ICON_WIDTH 32
336
337 /* some static variables for composing and decomposing pixel values into
338  * red, green and blue values
339  */
340 static unsigned long redMask, greenMask, blueMask;
341 static int redShift, greenShift, blueShift;
342
343 /*
344  * Use smooth rescaling?
345  */
346 static bool smoothRescaling = true;
347
348 /*
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
355  */
356 static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
357 {
358     int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
359     unsigned long pix;
360     int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
361     bool getNextPix;
362
363     if (iw == ow) {
364         /* unscaled */
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;
370         }
371     } else if (iw < ow) {
372         /* scaling by subsampling (grow) */
373         iw--;
374         ow--;
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;
380         prevRed = nextRed;
381         prevGreen = nextGreen;
382         prevBlue = nextBlue;
383         /* si and sifrac give the subsampling position: */
384         si = x;
385         sifrac = 0;
386         /* getNextPix tells us, that we need the next pixel */
387         getNextPix = true;
388
389         for (xi = 0; xi <= ow; xi++) {
390             if (getNextPix) {
391                 prevRed = nextRed;
392                 prevGreen = nextGreen;
393                 prevBlue = nextBlue;
394                 if (xi < ow) {
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;
400                 }
401             }
402
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;
408
409             /* advance sampling position: */
410             sifrac += iw;
411             if (sifrac >= ow) {
412                 si++;
413                 sifrac -= ow;
414                 getNextPix = true;
415             } else {
416                 getNextPix = true;
417             }
418         }
419     } else {
420         /* scaling by averaging (shrink) */
421         /* width of an output pixel in input pixels: */
422         addWhole = iw / ow;
423         addFrac = iw % ow;
424         /* start position of the first output pixel: */
425         si = x;
426         sifrac = 0;
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: */
434             ci = si + addWhole;
435             cifrac = sifrac + addFrac;
436             if (cifrac >= ow) {
437                 ci++;
438                 cifrac -= ow;
439             }
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);
444             si++;
445             /* add values for whole pixels: */
446             while (si < ci) {
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;
451                 si++;
452             }
453             /* add fraction of current input pixel (ending segment): */
454             if (xi < ow - 1) {
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;
460             }
461             sifrac = cifrac;
462             if (sifrac > 0) {
463                 redScan[xi] += nextRed * sifrac;
464                 greenScan[xi] += nextGreen * sifrac;
465                 blueScan[xi] += nextBlue * sifrac;
466             }
467         }
468     }
469 }
470
471 /*
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
475  * are divided first.
476  */
477 static void PutRGBScan(XImage *Im, int x, int y, int w, int div, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
478 {
479     int xi;
480     unsigned long pix;
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);
486     }
487 }
488
489 /*
490  * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
491  * locate (x2, y2).
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).
497  */
498 static void ScaleIcon(XImage *ImIn, XImage *ImOut, int x1, int y1, int x2, int y2, int ix, int iy, int ox, int oy)
499 {
500     int div;
501     int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
502
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];
513
514     bool getNextRow;
515
516     /* get divider value for the horizontal scaling: */
517     if (ix == ox)
518         div = 1;
519     else if (ix < ox)
520         div = ox - 1;
521     else
522         div = ix;
523
524     if (iy == oy) {
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);
529         }
530     } else if (iy < oy) {
531         /* scaling by subsampling (grow): */
532         iy--;
533         oy--;
534         div *= oy;
535         /* get first row: */
536         GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
537         /* si and sifrac give the subsampling position: */
538         si = y1;
539         sifrac = 0;
540         /* getNextRow tells us, that we need the next row */
541         getNextRow = true;
542         for (yi = 0; yi <= oy; yi++) {
543             if (getNextRow) {
544                 for (xi = 0; xi < ox; xi++) {
545                     prevRed[xi] = nextRed[xi];
546                     prevGreen[xi] = nextGreen[xi];
547                     prevBlue[xi] = nextBlue[xi];
548                 }
549                 if (yi < oy) {
550                     /* only get next row if in same icon */
551                     GetScaledRow(ImIn, x1, si + 1, ix, ox, nextRed, nextGreen, nextBlue);
552                 }
553             }
554
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);
561             }
562
563             /* write row to output image: */
564             PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
565
566             /* advance sampling position: */
567             sifrac += iy;
568             if (sifrac >= oy) {
569                 si++;
570                 sifrac -= oy;
571                 getNextRow = true;
572             } else {
573                 getNextRow = true;
574             }
575         }
576     } else {
577         /* scaling by averaging (shrink) */
578         div *= iy;
579         /* height of a output row in input rows: */
580         addWhole = iy / oy;
581         addFrac = iy % oy;
582         /* start position of the first output row: */
583         si = y1;
584         sifrac = 0;
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: */
589             ci = si + addWhole;
590             cifrac = sifrac + addFrac;
591             if (cifrac >= oy) {
592                 ci++;
593                 cifrac -= oy;
594             }
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);
600             }
601             si++;
602             /* add values for whole pixels: */
603             while (si < ci) {
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;
609                 }
610                 si++;
611             }
612             /* add fraction of current input row (ending segment): */
613             if (yi < oy - 1) {
614                 /* only get next row if still in icon: */
615                 GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue);
616             }
617             sifrac = cifrac;
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;
622             }
623             /* write row to output image: */
624             PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
625         }
626     }
627 }
628
629 static XImage *ResizeImageSmooth(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
630 {
631     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
632
633     int width1, height1, width2, height2;
634     int x1, x2, y1, y2;
635
636     XImage *Tmp;
637
638     char *Data;
639
640     width1 = Im->width;
641     height1 = Im->height;
642
643     width2 = ox * width1 / ix;
644     height2 = oy * height1 / iy;
645
646     Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
647
648     Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
649
650     /* compute values for decomposing pixel into color values: */
651     redMask = Im->red_mask;
652     redShift = 0;
653     while ((redMask & 1) == 0) {
654         redShift++;
655         redMask >>= 1;
656     }
657     greenMask = Im->green_mask;
658     greenShift = 0;
659     while ((greenMask & 1) == 0) {
660         greenShift++;
661         greenMask >>= 1;
662     }
663     blueMask = Im->blue_mask;
664     blueShift = 0;
665     while ((blueMask & 1) == 0) {
666         blueShift++;
667         blueMask >>= 1;
668     }
669
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);
674         }
675     }
676
677     return Tmp;
678 }
679
680 /*
681  * Resize an image.
682  */
683 static XImage *ResizeImage(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
684 {
685     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
686
687     int width1, height1, width2, height2;
688     volatile int x1, x2, y1, y2, Tx, Ty;
689     volatile int *px1, *px2, *dx1, *dx2;
690     volatile int *py1, *py2, *dy1, *dy2;
691
692     XImage *Tmp;
693
694     char *Data;
695
696     if (smoothRescaling && (ix != ox || iy != oy) && visual->c_class == TrueColor) {
697         return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
698     }
699
700     width1 = Im->width;
701     height1 = Im->height;
702
703     width2 = ox * width1 / ix;
704     height2 = oy * height1 / iy;
705
706     Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
707
708     Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
709
710     if (ix > ox) {
711         px1 = &x1;
712         px2 = &x2;
713         dx1 = &ix;
714         dx2 = &ox;
715     } else {
716         px1 = &x2;
717         px2 = &x1;
718         dx1 = &ox;
719         dx2 = &ix;
720     }
721
722     if (iy > oy) {
723         py1 = &y1;
724         py2 = &y2;
725         dy1 = &iy;
726         dy2 = &oy;
727     } else {
728         py1 = &y2;
729         py2 = &y1;
730         dy1 = &oy;
731         dy2 = &iy;
732     }
733
734     Ty = *dy1 / 2;
735
736     for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2);) {
737         Tx = *dx1 / 2;
738
739         for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2);) {
740             XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
741
742             (*px1)++;
743
744             Tx -= *dx2;
745             if (Tx < 0) {
746                 Tx += *dx1;
747                 (*px2)++;
748             }
749         }
750
751         (*py1)++;
752
753         Ty -= *dy2;
754         if (Ty < 0) {
755             Ty += *dy1;
756             (*py2)++;
757         }
758     }
759
760     return Tmp;
761 }
762
763 #endif /* !USE_XFT */
764
765 #endif /* USE_X11 */