#include "imageutils.h"
#include "ifapp.h"
#include <kimageio.h>
#include <klocale.h>
#include <qbitmap.h>
#include <qfile.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <zlib.h>

// ImageMagick redefines off_t to long long
#undef off_t
#define off_t long long
extern "C"{
#include <api.h>
};

// in compressedgif.cpp
unsigned int WriteCompressedGIFImage(const ImageInfo *image_info,
                                     Image *image);

// Returns the filename extension
const char* extension(const char *str)
{
    if(!str)
        return(NULL);
    const char *ptr = str;
    const char *last;
    while(*ptr != '\0')
        ++ptr;
    if(ptr == str)
        return(NULL);
    last = ptr;
    while(*ptr != '.' && ptr != str)
        --ptr;
    if(*ptr == '.' && ptr != last)
        return(++ptr);
    else
        return(NULL);
}

// A little global class. Right now it just contains the recognized image
// filename extension list and a global dict for blended icons
PixieGlobal::PixieGlobal()
{
    int i, n;

    //
    // Do the image extensions. These need to be super fast because the
    // extension is checked when sorting directories if the "Images
    // on top" option is selected, (the default). The mimetype isn't checked
    // until later - checking them all when entering the folder is way too
    // slow. KDE doesn't check them all right away, either, but KDE also
    // doesn't sort by file type ;-)
    //
    // Because speed is important we don't use a normal array or QStrList.
    // Instead we create a array of 256 pointers, each of which can hold
    // up to 10 strings. Then we index by the first character of the
    // extension so "bmp" would only have to check against extensions that
    // begin with "b" - not all of them. A little more processing at
    // startup, a lot less when sorting.
    //
    for(i=0; i < 256; ++i){
        for(n=0; n < 10; ++n){
            extensions[i][n] = NULL;
        }
    }
    // Add extensions for KDE/Qt image formats. These are simply lowercase
    // format names.
    QStrList inputList = QImageIO::inputFormats();
    QStrList skipList;
    const char *str;
    char buffer[32];
    for(str = inputList.first(); str != NULL; str = inputList.next()){
        for(i=0; str[i] != '\0'; ++i)
            buffer[i] = tolower(str[i]);
        buffer[i] = '\0';
        insertExtension(buffer);
        skipList.append(buffer);
    }
    // Manually install jpg and tif extensions since Qt uses jpeg and tiff
    insertExtension("jpg");
    insertExtension("tif");
    // Other extensions we can do
    insertExtension("mif");
    insertExtension("miff");
    insertExtension("xcf");
    insertExtension("pcx");
    insertExtension("tga");
    insertExtension("pnm");
    insertExtension("ppm");
    insertExtension("rs");
    insertExtension("sgi");
    insertExtension("sunras");
    insertExtension("xwd");
    insertExtension("wmf");
    insertExtension("svg");
}

// You shouldn't ever call these PixieGlobal methods
void PixieGlobal::insertExtension(const char *str)
{
    int i, idx=str[0];
    for(i=0; i < 10; ++i){
        if(!extensions[idx][i]){
            extensions[idx][i] = (char *)malloc(strlen(str)+1);
            qstrcpy(extensions[idx][i], str);
            return;
        }
    }
    qWarning("Not enough space for %s!", str);
    return;
}

PixieGlobal::~PixieGlobal()
{
    int i, n;
    for(i=0; i < 256; ++i){
        for(n=0; n < 10; ++n){
            if(extensions[i][n])
                free(extensions[i][n]);
        }
    }
}

// Don't use these
bool PixieGlobal::isImageExtension(const char *str)
{
    if(!str)
        return(false);
    if(str[0] == '.')
        ++str;
    int i, idx = str[0];
    for(i=0; i < 10; ++i){
        if(!extensions[idx][i])
            return(false);
        else if(strcasecmp(extensions[idx][i], str) == 0)
            return(true);
    }
    return(false);
}

bool PixieGlobal::isImageType(const QString &filename)
{
    return(isImageExtension(extension(filename.lower().ascii())));
}

// Use these ;-)

// This checks the filename extension against the global list. Use
// PixieBrowser::isImage() for mimetype support
bool isImageType(const QString &fn)
{
    return(kifapp()->globals()->isImageType(fn));
}

bool isVideoType(const QString &fn)
{
    const char *ext = extension(QFile::encodeName(fn));
    if(!ext)
        return(false);
    if(qstricmp(ext, "avi") == 0 || qstricmp(ext, "mpg")  == 0 ||
       qstricmp(ext, "mpeg") == 0 || qstricmp(ext, "wmv") == 0 ||
       qstricmp(ext, "asf") == 0 || qstricmp(ext, "viv") == 0 ||
       qstricmp(ext, "asx") == 0 || qstricmp(ext, "rm") == 0 ||
       qstricmp(ext, "mov") == 0 || qstricmp(ext, "asx") == 0)
        return(true);
    return(false);
}

