diff options
Diffstat (limited to 'src/maid-x11.c')
-rwxr-xr-x | src/maid-x11.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/src/maid-x11.c b/src/maid-x11.c new file mode 100755 index 00000000..86df2119 --- /dev/null +++ b/src/maid-x11.c @@ -0,0 +1,855 @@ +/* File: maid-x11.c */ + +/* + * Copyright (c) 1997 Ben Harrison, and others + * + * This software may be copied and distributed for educational, research, + * and not for profit purposes provided that this copyright and statement + * are included in all such copies. + */ + +#if defined(USE_X11) || defined(USE_XAW) + +/* + * This file defines some "XImage" manipulation functions for X11. + * + * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr). + * + * BMP format support by Denis Eropkin (denis@dream.homepage.ru). + * + * Major fixes and cleanup by Ben Harrison (benh@phial.com). + * + * This file is designed to be "included" by "main-x11.c" or "main-xaw.c", + * which will have already "included" several relevant header files. + */ + +#ifndef IsModifierKey + +/* + * Keysym macros, used on Keysyms to test for classes of symbols + * These were stolen from one of the X11 header files + * + * Also appears in "main-x11.c". + */ + +#define IsKeypadKey(keysym) \ +(((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal)) + +#define IsCursorKey(keysym) \ +(((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select)) + +#define IsPFKey(keysym) \ +(((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4)) + +#define IsFunctionKey(keysym) \ +(((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35)) + +#define IsMiscFunctionKey(keysym) \ +(((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space)) + +#define IsModifierKey(keysym) \ +(((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R)) + +#endif /* IsModifierKey */ + + +/* + * Checks if the keysym is a special key or a normal key + * Assume that XK_MISCELLANY keysyms are special + * + * Also appears in "main-x11.c". + */ +#define IsSpecialKey(keysym) \ +((unsigned)(keysym) >= 0xFF00) + + +/* + * Hack -- Convert an RGB value to an X11 Pixel, or die. + */ +static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue) +{ + Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)); + + char cname[8]; + + XColor xcolour; + + /* Build the color */ + + xcolour.red = red * 255 + red; + xcolour.green = green * 255 + green; + xcolour.blue = blue * 255 + blue; + xcolour.flags = DoRed | DoGreen | DoBlue; + + /* Attempt to Allocate the Parsed color */ + if (!(XAllocColor(dpy, cmap, &xcolour))) + { + quit_fmt("Couldn't allocate bitmap color '%s'\n", cname); + } + + return (xcolour.pixel); +} + + + +#ifdef USE_GRAPHICS + +/* + * The Win32 "BITMAPFILEHEADER" type. + */ +typedef struct BITMAPFILEHEADER +{ + u16b bfType; + u32b bfSize; + u16b bfReserved1; + u16b bfReserved2; + u32b bfOffBits; +} +BITMAPFILEHEADER; + + +/* + * The Win32 "BITMAPINFOHEADER" type. + */ +typedef struct BITMAPINFOHEADER +{ + u32b biSize; + u32b biWidth; + u32b biHeight; + u16b biPlanes; + u16b biBitCount; + u32b biCompresion; + u32b biSizeImage; + u32b biXPelsPerMeter; + u32b biYPelsPerMeter; + u32b biClrUsed; + u32b biClrImportand; +} +BITMAPINFOHEADER; + +/* + * The Win32 "RGBQUAD" type. + */ +typedef struct RGBQUAD +{ + unsigned char b, g, r; + unsigned char filler; +} +RGBQUAD; + + +/*** Helper functions for system independent file loading. ***/ + +static byte get_byte(FILE *fff) +{ + /* Get a character, and return it */ + return (getc(fff) & 0xFF); +} + +static void rd_byte(FILE *fff, byte *ip) +{ + *ip = get_byte(fff); +} + +static void rd_u16b(FILE *fff, u16b *ip) +{ + (*ip) = get_byte(fff); + (*ip) |= ((u16b)(get_byte(fff)) << 8); +} + +static void rd_u32b(FILE *fff, u32b *ip) +{ + (*ip) = get_byte(fff); + (*ip) |= ((u32b)(get_byte(fff)) << 8); + (*ip) |= ((u32b)(get_byte(fff)) << 16); + (*ip) |= ((u32b)(get_byte(fff)) << 24); +} + + +/* + * Read a Win32 BMP file. + * + * This function replaces the old ReadRaw and RemapColors functions. + * + * Assumes that the bitmap has a size such that no padding is needed in + * various places. Currently only handles bitmaps with 3 to 256 colors. + */ +static XImage *ReadBMP(Display *dpy, char *Name) +{ + Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); + + int depth = DefaultDepth(dpy, DefaultScreen(dpy)); + + FILE *f; + + BITMAPFILEHEADER fileheader; + BITMAPINFOHEADER infoheader; + + XImage *Res = NULL; + + char *Data; + + int ncol; + + int total; + + int i, j; + + u32b x, y; + + unsigned long clr_pixels[256]; + + + /* Open the BMP file */ + f = fopen(Name, "r"); + + /* No such file */ + if (f == NULL) + { + return (NULL); + } + + /* Read the "BITMAPFILEHEADER" */ + rd_u16b(f, &(fileheader.bfType)); + rd_u32b(f, &(fileheader.bfSize)); + rd_u16b(f, &(fileheader.bfReserved1)); + rd_u16b(f, &(fileheader.bfReserved2)); + rd_u32b(f, &(fileheader.bfOffBits)); + + /* Read the "BITMAPINFOHEADER" */ + rd_u32b(f, &(infoheader.biSize)); + rd_u32b(f, &(infoheader.biWidth)); + rd_u32b(f, &(infoheader.biHeight)); + rd_u16b(f, &(infoheader.biPlanes)); + rd_u16b(f, &(infoheader.biBitCount)); + rd_u32b(f, &(infoheader.biCompresion)); + rd_u32b(f, &(infoheader.biSizeImage)); + rd_u32b(f, &(infoheader.biXPelsPerMeter)); + rd_u32b(f, &(infoheader.biYPelsPerMeter)); + rd_u32b(f, &(infoheader.biClrUsed)); + rd_u32b(f, &(infoheader.biClrImportand)); + + /* Verify the header */ + if (feof(f) || + (fileheader.bfType != 19778) || + (infoheader.biSize != 40)) + { + quit_fmt("Incorrect BMP file format %s", Name); + } + + /* The two headers above occupy 54 bytes total */ + /* The "bfOffBits" field says where the data starts */ + /* The "biClrUsed" field does not seem to be reliable */ + /* Compute number of colors recorded */ + ncol = (fileheader.bfOffBits - 54) / 4; + + for (i = 0; i < ncol; i++) + { + RGBQUAD clrg; + + /* Read an "RGBQUAD" */ + rd_byte(f, &(clrg.b)); + rd_byte(f, &(clrg.g)); + rd_byte(f, &(clrg.r)); + rd_byte(f, &(clrg.filler)); + + /* Analyze the color */ + clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b); + } + + /* Determine total bytes needed for image */ + i = 1; + j = (depth - 1) >> 2; + while (j >>= 1) i <<= 1; + total = infoheader.biWidth * infoheader.biHeight * i; + + /* Allocate image memory */ + C_MAKE(Data, total, char); + + Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/, + Data, infoheader.biWidth, infoheader.biHeight, + 8 /*bitmap_pad*/, 0 /*bytes_per_line*/); + + /* Failure */ + if (Res == NULL) + { + C_KILL(Data, total, char); + fclose(f); + return (NULL); + } + + for (y = 0; y < infoheader.biHeight; y++) + { + int y2 = infoheader.biHeight - y - 1; + + for (x = 0; x < infoheader.biWidth; x++) + { + int ch = getc(f); + + /* Verify not at end of file XXX XXX */ + if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); + + if (infoheader.biBitCount == 24) + { + int c2 = getc(f); + int c3 = getc(f); + + /* Verify not at end of file XXX XXX */ + if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); + + XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3)); + } + else if (infoheader.biBitCount == 8) + { + XPutPixel(Res, x, y2, clr_pixels[ch]); + } + else if (infoheader.biBitCount == 4) + { + XPutPixel(Res, x, y2, clr_pixels[ch / 16]); + x++; + XPutPixel(Res, x, y2, clr_pixels[ch % 16]); + } + else + { + /* Technically 1 bit is legal too */ + quit_fmt("Illegal biBitCount %d in %s", + infoheader.biBitCount, Name); + } + } + } + + fclose(f); + + return Res; +} + + +/* ========================================================*/ +/* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */ +/* ========================================================*/ + +/* + * to save ourselves some labour, define a maximum expected icon width here: + */ +#define MAX_ICON_WIDTH 32 + + +/* some static variables for composing and decomposing pixel values into + * red, green and blue values + */ +static unsigned long redMask, greenMask, blueMask; +static int redShift, greenShift, blueShift; + + +/* + * Use smooth rescaling? + */ +static bool_ smoothRescaling = TRUE; + + +/* + * GetScaledRow reads a scan from the given XImage, scales it smoothly + * and returns the red, green and blue values in arrays. + * The values in this arrays must be divided by a certain value that is + * calculated in ScaleIcon. + * x, y is the position, iw is the input width and ow the output width + * redScan, greenScan and blueScan must be sufficiently sized + */ +static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow, + unsigned long *redScan, unsigned long *greenScan, + unsigned long *blueScan) +{ + int xi, si, sifrac, ci, cifrac, addWhole, addFrac; + unsigned long pix; + int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue; + bool_ getNextPix; + + if (iw == ow) + { + /* unscaled */ + for (xi = 0; xi < ow; xi++) + { + pix = XGetPixel(Im, x + xi, y); + redScan [xi] = (pix >> redShift) & redMask; + greenScan [xi] = (pix >> greenShift) & greenMask; + blueScan [xi] = (pix >> blueShift) & blueMask; + } + } + else if (iw < ow) + { + /* scaling by subsampling (grow) */ + iw--; + ow--; + /* read first pixel: */ + pix = XGetPixel(Im, x, y); + nextRed = (pix >> redShift) & redMask; + nextGreen = (pix >> greenShift) & greenMask; + nextBlue = (pix >> blueShift) & blueMask; + prevRed = nextRed; + prevGreen = nextGreen; + prevBlue = nextBlue; + /* si and sifrac give the subsampling position: */ + si = x; + sifrac = 0; + /* getNextPix tells us, that we need the next pixel */ + getNextPix = TRUE; + + for (xi = 0; xi <= ow; xi++) + { + if (getNextPix) + { + prevRed = nextRed; + prevGreen = nextGreen; + prevBlue = nextBlue; + if (xi < ow) + { + /* only get next pixel if in same icon */ + pix = XGetPixel(Im, si + 1, y); + nextRed = (pix >> redShift) & redMask; + nextGreen = (pix >> greenShift) & greenMask; + nextBlue = (pix >> blueShift) & blueMask; + } + } + + /* calculate subsampled color values: */ + /* division by ow occurs in ScaleIcon */ + redScan [xi] = prevRed * (ow - sifrac) + nextRed * sifrac; + greenScan [xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac; + blueScan [xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac; + + /* advance sampling position: */ + sifrac += iw; + if (sifrac >= ow) + { + si++; + sifrac -= ow; + getNextPix = TRUE; + } + else + { + getNextPix = FALSE; + } + + } + } + else + { + /* scaling by averaging (shrink) */ + /* width of an output pixel in input pixels: */ + addWhole = iw / ow; + addFrac = iw % ow; + /* start position of the first output pixel: */ + si = x; + sifrac = 0; + /* get first input pixel: */ + pix = XGetPixel(Im, x, y); + nextRed = (pix >> redShift) & redMask; + nextGreen = (pix >> greenShift) & greenMask; + nextBlue = (pix >> blueShift) & blueMask; + for (xi = 0; xi < ow; xi++) + { + /* find endpoint of the current output pixel: */ + ci = si + addWhole; + cifrac = sifrac + addFrac; + if (cifrac >= ow) + { + ci++; + cifrac -= ow; + } + /* take fraction of current input pixel (starting segment): */ + redScan[xi] = nextRed * (ow - sifrac); + greenScan[xi] = nextGreen * (ow - sifrac); + blueScan[xi] = nextBlue * (ow - sifrac); + si++; + /* add values for whole pixels: */ + while (si < ci) + { + pix = XGetPixel(Im, si, y); + redScan[xi] += ((pix >> redShift) & redMask) * ow; + greenScan[xi] += ((pix >> greenShift) & greenMask) * ow; + blueScan[xi] += ((pix >> blueShift) & blueMask) * ow; + si++; + } + /* add fraction of current input pixel (ending segment): */ + if (xi < ow - 1) + { + /* only get next pixel if still in icon: */ + pix = XGetPixel(Im, si, y); + nextRed = (pix >> redShift) & redMask; + nextGreen = (pix >> greenShift) & greenMask; + nextBlue = (pix >> blueShift) & blueMask; + } + sifrac = cifrac; + if (sifrac > 0) + { + redScan[xi] += nextRed * sifrac; + greenScan[xi] += nextGreen * sifrac; + blueScan[xi] += nextBlue * sifrac; + } + } + } +} + + +/* + * PutRGBScan takes arrays for red, green and blue and writes pixel values + * according to this values in the XImage-structure. w is the number of + * pixels to write and div is the value by which all red/green/blue values + * are divided first. + */ +static void PutRGBScan(XImage *Im, int x, int y, int w, int div, + unsigned long *redScan, unsigned long *greenScan, + unsigned long *blueScan) +{ + int xi; + unsigned long pix; + unsigned long adj = div / 2; + for (xi = 0; xi < w; xi++) + { + pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) + + ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) + + ((((blueScan[xi] + adj) / div) & blueMask) << blueShift)); + XPutPixel(Im, x + xi, y, pix); + } +} + + +/* + * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut, + * locate (x2, y2). + * Source size is (ix, iy) and destination size is (ox, oy). + * It does this by getting icon scan line from GetScaledScan and handling + * them the same way as pixels are handled in GetScaledScan. + * This even allows icons to be scaled differently in horizontal and + * vertical directions (eg. shrink horizontal, grow vertical). + */ +static void ScaleIcon(XImage *ImIn, XImage *ImOut, + int x1, int y1, int x2, int y2, + int ix, int iy, int ox, int oy) +{ + int div; + int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac; + + /* buffers for pixel rows: */ + unsigned long prevRed [MAX_ICON_WIDTH]; + unsigned long prevGreen [MAX_ICON_WIDTH]; + unsigned long prevBlue [MAX_ICON_WIDTH]; + unsigned long nextRed [MAX_ICON_WIDTH]; + unsigned long nextGreen [MAX_ICON_WIDTH]; + unsigned long nextBlue [MAX_ICON_WIDTH]; + unsigned long tempRed [MAX_ICON_WIDTH]; + unsigned long tempGreen [MAX_ICON_WIDTH]; + unsigned long tempBlue [MAX_ICON_WIDTH]; + + bool_ getNextRow; + + /* get divider value for the horizontal scaling: */ + if (ix == ox) + div = 1; + else if (ix < ox) + div = ox - 1; + else + div = ix; + + if (iy == oy) + { + /* no scaling needed vertically: */ + for (yi = 0; yi < oy; yi++) + { + GetScaledRow(ImIn, x1, y1 + yi, ix, ox, + tempRed, tempGreen, tempBlue); + PutRGBScan(ImOut, x2, y2 + yi, ox, div, + tempRed, tempGreen, tempBlue); + } + } + else if (iy < oy) + { + /* scaling by subsampling (grow): */ + iy--; + oy--; + div *= oy; + /* get first row: */ + GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue); + /* si and sifrac give the subsampling position: */ + si = y1; + sifrac = 0; + /* getNextRow tells us, that we need the next row */ + getNextRow = TRUE; + for (yi = 0; yi <= oy; yi++) + { + if (getNextRow) + { + for (xi = 0; xi < ox; xi++) + { + prevRed[xi] = nextRed[xi]; + prevGreen[xi] = nextGreen[xi]; + prevBlue[xi] = nextBlue[xi]; + } + if (yi < oy) + { + /* only get next row if in same icon */ + GetScaledRow(ImIn, x1, si + 1, ix, ox, + nextRed, nextGreen, nextBlue); + } + } + + /* calculate subsampled color values: */ + /* division by oy occurs in PutRGBScan */ + for (xi = 0; xi < ox; xi++) + { + tempRed[xi] = (prevRed[xi] * (oy - sifrac) + + nextRed[xi] * sifrac); + tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) + + nextGreen[xi] * sifrac); + tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) + + nextBlue[xi] * sifrac); + } + + /* write row to output image: */ + PutRGBScan(ImOut, x2, y2 + yi, ox, div, + tempRed, tempGreen, tempBlue); + + /* advance sampling position: */ + sifrac += iy; + if (sifrac >= oy) + { + si++; + sifrac -= oy; + getNextRow = TRUE; + } + else + { + getNextRow = FALSE; + } + + } + } + else + { + /* scaling by averaging (shrink) */ + div *= iy; + /* height of a output row in input rows: */ + addWhole = iy / oy; + addFrac = iy % oy; + /* start position of the first output row: */ + si = y1; + sifrac = 0; + /* get first input row: */ + GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue); + for (yi = 0; yi < oy; yi++) + { + /* find endpoint of the current output row: */ + ci = si + addWhole; + cifrac = sifrac + addFrac; + if (cifrac >= oy) + { + ci++; + cifrac -= oy; + } + /* take fraction of current input row (starting segment): */ + for (xi = 0; xi < ox; xi++) + { + tempRed[xi] = nextRed[xi] * (oy - sifrac); + tempGreen[xi] = nextGreen[xi] * (oy - sifrac); + tempBlue[xi] = nextBlue[xi] * (oy - sifrac); + } + si++; + /* add values for whole pixels: */ + while (si < ci) + { + GetScaledRow(ImIn, x1, si, ix, ox, + nextRed, nextGreen, nextBlue); + for (xi = 0; xi < ox; xi++) + { + tempRed[xi] += nextRed[xi] * oy; + tempGreen[xi] += nextGreen[xi] * oy; + tempBlue[xi] += nextBlue[xi] * oy; + } + si++; + } + /* add fraction of current input row (ending segment): */ + if (yi < oy - 1) + { + /* only get next row if still in icon: */ + GetScaledRow(ImIn, x1, si, ix, ox, + nextRed, nextGreen, nextBlue); + } + sifrac = cifrac; + for (xi = 0; xi < ox; xi++) + { + tempRed[xi] += nextRed[xi] * sifrac; + tempGreen[xi] += nextGreen[xi] * sifrac; + tempBlue[xi] += nextBlue[xi] * sifrac; + } + /* write row to output image: */ + PutRGBScan(ImOut, x2, y2 + yi, ox, div, + tempRed, tempGreen, tempBlue); + } + } +} + + + +static XImage *ResizeImageSmooth(Display *dpy, XImage *Im, + int ix, int iy, int ox, int oy) +{ + Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); + + int width1, height1, width2, height2; + int x1, x2, y1, y2; + + XImage *Tmp; + + char *Data; + + width1 = Im->width; + height1 = Im->height; + + width2 = ox * width1 / ix; + height2 = oy * height1 / iy; + + Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8); + + Tmp = XCreateImage(dpy, visual, + Im->depth, ZPixmap, 0, Data, width2, height2, + 32, 0); + + /* compute values for decomposing pixel into color values: */ + redMask = Im->red_mask; + redShift = 0; + while ((redMask & 1) == 0) + { + redShift++; + redMask >>= 1; + } + greenMask = Im->green_mask; + greenShift = 0; + while ((greenMask & 1) == 0) + { + greenShift++; + greenMask >>= 1; + } + blueMask = Im->blue_mask; + blueShift = 0; + while ((blueMask & 1) == 0) + { + blueShift++; + blueMask >>= 1; + } + + /* scale each icon: */ + for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy) + { + for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox) + { + ScaleIcon(Im, Tmp, x1, y1, x2, y2, + ix, iy, ox, oy); + } + } + + return Tmp; +} + +/* + * Resize an image. XXX XXX XXX + * + * Also appears in "main-xaw.c". + */ +static XImage *ResizeImage(Display *dpy, XImage *Im, + int ix, int iy, int ox, int oy) +{ + Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); + + int width1, height1, width2, height2; + int x1, x2, y1, y2, Tx, Ty; + int *px1, *px2, *dx1, *dx2; + int *py1, *py2, *dy1, *dy2; + + XImage *Tmp; + + char *Data; + + if (smoothRescaling && (ix != ox || iy != oy) && + visual->class == TrueColor) + { + return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy); + } + + width1 = Im->width; + height1 = Im->height; + + width2 = ox * width1 / ix; + height2 = oy * height1 / iy; + + Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8); + + Tmp = XCreateImage(dpy, visual, + Im->depth, ZPixmap, 0, Data, width2, height2, + 32, 0); + + if (ix > ox) + { + px1 = &x1; + px2 = &x2; + dx1 = &ix; + dx2 = &ox; + } + else + { + px1 = &x2; + px2 = &x1; + dx1 = &ox; + dx2 = &ix; + } + + if (iy > oy) + { + py1 = &y1; + py2 = &y2; + dy1 = &iy; + dy2 = &oy; + } + else + { + py1 = &y2; + py2 = &y1; + dy1 = &oy; + dy2 = &iy; + } + + Ty = *dy1 / 2; + + for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); ) + { + Tx = *dx1 / 2; + + for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); ) + { + XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1)); + + (*px1)++; + + Tx -= *dx2; + if (Tx < 0) + { + Tx += *dx1; + (*px2)++; + } + } + + (*py1)++; + + Ty -= *dy2; + if (Ty < 0) + { + Ty += *dy1; + (*py2)++; + } + } + + return Tmp; +} + +#endif /* USE_GRAPHICS */ + +#endif /* USE_X11 || USE_XAW */ |