OSDN Git Service

Merge pull request #2579 from Hourier/Make-Deceleration-Class
[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
80         gamma_table_ready = true;
81
82         /* Only need to build the table if gamma exists */
83         if (gamma_val) {
84             build_gamma_table(gamma_val);
85         }
86     }
87
88     /* Hack -- Gamma Correction */
89     if (gamma_val > 0) {
90         red = gamma_table[red];
91         green = gamma_table[green];
92         blue = gamma_table[blue];
93     }
94
95     /* Build the color */
96
97     xcolour.red = red * 255;
98     xcolour.green = green * 255;
99     xcolour.blue = blue * 255;
100     xcolour.flags = DoRed | DoGreen | DoBlue;
101
102 #ifdef USE_XFT
103     XftColor color;
104     XRenderColor xcol;
105     xcol.red = xcolour.red;
106     xcol.green = xcolour.green;
107     xcol.blue = xcolour.blue;
108     xcol.alpha = 0xFFFF;
109     if (!XftColorAllocValue(dpy, DefaultVisual(dpy, 0), cmap, &xcol, &color)) {
110         quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n", red, green, blue);
111     }
112
113     return color;
114 #else
115     /* Attempt to Allocate the Parsed color */
116     if (!(XAllocColor(dpy, cmap, &xcolour))) {
117         quit_fmt("Couldn't allocate bitmap color '#%02x%02x%02x'\n", red, green, blue);
118     }
119
120     return xcolour.pixel;
121 #endif
122 }
123
124 #ifndef USE_XFT
125
126 /*
127  * The Win32 "BITMAPFILEHEADER" type.
128  */
129 struct BITMAPFILEHEADER {
130     uint16_t bfType;
131     uint32_t bfSize;
132     uint16_t bfReserved1;
133     uint16_t bfReserved2;
134     uint32_t bfOffBits;
135 };
136
137 /*
138  * The Win32 "BITMAPINFOHEADER" type.
139  */
140 struct BITMAPINFOHEADER {
141     uint32_t biSize;
142     uint32_t biWidth;
143     uint32_t biHeight;
144     uint16_t biPlanes;
145     uint16_t biBitCount;
146     uint32_t biCompresion;
147     uint32_t biSizeImage;
148     uint32_t biXPelsPerMeter;
149     uint32_t biYPelsPerMeter;
150     uint32_t biClrUsed;
151     uint32_t biClrImportand;
152 };
153
154 /*
155  * The Win32 "RGBQUAD" type.
156  */
157 struct RGBQUAD {
158     unsigned char b, g, r;
159     unsigned char filler;
160 };
161
162 /*** Helper functions for system independent file loading. ***/
163
164 static byte get_byte(FILE *fff)
165 {
166     /* Get a character, and return it */
167     return getc(fff) & 0xFF;
168 }
169
170 static void rd_byte(FILE *fff, byte *ip)
171 {
172     *ip = get_byte(fff);
173 }
174
175 static void rd_u16b(FILE *fff, uint16_t *ip)
176 {
177     (*ip) = get_byte(fff);
178     (*ip) |= ((uint16_t)(get_byte(fff)) << 8);
179 }
180
181 static void rd_u32b(FILE *fff, uint32_t *ip)
182 {
183     (*ip) = get_byte(fff);
184     (*ip) |= ((uint32_t)(get_byte(fff)) << 8);
185     (*ip) |= ((uint32_t)(get_byte(fff)) << 16);
186     (*ip) |= ((uint32_t)(get_byte(fff)) << 24);
187 }
188
189 /*
190  * Read a Win32 BMP file.
191  *
192  * This function replaces the old ReadRaw and RemapColors functions.
193  *
194  * Assumes that the bitmap has a size such that no padding is needed in
195  * various places.  Currently only handles bitmaps with 3 to 256 colors.
196  */
197 static XImage *ReadBMP(Display *dpy, char *Name)
198 {
199     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
200
201     int depth = DefaultDepth(dpy, DefaultScreen(dpy));
202
203     FILE *f;
204
205     BITMAPFILEHEADER fileheader;
206     BITMAPINFOHEADER infoheader;
207
208     XImage *Res = nullptr;
209
210     char *Data;
211
212     int ncol;
213
214     int total;
215
216     int i, j;
217
218     int x, y;
219
220     unsigned long clr_pixels[256];
221
222     /* Open the BMP file */
223     f = fopen(Name, "r");
224
225     /* No such file */
226     if (f == nullptr) {
227         return nullptr;
228     }
229
230     /* Read the "BITMAPFILEHEADER" */
231     rd_u16b(f, &(fileheader.bfType));
232     rd_u32b(f, &(fileheader.bfSize));
233     rd_u16b(f, &(fileheader.bfReserved1));
234     rd_u16b(f, &(fileheader.bfReserved2));
235     rd_u32b(f, &(fileheader.bfOffBits));
236
237     /* Read the "BITMAPINFOHEADER" */
238     rd_u32b(f, &(infoheader.biSize));
239     rd_u32b(f, &(infoheader.biWidth));
240     rd_u32b(f, &(infoheader.biHeight));
241     rd_u16b(f, &(infoheader.biPlanes));
242     rd_u16b(f, &(infoheader.biBitCount));
243     rd_u32b(f, &(infoheader.biCompresion));
244     rd_u32b(f, &(infoheader.biSizeImage));
245     rd_u32b(f, &(infoheader.biXPelsPerMeter));
246     rd_u32b(f, &(infoheader.biYPelsPerMeter));
247     rd_u32b(f, &(infoheader.biClrUsed));
248     rd_u32b(f, &(infoheader.biClrImportand));
249
250     /* Verify the header */
251     if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) {
252         quit_fmt("Incorrect BMP file format %s", Name);
253     }
254
255     /* The two headers above occupy 54 bytes total */
256     /* The "bfOffBits" field says where the data starts */
257     /* The "biClrUsed" field does not seem to be reliable */
258     /* Compute number of colors recorded */
259     ncol = (fileheader.bfOffBits - 54) / 4;
260
261     for (i = 0; i < ncol; i++) {
262         RGBQUAD clrg;
263
264         /* Read an "RGBQUAD" */
265         rd_byte(f, &(clrg.b));
266         rd_byte(f, &(clrg.g));
267         rd_byte(f, &(clrg.r));
268         rd_byte(f, &(clrg.filler));
269
270         /* Analyze the color */
271         clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
272     }
273
274     /* Determine total bytes needed for image */
275     i = 1;
276     j = (depth - 1) >> 2;
277     while (j >>= 1) {
278         i <<= 1;
279     }
280     total = infoheader.biWidth * infoheader.biHeight * i;
281
282     /* Allocate image memory */
283     Data = (char *)malloc(total);
284
285     Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/, Data, infoheader.biWidth, infoheader.biHeight, 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
286
287     /* Failure */
288     if (Res == nullptr) {
289         free(Data);
290         fclose(f);
291         return nullptr;
292     }
293
294     for (y = 0; y < static_cast<int>(infoheader.biHeight); y++) {
295         int y2 = infoheader.biHeight - y - 1;
296
297         for (x = 0; x < static_cast<int>(infoheader.biWidth); x++) {
298             int ch = getc(f);
299
300             /* Verify not at end of file XXX XXX */
301             if (feof(f)) {
302                 quit_fmt("Unexpected end of file in %s", Name);
303             }
304
305             if (infoheader.biBitCount == 24) {
306                 int c2 = getc(f);
307                 int c3 = getc(f);
308
309                 /* Verify not at end of file XXX XXX */
310                 if (feof(f)) {
311                     quit_fmt("Unexpected end of file in %s", Name);
312                 }
313
314                 XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
315             } else if (infoheader.biBitCount == 8) {
316                 XPutPixel(Res, x, y2, clr_pixels[ch]);
317             } else if (infoheader.biBitCount == 4) {
318                 XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
319                 x++;
320                 XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
321             } else {
322                 /* Technically 1 bit is legal too */
323                 quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name);
324             }
325         }
326     }
327
328     fclose(f);
329
330     return Res;
331 }
332
333 /* ========================================================*/
334 /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
335 /* ========================================================*/
336
337 /*
338  * to save ourselves some labour, define a maximum expected icon width here:
339  */
340 #define MAX_ICON_WIDTH 32
341
342 /* some static variables for composing and decomposing pixel values into
343  * red, green and blue values
344  */
345 static unsigned long redMask, greenMask, blueMask;
346 static int redShift, greenShift, blueShift;
347
348 /*
349  * Use smooth rescaling?
350  */
351 static bool smoothRescaling = true;
352
353 /*
354  * GetScaledRow reads a scan from the given XImage, scales it smoothly
355  * and returns the red, green and blue values in arrays.
356  * The values in this arrays must be divided by a certain value that is
357  * calculated in ScaleIcon.
358  * x, y is the position, iw is the input width and ow the output width
359  * redScan, greenScan and blueScan must be sufficiently sized
360  */
361 static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
362 {
363     int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
364     unsigned long pix;
365     int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
366     bool getNextPix;
367
368     if (iw == ow) {
369         /* unscaled */
370         for (xi = 0; xi < ow; xi++) {
371             pix = XGetPixel(Im, x + xi, y);
372             redScan[xi] = (pix >> redShift) & redMask;
373             greenScan[xi] = (pix >> greenShift) & greenMask;
374             blueScan[xi] = (pix >> blueShift) & blueMask;
375         }
376     } else if (iw < ow) {
377         /* scaling by subsampling (grow) */
378         iw--;
379         ow--;
380         /* read first pixel: */
381         pix = XGetPixel(Im, x, y);
382         nextRed = (pix >> redShift) & redMask;
383         nextGreen = (pix >> greenShift) & greenMask;
384         nextBlue = (pix >> blueShift) & blueMask;
385         prevRed = nextRed;
386         prevGreen = nextGreen;
387         prevBlue = nextBlue;
388         /* si and sifrac give the subsampling position: */
389         si = x;
390         sifrac = 0;
391         /* getNextPix tells us, that we need the next pixel */
392         getNextPix = true;
393
394         for (xi = 0; xi <= ow; xi++) {
395             if (getNextPix) {
396                 prevRed = nextRed;
397                 prevGreen = nextGreen;
398                 prevBlue = nextBlue;
399                 if (xi < ow) {
400                     /* only get next pixel if in same icon */
401                     pix = XGetPixel(Im, si + 1, y);
402                     nextRed = (pix >> redShift) & redMask;
403                     nextGreen = (pix >> greenShift) & greenMask;
404                     nextBlue = (pix >> blueShift) & blueMask;
405                 }
406             }
407
408             /* calculate subsampled color values: */
409             /* division by ow occurs in ScaleIcon */
410             redScan[xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
411             greenScan[xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
412             blueScan[xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
413
414             /* advance sampling position: */
415             sifrac += iw;
416             if (sifrac >= ow) {
417                 si++;
418                 sifrac -= ow;
419                 getNextPix = true;
420             } else {
421                 getNextPix = true;
422             }
423         }
424     } else {
425         /* scaling by averaging (shrink) */
426         /* width of an output pixel in input pixels: */
427         addWhole = iw / ow;
428         addFrac = iw % ow;
429         /* start position of the first output pixel: */
430         si = x;
431         sifrac = 0;
432         /* get first input pixel: */
433         pix = XGetPixel(Im, x, y);
434         nextRed = (pix >> redShift) & redMask;
435         nextGreen = (pix >> greenShift) & greenMask;
436         nextBlue = (pix >> blueShift) & blueMask;
437         for (xi = 0; xi < ow; xi++) {
438             /* find endpoint of the current output pixel: */
439             ci = si + addWhole;
440             cifrac = sifrac + addFrac;
441             if (cifrac >= ow) {
442                 ci++;
443                 cifrac -= ow;
444             }
445             /* take fraction of current input pixel (starting segment): */
446             redScan[xi] = nextRed * (ow - sifrac);
447             greenScan[xi] = nextGreen * (ow - sifrac);
448             blueScan[xi] = nextBlue * (ow - sifrac);
449             si++;
450             /* add values for whole pixels: */
451             while (si < ci) {
452                 pix = XGetPixel(Im, si, y);
453                 redScan[xi] += ((pix >> redShift) & redMask) * ow;
454                 greenScan[xi] += ((pix >> greenShift) & greenMask) * ow;
455                 blueScan[xi] += ((pix >> blueShift) & blueMask) * ow;
456                 si++;
457             }
458             /* add fraction of current input pixel (ending segment): */
459             if (xi < ow - 1) {
460                 /* only get next pixel if still in icon: */
461                 pix = XGetPixel(Im, si, y);
462                 nextRed = (pix >> redShift) & redMask;
463                 nextGreen = (pix >> greenShift) & greenMask;
464                 nextBlue = (pix >> blueShift) & blueMask;
465             }
466             sifrac = cifrac;
467             if (sifrac > 0) {
468                 redScan[xi] += nextRed * sifrac;
469                 greenScan[xi] += nextGreen * sifrac;
470                 blueScan[xi] += nextBlue * sifrac;
471             }
472         }
473     }
474 }
475
476 /*
477  * PutRGBScan takes arrays for red, green and blue and writes pixel values
478  * according to this values in the XImage-structure. w is the number of
479  * pixels to write and div is the value by which all red/green/blue values
480  * are divided first.
481  */
482 static void PutRGBScan(XImage *Im, int x, int y, int w, int div, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan)
483 {
484     int xi;
485     unsigned long pix;
486     unsigned long adj = div / 2;
487     for (xi = 0; xi < w; xi++) {
488         pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) + ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) + ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
489         XPutPixel(Im, x + xi, y, pix);
490     }
491 }
492
493 /*
494  * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
495  * locate (x2, y2).
496  * Source size is (ix, iy) and destination size is (ox, oy).
497  * It does this by getting icon scan line from GetScaledScan and handling
498  * them the same way as pixels are handled in GetScaledScan.
499  * This even allows icons to be scaled differently in horizontal and
500  * vertical directions (eg. shrink horizontal, grow vertical).
501  */
502 static void ScaleIcon(XImage *ImIn, XImage *ImOut, int x1, int y1, int x2, int y2, int ix, int iy, int ox, int oy)
503 {
504     int div;
505     int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
506
507     /* buffers for pixel rows: */
508     unsigned long prevRed[MAX_ICON_WIDTH];
509     unsigned long prevGreen[MAX_ICON_WIDTH];
510     unsigned long prevBlue[MAX_ICON_WIDTH];
511     unsigned long nextRed[MAX_ICON_WIDTH];
512     unsigned long nextGreen[MAX_ICON_WIDTH];
513     unsigned long nextBlue[MAX_ICON_WIDTH];
514     unsigned long tempRed[MAX_ICON_WIDTH];
515     unsigned long tempGreen[MAX_ICON_WIDTH];
516     unsigned long tempBlue[MAX_ICON_WIDTH];
517
518     bool getNextRow;
519
520     /* get divider value for the horizontal scaling: */
521     if (ix == ox) {
522         div = 1;
523     } else if (ix < ox) {
524         div = ox - 1;
525     } else {
526         div = ix;
527     }
528
529     if (iy == oy) {
530         /* no scaling needed vertically: */
531         for (yi = 0; yi < oy; yi++) {
532             GetScaledRow(ImIn, x1, y1 + yi, ix, ox, tempRed, tempGreen, tempBlue);
533             PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
534         }
535     } else if (iy < oy) {
536         /* scaling by subsampling (grow): */
537         iy--;
538         oy--;
539         div *= oy;
540         /* get first row: */
541         GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
542         /* si and sifrac give the subsampling position: */
543         si = y1;
544         sifrac = 0;
545         /* getNextRow tells us, that we need the next row */
546         getNextRow = true;
547         for (yi = 0; yi <= oy; yi++) {
548             if (getNextRow) {
549                 for (xi = 0; xi < ox; xi++) {
550                     prevRed[xi] = nextRed[xi];
551                     prevGreen[xi] = nextGreen[xi];
552                     prevBlue[xi] = nextBlue[xi];
553                 }
554                 if (yi < oy) {
555                     /* only get next row if in same icon */
556                     GetScaledRow(ImIn, x1, si + 1, ix, ox, nextRed, nextGreen, nextBlue);
557                 }
558             }
559
560             /* calculate subsampled color values: */
561             /* division by oy occurs in PutRGBScan */
562             for (xi = 0; xi < ox; xi++) {
563                 tempRed[xi] = (prevRed[xi] * (oy - sifrac) + nextRed[xi] * sifrac);
564                 tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) + nextGreen[xi] * sifrac);
565                 tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) + nextBlue[xi] * sifrac);
566             }
567
568             /* write row to output image: */
569             PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
570
571             /* advance sampling position: */
572             sifrac += iy;
573             if (sifrac >= oy) {
574                 si++;
575                 sifrac -= oy;
576                 getNextRow = true;
577             } else {
578                 getNextRow = true;
579             }
580         }
581     } else {
582         /* scaling by averaging (shrink) */
583         div *= iy;
584         /* height of a output row in input rows: */
585         addWhole = iy / oy;
586         addFrac = iy % oy;
587         /* start position of the first output row: */
588         si = y1;
589         sifrac = 0;
590         /* get first input row: */
591         GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
592         for (yi = 0; yi < oy; yi++) {
593             /* find endpoint of the current output row: */
594             ci = si + addWhole;
595             cifrac = sifrac + addFrac;
596             if (cifrac >= oy) {
597                 ci++;
598                 cifrac -= oy;
599             }
600             /* take fraction of current input row (starting segment): */
601             for (xi = 0; xi < ox; xi++) {
602                 tempRed[xi] = nextRed[xi] * (oy - sifrac);
603                 tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
604                 tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
605             }
606             si++;
607             /* add values for whole pixels: */
608             while (si < ci) {
609                 GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue);
610                 for (xi = 0; xi < ox; xi++) {
611                     tempRed[xi] += nextRed[xi] * oy;
612                     tempGreen[xi] += nextGreen[xi] * oy;
613                     tempBlue[xi] += nextBlue[xi] * oy;
614                 }
615                 si++;
616             }
617             /* add fraction of current input row (ending segment): */
618             if (yi < oy - 1) {
619                 /* only get next row if still in icon: */
620                 GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue);
621             }
622             sifrac = cifrac;
623             for (xi = 0; xi < ox; xi++) {
624                 tempRed[xi] += nextRed[xi] * sifrac;
625                 tempGreen[xi] += nextGreen[xi] * sifrac;
626                 tempBlue[xi] += nextBlue[xi] * sifrac;
627             }
628             /* write row to output image: */
629             PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue);
630         }
631     }
632 }
633
634 static XImage *ResizeImageSmooth(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
635 {
636     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
637
638     int width1, height1, width2, height2;
639     int x1, x2, y1, y2;
640
641     XImage *Tmp;
642
643     char *Data;
644
645     width1 = Im->width;
646     height1 = Im->height;
647
648     width2 = ox * width1 / ix;
649     height2 = oy * height1 / iy;
650
651     Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
652
653     Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
654
655     /* compute values for decomposing pixel into color values: */
656     redMask = Im->red_mask;
657     redShift = 0;
658     while ((redMask & 1) == 0) {
659         redShift++;
660         redMask >>= 1;
661     }
662     greenMask = Im->green_mask;
663     greenShift = 0;
664     while ((greenMask & 1) == 0) {
665         greenShift++;
666         greenMask >>= 1;
667     }
668     blueMask = Im->blue_mask;
669     blueShift = 0;
670     while ((blueMask & 1) == 0) {
671         blueShift++;
672         blueMask >>= 1;
673     }
674
675     /* scale each icon: */
676     for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy) {
677         for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox) {
678             ScaleIcon(Im, Tmp, x1, y1, x2, y2, ix, iy, ox, oy);
679         }
680     }
681
682     return Tmp;
683 }
684
685 /*
686  * Resize an image.
687  */
688 static XImage *ResizeImage(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy)
689 {
690     Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
691
692     int width1, height1, width2, height2;
693     volatile int x1, x2, y1, y2, Tx, Ty;
694     volatile int *px1, *px2, *dx1, *dx2;
695     volatile int *py1, *py2, *dy1, *dy2;
696
697     XImage *Tmp;
698
699     char *Data;
700
701     if (smoothRescaling && (ix != ox || iy != oy) && visual->c_class == TrueColor) {
702         return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
703     }
704
705     width1 = Im->width;
706     height1 = Im->height;
707
708     width2 = ox * width1 / ix;
709     height2 = oy * height1 / iy;
710
711     Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
712
713     Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0);
714
715     if (ix > ox) {
716         px1 = &x1;
717         px2 = &x2;
718         dx1 = &ix;
719         dx2 = &ox;
720     } else {
721         px1 = &x2;
722         px2 = &x1;
723         dx1 = &ox;
724         dx2 = &ix;
725     }
726
727     if (iy > oy) {
728         py1 = &y1;
729         py2 = &y2;
730         dy1 = &iy;
731         dy2 = &oy;
732     } else {
733         py1 = &y2;
734         py2 = &y1;
735         dy1 = &oy;
736         dy2 = &iy;
737     }
738
739     Ty = *dy1 / 2;
740
741     for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2);) {
742         Tx = *dx1 / 2;
743
744         for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2);) {
745             XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
746
747             (*px1)++;
748
749             Tx -= *dx2;
750             if (Tx < 0) {
751                 Tx += *dx1;
752                 (*px2)++;
753             }
754         }
755
756         (*py1)++;
757
758         Ty -= *dy2;
759         if (Ty < 0) {
760             Ty += *dy1;
761             (*py2)++;
762         }
763     }
764
765     return Tmp;
766 }
767
768 #endif /* !USE_XFT */
769
770 #endif /* USE_X11 */