const char* extensionForFormat(const char *format)
{
    if(!format)
        return(NULL);

    if(qstrcmp(format, "APP1JPEG") == 0 || qstrcmp(format, "JPEG") == 0 ||
       qstrcmp(format, "JPG") == 0)
        return(".jpg");

    if(qstrcmp(format, "ICO") == 0 || qstrcmp(format, "ICON") == 0)
        return(".ico");

    if(qstrcmp(format, "ICB") == 0 || qstrcmp(format, "TGA") == 0 ||
       qstrcmp(format, "VDA") == 0 || qstrcmp(format, "VST") == 0)
        return(".tga");

    if(qstrcmp(format, "GIF") == 0 || qstrcmp(format, "GIF87") == 0)
        return(".gif");

    if(qstrcmp(format, "TIFF") == 0 || qstrcmp(format, "TIF") == 0)
        return(".tiff");

    // Most extensions are just lowercase versions of the format name. I don't
    // want to just use that for everything, tho, since there are a lot of
    // formats I don't know the extension for. Returning NULL will prompt the
    // user to make sure.
    if(qstrcmp(format, "PNG") == 0)
        return(".png");

    if(qstrcmp(format, "MNG") == 0)
        return(".mng");

    if(qstrcmp(format, "BMP") == 0)
        return(".bmp");

    if(qstrcmp(format, "MIFF") == 0)
        return(".miff");

    if(qstrcmp(format, "XCF") == 0)
        return(".xcf");

    if(qstrcmp(format, "XBM") == 0)
        return(".xbm");

    if(qstrcmp(format, "XPM") == 0)
        return(".xpm");

    if(qstrcmp(format, "XWD") == 0)
        return(".xwd");

    if(qstrcmp(format, "WMF") == 0)
        return(".wmf");

    if(qstrcmp(format, "SVG") == 0)
        return(".svg");

    if(qstrcmp(format, "PCX") == 0)
        return(".pcx");

    if(qstrcmp(format, "PNM") == 0)
        return(".pnm");

    if(qstrcmp(format, "PPM") == 0)
        return(".ppm");

    return(NULL);
}

bool displayFormat(const char *format)
{
    // These are the only formats we display by default in dialogs. The
    // rest can be shown by clicking a button
    if(qstrcmp(format, "BMP") == 0 || qstrcmp(format, "CYMK") == 0 ||
       qstrcmp(format, "CYMKA") == 0 || qstrcmp(format, "CUT") == 0 ||
       qstrcmp(format, "DCM") == 0 || qstrcmp(format, "DIB") == 0 ||
       qstrcmp(format, "FITS") == 0 || qstrcmp(format, "FPX") == 0 ||
       qstrcmp(format, "GIF") == 0 || qstrcmp(format, "ICO") == 0 ||
       qstrcmp(format, "JPEG") == 0 || qstrcmp(format, "MIFF") == 0 ||
       qstrcmp(format, "MNG") == 0 || qstrcmp(format, "MAT") == 0 ||
       qstrcmp(format, "PALM") == 0 || qstrcmp(format, "PCD") == 0 ||
       qstrcmp(format, "PCT") == 0 || qstrcmp(format, "PCX") == 0 ||
       qstrcmp(format, "PDB") == 0 || qstrcmp(format, "PM") == 0 ||
       qstrcmp(format, "PNG") == 0 || qstrcmp(format, "PNM") == 0 ||
       qstrcmp(format, "PPM") == 0 || qstrcmp(format, "RAS") == 0 ||
       qstrcmp(format, "RGB") == 0 || qstrcmp(format, "RGBA") == 0 ||
       qstrcmp(format, "SUN") == 0 || qstrcmp(format, "SVG") == 0 ||
       qstrcmp(format, "TGA") == 0 || qstrcmp(format, "TIFF") == 0 ||
       qstrcmp(format, "WPG") == 0 || qstrcmp(format, "X") == 0 ||
       qstrcmp(format, "XBM") == 0 || qstrcmp(format, "XCF") == 0 ||
       qstrcmp(format, "XPM") == 0 || qstrcmp(format, "XWD") == 0 ||
       qstrcmp(format, "XBM") == 0 || qstrcmp(format, "XCF") == 0)
        return(true);
    return(false);
}

void outputFormats()
{
    // This is a debugging method
    ExceptionInfo exception;
    GetExceptionInfo(&exception);
    const MagickInfo *i = GetMagickInfo(NULL, &exception);
    qWarning("Can save image formats: ");
    while(i){
        if(i->encoder)
            qWarning("%s, %s", i->name, i->description);
        i = i->next;
    }
    i = GetMagickInfo(NULL, &exception);
    qWarning("\nCan read image formats: ");
    while(i){
        if(i->decoder)
            qWarning("%s, %s", i->name, i->description);
        i = i->next;
    }
    qWarning("\n");
    DestroyExceptionInfo(&exception);
}

const char* formatForFilename(const QString &filename)
{
    const char *ext = extension(QFile::encodeName(filename));
    if(!ext)
        return(NULL);
    if(qstrcmp(ext, "jpg") == 0 || qstrcmp(ext, "jpeg") == 0)
        return("JPEG");
    if(qstrcmp(ext, "tif") == 0 || qstrcmp(ext, "tiff") == 0)
        return("TIFF");
    if(qstrcmp(ext, "mif") == 0 || qstrcmp(ext, "miff") == 0)
        return("MIFF");
    if(qstrcmp(ext, "gif") == 0)
        return("GIF");
    if(qstrcmp(ext, "tga") == 0)
        return("TGA");
    if(qstrcmp(ext, "ico") == 0)
        return("ICO");
    if(qstrcmp(ext, "tga") == 0)
        return("TGA");
    if(qstrcmp(ext, "png") == 0)
        return("PNG");
    if(qstrcmp(ext, "mng") == 0)
        return("MNG");
    if(qstrcmp(ext, "bmp") == 0)
        return("BMP");
    if(qstrcmp(ext, "xcf") == 0)
        return("XCF");
    if(qstrcmp(ext, "xbm") == 0)
        return("XBM");
    if(qstrcmp(ext, "xpm") == 0)
        return("XPM");
    if(qstrcmp(ext, "xwd") == 0)
        return("XWD");
    if(qstrcmp(ext, "wmf") == 0)
        return("WMF");
    if(qstrcmp(ext, "svg") == 0)
        return("SVG");
    if(qstrcmp(ext, "pcx") == 0)
        return("PCX");
    if(qstrcmp(ext, "pnm") == 0)
        return("PNM");
    if(qstrcmp(ext, "PPM") == 0)
        return("ppm");
    return(NULL);
}

bool formatUsesComment(const char *format)
{
    if(qstrcmp(format, "PNG") == 0 || qstrcmp(format, "GIF") == 0 ||
       qstrcmp(format, "JPEG") == 0 || qstrcmp(format, "JPG") == 0 ||
       qstrcmp(format, "FPX") == 0 || qstrcmp(format, "MIFF") == 0 ||
       qstrcmp(format, "PDB") == 0 || qstrcmp(format, "PNM") ||
       qstrcmp(format, "TGA") == 0){
        return(true);
    }
    return(false);
}

bool fileUsesComment(const QString &filename)
{
    return(formatUsesComment(formatForFilename(filename)));
}

bool fileListUsesComment(const QStringList &fileList)
{
    QStringList::ConstIterator it;
    for(it = fileList.begin(); it != fileList.end(); ++it){
        if(fileUsesComment(*it))
            return(true);
    }
    return(false);
}

// Loads an image using Qt if possible, otherwise ImageMagick. If
// formatRet is != NULL it will try to copy the format used to load. Useful
// for saveImage().
bool loadImage(QImage &img, const QString &filename,
               const char *format, char *formatRet)
{
    bool tryQt = false;
    bool result;
    const char *str;
    // try using Qt first if there is no format given or a format name
    // that it recognizes
    if(format){
        QStrList inputList = QImageIO::inputFormats();
        for(str = inputList.first(); str != NULL; str = inputList.next()){
            if(strcasecmp(str, format) == 0){
                tryQt = true;
                break;
            }
        }
    }
    else
        tryQt = true;
    if(tryQt && img.load(filename, format)){
        if(format && formatRet)
            qstrcpy(formatRet, format);
        else if(formatRet){
            // ugh, really sucks that Qt doesn't let you query the format used
            // to load :P
            str = QImageIO::imageFormat(filename);
            if(str)
                qstrcpy(formatRet, str);
            else
                *formatRet ='\0';
        }
        return(true);
    }
    // Okay, no Qt support - use ImageMagick!
    ExceptionInfo exception;
    ImageInfo *info = CloneImageInfo(NULL);
    Image *image;

    GetExceptionInfo(&exception);
    qstrcpy(info->filename, QFile::encodeName(filename));
    image = ReadImage(info, &exception);
    if(!image){
        if(formatRet)
            *formatRet = '\0';
        DestroyImageInfo(info);
        DestroyExceptionInfo(&exception);
        return(false);
    }
    img.reset();
    img.create(image->columns, image->rows, 32);
    result = DispatchImage(image, 0, 0, img.width(), img.height(),
                           "BGRA", CharPixel, img.bits(), &exception);
    if(!result){
        if(formatRet)
            *formatRet = '\0';
        img.reset();
    }
    else{
        if(format && formatRet)
            qstrcpy(formatRet, format);
        else if(formatRet)
            qstrcpy(formatRet, image->magick);
    }
    if(image->next)
        DestroyImageList(image);
    else
        DestroyImage(image);
    DestroyImageInfo(info);
    DestroyExceptionInfo(&exception);
    return(result);
}

/* Temporary fix  - ImageMagick's Constitute is just giving blank images! */
Image* myConstituteImage(QImage &srcImage)
{
    int x, y;
    int w = srcImage.width();
    int h = srcImage.height();
    PixelPacket *destScanline;
    unsigned int pixel;

    ImageInfo *info = CloneImageInfo(NULL);
    QString sizeStr;
    sizeStr = sizeStr.sprintf("%dx%d", w, h);
    info->size = (char *)malloc(sizeStr.length()+1);
    strcpy(info->size, sizeStr.latin1());

    Image *destImage = AllocateImage(info);
    if(!destImage){
        DestroyImageInfo(info);
        return(NULL);
    }

    if(srcImage.depth() > 8){
        unsigned int *srcScanline;
        for(y=0; y < h; ++y){
            srcScanline = (unsigned int *)srcImage.scanLine(y);
            destScanline = GetImagePixels(destImage, 0, y, destImage->columns, 1);
            for(x=0; x < w; ++x){
                pixel = srcScanline[x];
                destScanline[x].red = ScaleCharToQuantum(qRed(pixel));
                destScanline[x].green =  ScaleCharToQuantum(qGreen(pixel));
                destScanline[x].blue =  ScaleCharToQuantum(qBlue(pixel));
                destScanline[x].opacity =  ScaleCharToQuantum(255-qAlpha(pixel));
            }
        }
    }
    else{
        unsigned char *srcScanline;
        unsigned char idx;
        unsigned int *colorTable = srcImage.colorTable();
        for(y=0; y < h; ++y){
            srcScanline = (unsigned char *)srcImage.scanLine(y);
            destScanline = GetImagePixels(destImage, 0, y, destImage->columns, 1);
            for(x=0; x < w; ++x){
                idx = srcScanline[x];
                pixel = colorTable[idx];
                destScanline[x].red = ScaleCharToQuantum(qRed(pixel));
                destScanline[x].green =  ScaleCharToQuantum(qGreen(pixel));
                destScanline[x].blue =  ScaleCharToQuantum(qBlue(pixel));
                destScanline[x].opacity =  ScaleCharToQuantum(255-qAlpha(pixel));
            }
        }
    }
    DestroyImageInfo(info);
    return(destImage);
}

// Saves an image using Qt if it recognizes the format, ImageMagick otherwise.
bool saveImage(QImage &img, const QString &fileName, const char *format,
               int quality, const QString &comment)
{
    bool tryQt = false;
    bool result;
    bool isGIF = format ? qstricmp(format, "GIF") == 0 : false;
    const char *str;
    // see if it's a format Qt recognizes
    if(format){
        if(isGIF ||(comment != QString::null && qstricmp(format, "PNG") != 0))
            tryQt = false; // use Pixie's ImageMagick writer
        else{
            QStrList outputList = QImageIO::outputFormats();
            tryQt = false;
            for(str=outputList.first(); str != NULL; str=outputList.next()){
                if(strcasecmp(str, format) == 0){
                    tryQt = true;
                    break;
                }
            }
        }
    }
    else if(comment == QString::null || qstricmp(format, "PNG") == 0)
        tryQt = true;

    if(tryQt){
        if(!comment.isEmpty())
            img.setText("Description", 0, comment.latin1());
        if(img.save(fileName, format, quality))
            return(true);
    }

    // Okay, not handled by Qt, let's do ImageMagick
    ExceptionInfo exception;
    ImageInfo *info;

    GetExceptionInfo(&exception);
    Image *image;
    if(img.depth() < 8)
        img = img.convertDepth(8);

    // This call is busted
    /*image = ConstituteImage(img.width(), img.height(), "BGRA",
     CharPixel, img.bits(), &exception); */
    image = myConstituteImage(img);

    if(!image){
        DestroyExceptionInfo(&exception);
        return(false);
    }
    info = CloneImageInfo(NULL);
    if(comment != QString::null)
        SetImageAttribute(image, "comment", comment.latin1());

    // don't think I need to set both image and info, but better make sure :)
    qstrcpy(image->filename, QFile::encodeName(fileName));
    qstrcpy(info->filename, image->filename);
    qstrcpy(info->magick, format);
    qstrcpy(image->magick, info->magick);

    if(quality != -1)
        info->quality = quality;

    // Just setting the magick format item doesn't seem to work - it is
    // still determined by extension. Iterate through known formats and use
    // the correct encoder
    result = false;
    if(isGIF){
        qWarning("Using compressed GIF writer");
        result = WriteCompressedGIFImage(info, image);
    }
    else{
        const MagickInfo *i = GetMagickInfo(NULL, &exception);
        while(i){
            if(i->encoder && qstricmp(i->name, format) == 0){
                result = i->encoder(info, image);
                break;
            }
            i = i->next;
        }
    }
    result = WriteImage(info, image);
    if(image->next)
        DestroyImageList(image);
    else
        DestroyImage(image);
    DestroyImageInfo(info);
    DestroyExceptionInfo(&exception);
    return(result);
}

// these require 32bpp images and don't check values!
void copyQImage(QImage &src, QImage &dest, int x, int y)
{
    int srcx, destx, srcy, desty;
    unsigned int *input, *output;
    for(srcy=0, desty = y; srcy < src.height(); ++srcy, ++desty){
            input = (unsigned int *)src.scanLine(srcy);
            output = (unsigned int *)dest.scanLine(desty);
            for(srcx=0, destx = x; srcx < src.width(); ++srcx, ++destx)
                output[destx] = input[srcx];
    }
}

void copyQImageWithAlpha(QImage &src, QImage &dest, int x, int y)
{
    int srcx, destx, srcy, desty;
    unsigned int *input, *output;
    int alpha, r, g, b;
    for(srcy=0, desty = y; srcy < src.height(); ++srcy, ++desty){
            input = (unsigned int *)src.scanLine(srcy);
            output = (unsigned int *)dest.scanLine(desty);
            for(srcx=0, destx = x; srcx < src.width(); ++srcx, ++destx){
                alpha = qAlpha(input[srcx]);
                if(alpha == 0){
                    ;
                }
                else if(alpha != 255){
                    float srcPercent = ((float)alpha)/255.0;
                    float destPercent = 1.0-srcPercent;
                    r = (int)((srcPercent*qRed(input[srcx])) + (destPercent*qRed(output[destx])));
                    g = (int)((srcPercent*qGreen(input[srcx])) + (destPercent*qGreen(output[destx])));
                    b = (int)((srcPercent*qBlue(input[srcx])) + (destPercent*qBlue(output[destx])));
                    output[destx] = qRgba(r, g, b, 255);
                }
                else
                    output[destx] = input[srcx];
            }
    }
}


void tileQImage(QImage &tile, QImage &dest)
{
    int sx, sy, dx, dy;
    unsigned int *srcData, *destData;

    for(sy=0, dy=0; dy < dest.height(); ++sy, ++dy){
        if(sy >= tile.height())
            sy = 0;
        srcData = (unsigned int *)tile.scanLine(sy);
        destData = (unsigned int *)dest.scanLine(dy);
        for(sx=0, dx=0; dx < dest.width(); ++sx, ++dx){
            if(sx >= tile.width())
                sx = 0;
            destData[dx] = srcData[sx];
        }
    }
}

void tileQImage(QImage &dest, int dx, int dy, int dw, int dh, QImage &src,
                int sx, int sy, int sw, int sh)
{
    unsigned int *srcData, *destData;
    int orig_sx = sx;
    int orig_sy = sy;
    int orig_dx = dx;
    //int orig_dy = dy;
    int sx2 = sx+sw-1;
    int sy2 = sy+sh-1;
    int dx2 = dx+dw-1;
    int dy2 = dy+dh-1;

    for(;dy < dy2; ++sy, ++dy){
        if(sy > sy2)
            sy = orig_sy;
        srcData = (unsigned int *)src.scanLine(sy);
        destData = (unsigned int *)dest.scanLine(dy);
        for(sx=orig_sx, dx=orig_dx; dx < dx2; ++sx, ++dx){
            if(sx > sx2)
                sx = orig_sx;
            destData[dx] = srcData[sx];
        }
    }
}

void copyQImageSecondaryAlpha(QImage &dest, int dx, int dy, int dw, int dh,
                              QImage &src, int sx, int sy, int sw, int sh)
{
    unsigned int *srcData, *destData;
    int orig_sx = sx;
    int orig_sy = sy;
    int orig_dx = dx;
    //int orig_dy = dy;
    int sx2 = sx+sw-1;
    int sy2 = sy+sh-1;
    int dx2 = dx+dw-1;
    int dy2 = dy+dh-1;

    int r, g, b, alpha;

    for(;dy < dy2; ++sy, ++dy){
        if(sy > sy2)
            sy = orig_sy;
        srcData = (unsigned int *)src.scanLine(sy);
        destData = (unsigned int *)dest.scanLine(dy);
        for(sx=orig_sx, dx=orig_dx; dx < dx2; ++sx, ++dx){
            if(sx > sx2)
                sx = orig_sx;
            r = qRed(destData[dx]);
            g = qGreen(destData[dx]);
            b = qBlue(destData[dx]);
            alpha = qAlpha(srcData[sx]);
            destData[dx] = qRgba(r, g, b, alpha);
        }
    }
}


// MITSHM stuff
bool useMITSHM = true;
Pixmap tempPix = 0;
GC tempGC = 0;
XImage *shmimage = 0;
XShmSegmentInfo xshared_segment_info;

int highest_bit(unsigned int v)
{
    int i;
    unsigned int b = (unsigned int)1 << 31;
    for(i=31; ((b & v) == 0) && i>=0;  i--)
        b >>= 1;
    return i;
}

void clearData()
{
    Display *dpy = QPaintDevice::x11AppDisplay();
    if(tempPix){
        XFreePixmap(dpy, tempPix);
        tempPix = 0;
    }
    if(tempGC){
        XFreeGC(dpy, tempGC);
        tempGC = 0;
    }
    if(useMITSHM && shmimage){
        XShmDetach(dpy, &xshared_segment_info);
        shmimage->data = NULL;
        XDestroyImage(shmimage);
        shmimage = NULL;
        shmdt(xshared_segment_info.shmaddr);
        shmctl(xshared_segment_info.shmid, IPC_RMID, 0);
    }
}

void allocateXImage(int w, int h)
{
    if(!useMITSHM)
        return;
    if(shmimage && shmimage->width >= w && shmimage->height >= h)
        return;

    Display *dpy = QPaintDevice::x11AppDisplay();
    Visual *vis = (Visual *)QPaintDevice::x11AppVisual();
    int depth = QPaintDevice::x11AppDepth();

    clearData();
    shmimage = XShmCreateImage(dpy, vis, depth, ZPixmap, 0,
                             &xshared_segment_info, w, h);
    if(shmimage){
        xshared_segment_info.shmid = shmget(IPC_PRIVATE,
                                            shmimage->bytes_per_line *
                                            shmimage->height,
                                            IPC_CREAT | 0777 );
        bool okay = xshared_segment_info.shmid != -1;
        if(okay){
            shmimage->data = (char*)shmat(xshared_segment_info.shmid, 0, 0 );
            xshared_segment_info.shmaddr = shmimage->data;
            okay = xshared_segment_info.shmaddr != 0;
        }
        xshared_segment_info.readOnly = false;
        if(okay)
            okay = XShmAttach(dpy, &xshared_segment_info);
        if(!okay){
            shmimage->data = NULL;
            XDestroyImage(shmimage);
            shmimage = NULL;
            if(xshared_segment_info.shmaddr)
                shmdt(xshared_segment_info.shmaddr);
            if(xshared_segment_info.shmid != -1)
                shmctl(xshared_segment_info.shmid, IPC_RMID, 0);
        }
        else{
            tempPix = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
                                       shmimage->data,
                                       &xshared_segment_info, w, h,
                                       depth);
        }
    }
    if(!shmimage)
        useMITSHM = false;
}

void convertImageToPixmapPaletteBlend(QImage &image, QImage &tile,
                                      int start_x, int start_y, QPixmap &pix)
{
    XImage *ximage;
    unsigned int *tileData;
    unsigned char *srcData;
    unsigned char alpha;
    char *destData;
    unsigned int pixel;
    unsigned int *colorTable;

    unsigned int r, g, b;
    int x, y, tx, ty;
    float srcPercent, destPercent;
    bool hasAlphaBuffer = image.hasAlphaBuffer();

    Display *dpy = QPaintDevice::x11AppDisplay();
    Visual *visual = (Visual *)QPaintDevice::x11AppVisual();
    int depth = QPaintDevice::x11AppDepth();

    allocateXImage(image.width(), image.height());
    if(shmimage){
        ximage = shmimage;
    }
    else{
        ximage = XCreateImage(dpy, visual, depth, ZPixmap,
                              0, 0, image.width(), image.height(), 32, 0);
        if(!ximage){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage!");
            return;
        }

        ximage->data = (char *)malloc(ximage->bytes_per_line*image.height());
        if(!ximage->data){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage data!");
            ximage->data = NULL;
            XDestroyImage(ximage);
            ximage = NULL;
            return;
        }
    }

    unsigned int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;

    red_mask = (unsigned int)visual->red_mask;
    green_mask = (unsigned int)visual->green_mask;
    blue_mask = (unsigned int)visual->blue_mask;
    red_shift = highest_bit(red_mask) - 7;
    green_shift = highest_bit(green_mask) - 7;
    blue_shift = highest_bit(blue_mask) - 7;

    int bppc = ximage->bits_per_pixel;
    if(bppc > 8 && ximage->byte_order == LSBFirst)
        bppc++;

    colorTable = (unsigned int *)image.colorTable();
    // There is a lot of code duplication here but we want to keep the
    // check for bpp out of the main loop or else we would be running a
    // case for every pixel, (which is what Qt does)
    if(bppc == 16){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = (pixel >> 8);
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 17){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
            }
        }
    }
    else if(bppc == 24){
         for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 25){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;

            }
        }
    }
    else if(bppc == 32){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 24;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 33){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = *(colorTable+srcData[x]);
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 24;

            }
        }
    }
    if(pix.mask()){
        // don't reset mask with a null bitmap because Qt makes annoying
        // messages if you resize
        pix = QPixmap(image.width(), image.height());
    }
    else if(pix.width() != image.width() || pix.height() != image.height())
        pix.resize(image.width(), image.height());

    if(!tempGC)
        tempGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);

    if(shmimage){
        XCopyArea(dpy, tempPix, pix.handle(), tempGC,
                  0, 0, image.width(), image.height(), 0, 0);
        QApplication::syncX();
    }
    else{
        XPutImage(dpy, pix.handle(), tempGC, ximage, 0, 0, 0, 0,
                  image.width(), image.height());
        free(ximage->data);
        ximage->data = NULL;
        XDestroyImage(ximage);
    }

}

void convertImageToPixmapBlend(QImage &image, QImage &tile,
                               int start_x, int start_y, QPixmap &pix)
{
    if(image.isNull())
        return;

    if(image.depth() < 32){
        convertImageToPixmapPaletteBlend(image, tile, start_x, start_y, pix);
        return;
    }

    XImage *ximage;
    unsigned int *srcData, *tileData;
    char *destData;
    unsigned char alpha;
    unsigned int pixel;

    unsigned int r, g, b;
    int x, y, tx, ty;
    float srcPercent, destPercent;
    bool hasAlphaBuffer = image.hasAlphaBuffer();

    Display *dpy = QPaintDevice::x11AppDisplay();
    Visual *visual = (Visual *)QPaintDevice::x11AppVisual();
    int depth = QPaintDevice::x11AppDepth();

    allocateXImage(image.width(), image.height());
    if(shmimage){
        ximage = shmimage;
    }
    else{
        ximage = XCreateImage(dpy, visual, depth, ZPixmap,
                              0, 0, image.width(), image.height(), 32, 0);
        if(!ximage){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage!");
            return;
        }

        ximage->data = (char *)malloc(ximage->bytes_per_line*image.height());
        if(!ximage->data){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage data!");
            ximage->data = NULL;
            XDestroyImage(ximage);
            ximage = NULL;
            return;
        }
    }

    unsigned int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;

    red_mask = (unsigned int)visual->red_mask;
    green_mask = (unsigned int)visual->green_mask;
    blue_mask = (unsigned int)visual->blue_mask;
    red_shift = highest_bit(red_mask) - 7;
    green_shift = highest_bit(green_mask) - 7;
    blue_shift = highest_bit(blue_mask) - 7;

    int bppc = ximage->bits_per_pixel;
    if(bppc > 8 && ximage->byte_order == LSBFirst)
        bppc++;

    // There is a lot of code duplication here but we want to keep the
    // check for bpp out of the main loop or else we would be running a
    // case for every pixel, (which is what Qt does)
    if(bppc == 16){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = (pixel >> 8);
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 17){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
            }
        }
    }
    else if(bppc == 24){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 25){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;

            }
        }
    }
    else if(bppc == 32){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 24;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 33){
        for(y=0, ty=start_y; y < image.height(); ++y, ++ty){
            srcData = (unsigned int *)image.scanLine(y);
            tileData = (unsigned int *)tile.scanLine(ty);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0, tx=start_x; x < image.width(); ++x, ++tx){
                pixel = srcData[x];
                alpha = hasAlphaBuffer ? qAlpha(pixel) : 255;
                if(alpha == 0)
                    pixel = tileData[tx];
                else if(alpha != 255){
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)((srcPercent*qRed(pixel))+
                                             (destPercent*qRed(tileData[tx]))),
                             (unsigned char)((srcPercent*qGreen(pixel))+
                                             (destPercent*qGreen(tileData[tx]))),
                             (unsigned char)((srcPercent*qBlue(pixel))+
                                             (destPercent*qBlue(tileData[tx]))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 24;

            }
        }
    }
    if(pix.mask()){
        // don't reset mask with a null bitmap because Qt makes annoying
        // messages if you resize
        pix = QPixmap(image.width(), image.height());
    }
    else if(pix.width() != image.width() || pix.height() != image.height())
        pix.resize(image.width(), image.height());

    if(!tempGC)
        tempGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);

    if(shmimage){
        XCopyArea(dpy, tempPix, pix.handle(), tempGC,
                  0, 0, image.width(), image.height(), 0, 0);
        QApplication::syncX();
    }
    else{
        XPutImage(dpy, pix.handle(), tempGC, ximage, 0, 0, 0, 0,
                  image.width(), image.height());
        free(ximage->data);
        ximage->data = NULL;
        XDestroyImage(ximage);
    }
}

void convertImageToPixmapPalette(QImage &image, QPixmap &pix,
                                 unsigned int bgcolor)
{
    XImage *ximage;
    unsigned char *srcData;
    unsigned char alpha;
    char *destData;
    unsigned int pixel=0;
    unsigned int *colorTable;
    float srcPercent, destPercent;

    unsigned int r, g, b;
    int x, y;

    Display *dpy = QPaintDevice::x11AppDisplay();
    Visual *visual = (Visual *)QPaintDevice::x11AppVisual();
    int depth = QPaintDevice::x11AppDepth();

    allocateXImage(image.width(), image.height());
    if(shmimage){
        ximage = shmimage;
    }
    else{
        ximage = XCreateImage(dpy, visual, depth, ZPixmap,
                              0, 0, image.width(), image.height(), 32, 0);
        if(!ximage){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage!");
            return;
        }

        ximage->data = (char *)malloc(ximage->bytes_per_line*image.height());
        if(!ximage->data){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage data!");
            ximage->data = NULL;
            XDestroyImage(ximage);
            ximage = NULL;
            return;
        }
    }

    unsigned int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;

    red_mask = (unsigned int)visual->red_mask;
    green_mask = (unsigned int)visual->green_mask;
    blue_mask = (unsigned int)visual->blue_mask;
    red_shift = highest_bit(red_mask) - 7;
    green_shift = highest_bit(green_mask) - 7;
    blue_shift = highest_bit(blue_mask) - 7;

    int bppc = ximage->bits_per_pixel;
    if(bppc > 8 && ximage->byte_order == LSBFirst)
        bppc++;

    colorTable = (unsigned int *)image.colorTable();

    if(image.hasAlphaBuffer()){
        // With paletted images if your alphablending to a solid background
        // color it's better to just blend the palette.
        r = qRed(bgcolor);
        g = qGreen(bgcolor);
        b = qBlue(bgcolor);
        unsigned int *convertedTable = (unsigned int *)
            malloc(sizeof(unsigned int)*image.numColors());
        for(x=0; x < image.numColors(); ++x){
            alpha = qAlpha(colorTable[x]);
            if(alpha == 0)
                pixel = bgcolor;
            else if(alpha != 255){
                srcPercent = ((float)alpha)/255.0;
                destPercent = 1.0-srcPercent;
                pixel =
                    qRgba((unsigned char)((srcPercent*qRed(pixel))+
                                          (destPercent*r)),
                          (unsigned char)((srcPercent*qGreen(pixel))+
                                          (destPercent*g)),
                          (unsigned char)((srcPercent*qBlue(pixel))+
                                          (destPercent*b)), 255);
            }
            convertedTable[x] = pixel;
        }
        colorTable = convertedTable;
    }

    // There is a lot of code duplication here but we want to keep the
    // check for bpp out of the main loop or else we would be running a
    // case for every pixel, (which is what Qt does)
    if(bppc == 16){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = (pixel >> 8);
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 17){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
            }
        }
    }
    else if(bppc == 24){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 25){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;

            }
        }
    }
    else if(bppc == 32){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 24;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 33){
        for(y=0; y < image.height(); ++y){
            srcData = image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                pixel = *(colorTable+srcData[x]);
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 24;

            }
        }
    }
    if(pix.mask()){
        // don't reset mask with a null bitmap because Qt makes annoying
        // messages if you resize
        pix = QPixmap(image.width(), image.height());
    }
    else if(pix.width() != image.width() || pix.height() != image.height())
        pix.resize(image.width(), image.height());

    if(!tempGC)
        tempGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);

    if(shmimage){
        XCopyArea(dpy, tempPix, pix.handle(), tempGC,
                  0, 0, image.width(), image.height(), 0, 0);
        QApplication::syncX();
    }
    else{
        XPutImage(dpy, pix.handle(), tempGC, ximage, 0, 0, 0, 0,
                  image.width(), image.height());
        free(ximage->data);
        ximage->data = NULL;
        XDestroyImage(ximage);
    }

    if(image.hasAlphaBuffer())
        free(colorTable);
}

// Converts a image to a pixmap using the global MITSHM buffer.
void convertImageToPixmap(QImage &image, QPixmap &pix, unsigned int bgcolor)
{

    if(image.isNull())
        return;
    // everything should be 32bpp anyways so we don't bother w/ other depths
    if(image.depth() < 32){
        convertImageToPixmapPalette(image, pix, bgcolor);
        return;
    }

    XImage *ximage;
    unsigned int *srcData;
    char *destData;
    unsigned char alpha;
    float srcPercent, destPercent;
    unsigned int pixel;

    unsigned int r, g, b;
    int x, y;
    bool hasAlphaBuffer = image.hasAlphaBuffer();

    Display *dpy = QPaintDevice::x11AppDisplay();
    Visual *visual = (Visual *)QPaintDevice::x11AppVisual();
    int depth = QPaintDevice::x11AppDepth();

    allocateXImage(image.width(), image.height());
    if(shmimage){
        ximage = shmimage;
    }
    else{
        ximage = XCreateImage(dpy, visual, depth, ZPixmap,
                              0, 0, image.width(), image.height(), 32, 0);
        if(!ximage){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage!");
            return;
        }

        ximage->data = (char *)malloc(ximage->bytes_per_line*image.height());
        if(!ximage->data){
            qWarning("convertImageToPixmap: Unable to allocate memory for XImage data!");
            ximage->data = NULL;
            XDestroyImage(ximage);
            ximage = NULL;
            return;
        }
    }

    unsigned int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;

    red_mask = (unsigned int)visual->red_mask;
    green_mask = (unsigned int)visual->green_mask;
    blue_mask = (unsigned int)visual->blue_mask;
    red_shift = highest_bit(red_mask) - 7;
    green_shift = highest_bit(green_mask) - 7;
    blue_shift = highest_bit(blue_mask) - 7;

    int bppc = ximage->bits_per_pixel;
    if(bppc > 8 && ximage->byte_order == LSBFirst)
        bppc++;

    // There is a lot of code duplication here but we want to keep the
    // check for bpp out of the main loop or else we would be running a
    // case for every pixel, (which is what Qt does)
    if(bppc == 16){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = (pixel >> 8);
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 17){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
            }
        }
    }
    else if(bppc == 24){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 25){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;

            }
        }
    }
    else if(bppc == 32){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel >> 24;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 8;
                *destData++ = pixel;
            }
        }
    }
    else if(bppc == 33){
        for(y=0; y < image.height(); ++y){
            srcData = (unsigned int *)image.scanLine(y);
            destData = ximage->data + ximage->bytes_per_line*y;
            for(x=0; x < image.width(); ++x){
                alpha = hasAlphaBuffer ? qAlpha(srcData[x]) : 255;
                if(alpha == 0){
                    pixel = bgcolor;
                }
                else if(alpha == 255){
                    pixel = srcData[x];
                }
                else{
                    srcPercent = ((float)alpha)/255.0;
                    destPercent = 1.0-srcPercent;
                    pixel =
                        qRgb((unsigned char)(srcPercent*qRed(srcData[x])+
                                             (destPercent*qRed(bgcolor))),
                             (unsigned char)(srcPercent*qGreen(srcData[x])+
                                             (destPercent*qGreen(bgcolor))),
                             (unsigned char)(srcPercent*qBlue(srcData[x])+
                                             (destPercent*qBlue(bgcolor))));
                }
                r = red_shift > 0 ? qRed(pixel) << red_shift :
                    qRed(pixel) >> -red_shift;
                g = green_shift > 0 ? qGreen(pixel) << green_shift :
                    qGreen(pixel) >> -green_shift;
                b = blue_shift  > 0 ? qBlue(pixel) << blue_shift  :
                    qBlue(pixel) >> -blue_shift;
                pixel = (b & blue_mask) | (g & green_mask) | (r & red_mask);
                *destData++ = pixel;
                *destData++ = pixel >> 8;
                *destData++ = pixel >> 16;
                *destData++ = pixel >> 24;

            }
        }
    }
    if(pix.mask()){
        // don't reset mask with a null bitmap because Qt makes annoying
        // messages if you resize
        pix = QPixmap(image.width(), image.height());
    }
    else if(pix.width() != image.width() || pix.height() != image.height())
        pix.resize(image.width(), image.height());

    if(!tempGC)
        tempGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);

    if(shmimage){
        XCopyArea(dpy, tempPix, pix.handle(), tempGC,
                  0, 0, image.width(), image.height(), 0, 0);
        QApplication::syncX();
    }
    else{
        XPutImage(dpy, pix.handle(), tempGC, ximage, 0, 0, 0, 0,
                  image.width(), image.height());
        free(ximage->data);
        ximage->data = NULL;
        XDestroyImage(ximage);
    }
}





