#include <windows.h>
#include <stdio.h>
#include "resedit.h"

extern HWND hwndParent;
extern HWND hwndMain;

void FreeImageUndo(IMAGEDATA *res);
void DeleteImageDC(IMAGEDATA *res);
void ClearImageDC(IMAGEDATA *res);

static int ClipboardFormat;

void  imageInit()
{
    ClipboardFormat = RegisterClipboardFormat("CCIDE");
}
static int validDIB(
    LPBITMAPINFO DIB,
    DWORD DIBSize,
    int bitmap)
{
    DWORD ANDMaskBytes;
    DWORD XORMaskBytes;
    DWORD colorTableBytes;
    DWORD height;

    if (DIBSize < sizeof(BITMAPINFOHEADER))
        return FALSE;

    if (DIB->bmiHeader.biSize != sizeof(BITMAPINFOHEADER))
        return FALSE;

    if (DIB->bmiHeader.biPlanes != 1)
        return FALSE;

    if (DIB->bmiHeader.biBitCount != 1 &&
            DIB->bmiHeader.biBitCount != 4 &&
            DIB->bmiHeader.biBitCount != 8 &&
            DIB->bmiHeader.biBitCount != 24)
        return FALSE;

    if (bitmap) {
        height = DIB->bmiHeader.biHeight;
        ANDMaskBytes = 0;
    }
    else {
        height = DIB->bmiHeader.biHeight / 2;
        ANDMaskBytes = (((DIB->bmiHeader.biWidth + 31) & 0xffffffe0) >> 3) *
                height;
    }
    colorTableBytes = (1 << DIB->bmiHeader.biBitCount) * sizeof(RGBQUAD);
    XORMaskBytes = ((((DIB->bmiHeader.biWidth * DIB->bmiHeader.biBitCount) +
            31) & 0xffffffe0) >> 3) * height;

    if (DIB->bmiHeader.biSizeImage &&
            DIB->bmiHeader.biSizeImage != XORMaskBytes + ANDMaskBytes)
        return FALSE;

    if (DIBSize != sizeof(BITMAPINFOHEADER) + colorTableBytes +
            XORMaskBytes + ANDMaskBytes)
        return FALSE;

    return TRUE;
}

IMAGEDATA *LoadBitmapFile(char *name)
{
    FILE *bmpFile;
    DWORD fileSize;
    DWORD DIBSize;
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;
    INT colors;
    IMAGEDATA *id;
    
    bmpFile = fopen(name, "rb");
    if (!bmpFile)
        return NULL;

    fseek(bmpFile, 0 , SEEK_END);
    fileSize = ftell(bmpFile);
    fseek(bmpFile, 0, SEEK_SET);

    fread(&bfh, 1, sizeof(BITMAPFILEHEADER), bmpFile);

    if (bfh.bfType != 0x4D42 || bfh.bfSize != fileSize) {
        fclose(bmpFile);
        return NULL;
    }

    fread(&bih, 1, sizeof(BITMAPINFOHEADER), bmpFile);

    DIBSize = fileSize - sizeof(BITMAPFILEHEADER);

    if (!validDIB( &bih, DIBSize, TRUE))
    {
        fclose(bmpFile);
        return NULL;
    }
    /* maximum image = 256 * 256 */
    if (bih.biWidth > 256 || bih.biHeight > 256) {
        fclose(bmpFile);
        return NULL;
    }

    switch (bih.biBitCount) {
        case 1:
            colors = 2;
            break;

        case 4:
            colors = 16;
            break;

        case 8:
            colors = 256;
            break;

        case 24:
            colors = 0;
            break;                
        default:
            fclose(bmpFile);
            return NULL;
    }
    id = calloc(1, sizeof(IMAGEDATA));
    if (!id)
    {
        fclose(bmpFile);
        return NULL;
    }
    id->DIBInfo = calloc(1, DIBSize);
    if (!id->DIBInfo)
    {
        free(id);
        fclose(bmpFile);
        return NULL;
    }
    id->type = FT_BMP;
    id->height = bih.biHeight;
    id->width = bih.biWidth;
    id->colors = colors;
    id->DIBSize = DIBSize;

    /* now read the dib */    
    fseek(bmpFile, sizeof(BITMAPFILEHEADER), SEEK_SET);
    fread(id->DIBInfo, 1, DIBSize, bmpFile);

    fclose(bmpFile);
    return id;
}
int SaveBitmapFile(char *name, IMAGEDATA *image)
{
    BITMAPFILEHEADER bfh;
    FILE *bmpFile;    
    /*
     * Open the file for writing.
     */
    bmpFile = fopen(name, "wb");
    if (!bmpFile)
        return FALSE;
    bfh.bfType = 0x4D42;
    bfh.bfSize = sizeof(BITMAPFILEHEADER) + image->DIBSize;
    bfh.bfReserved1 = 0;
    bfh.bfReserved2 = 0;
    bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
            (image->colors * sizeof(RGBQUAD));

    fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), bmpFile);
    fwrite(&image->DIBInfo, 1, image->DIBSize, bmpFile);

    fclose(bmpFile);

    return TRUE;
}

IMAGEDATA *LoadIconOrCursorFile(char *name, int icon)
{
    INT i;
    LPBITMAPINFO lpBitmapInfo;
    ICOCURSORHDR hdr;               // Header structure of icon/cursor file.
    int imageCount;
    PICOCURSORDESC descriptors;     // Array of ico/cur descriptors.
    int filePos;
    int fileSize;
    int resType;
    FILE *icoFile;
    IMAGEDATA *id, *head =0, **tail;
     
    icoFile = fopen(name,"rb");
    if (!icoFile)
        return FALSE;

    fseek(icoFile, 0, SEEK_END);
    fileSize = ftell(icoFile);
    fseek(icoFile, 0, SEEK_SET);

    if (icon)
        resType = FT_ICO;
    else
        resType = FT_CUR;

    fread(&hdr, 1, sizeof(ICOCURSORHDR), icoFile);
        
    if (hdr.reserved != 0) {
        fclose(icoFile);
        return FALSE;
    }

    /*
     * Get number of images in the file.
     */
    imageCount = hdr.resCount;

    if (!imageCount || imageCount > 64) {
        fclose(icoFile);
        return FALSE;
    }

    if (hdr.resType != 1 && hdr.resType != 2) {
        fclose(icoFile);
        return FALSE;
    }

    descriptors = (PICOCURSORDESC)calloc(imageCount, sizeof(ICOCURSORDESC));
    if (!descriptors)
    {
        fclose(icoFile);
        return FALSE;
    }

    fread(descriptors, sizeof(ICOCURSORDESC), imageCount, icoFile);    
    
    /* position after the descripts is the start of the DIBs */
    filePos = ftell(icoFile);

    /* Validate the descriptors */    
    for (i = 0; i < imageCount; i++) {
        if (descriptors[i].DIBOffset != filePos ||
                filePos + descriptors[i].DIBSize > fileSize) {
            fclose(icoFile);
            free(descriptors);
            return FALSE;
        }

        filePos += descriptors[i].DIBSize;
    }

    for (i = 0; i < imageCount; i++) {
        id = calloc(1,sizeof(IMAGEDATA) + descriptors[i].DIBSize -1);
        if (!id)
        {
            int j;
            free(descriptors);
            fclose(icoFile);
            while (head)
            {
                IMAGEDATA *next = head->next;
                free(head->DIBInfo);
                free(head);
                head = next;
            }
            return FALSE;
        }
        *tail = id;
        tail = &(*tail)->next;
        id->DIBInfo = calloc(1, descriptors[i].DIBSize);
        if (!id->DIBInfo)
        {
            free(descriptors);
            fclose(icoFile);
            while (head)
            {
                IMAGEDATA *next = head->next;
                free(head->DIBInfo);
                free(head);
                head = next;
            }
            return FALSE;
        }
        id->type = resType;
        id->height = descriptors[i].height;
        id->width = descriptors[i].width;
        id->colors = descriptors[i].colors == 8 ? 8 : 0;
        id->DIBSize = descriptors[i].DIBSize;
        id->hotspotX = descriptors[i].hotspotX;
        id->hotspotY = descriptors[i].hotspotY;

        fread(id->DIBInfo, 1, id->DIBSize, icoFile);
        lpBitmapInfo = (LPBITMAPINFO)id->DIBInfo;

        if (!validDIB(lpBitmapInfo, id->DIBSize, FALSE)) {
            free(descriptors);
            fclose(icoFile);
            while (head)
            {
                IMAGEDATA *next = head->next;
                free(head->DIBInfo);
                free(head);
                head = next;
            }
            return FALSE;
        }
        id->width = (INT)lpBitmapInfo->bmiHeader.biWidth;
        id->height = (INT)lpBitmapInfo->bmiHeader.biHeight / 2;
        if (id->colors == 0)
            id->colors = (1 << lpBitmapInfo->bmiHeader.biBitCount);
    }
    fclose(icoFile);
    free(descriptors);    
    
    return head;
}





int SaveIconCursorFile(char *name, IMAGEDATA *res)
{
    ICOCURSORHDR header;     // Header structure of icon/cursor file.
    ICOCURSORDESC descriptor;   // Icon/cursor descriptor struct.
    IMAGEDATA *id;
    int bitsOffset;          // Offset of the actual DIB bits for image.
    FILE *icoFile ;
    int resType = res->type; 
           
    icoFile = fopen(name,"wb");
    if (!icoFile)
    {
        return FALSE;
    }    

    header.reserved = (WORD)0;

    if (resType == FT_ICO)
        header.resType = 1;        // Icon type.
    else
        header.resType = 2;        // Cursor type.

    id = res;
    header.resCount = 0;
    while (id)
    {
        header.resCount ++ ;
        id = id->next;
    }

    fwrite(&header, 1, sizeof(ICOCURSORHDR), icoFile);    

    /*
     * Write all the descriptors.
     */
    bitsOffset = sizeof(ICOCURSORHDR) + (header.resCount * sizeof(ICOCURSORDESC));
    id = res;
    while (id)
    {
        descriptor.width = (BYTE)id->width;
        descriptor.height = (BYTE)id->height;
        descriptor.colors = (resType == FT_ICO) ?
                (BYTE)id->colors : (BYTE)0;
        descriptor.unused = 0;
        descriptor.hotspotX = (WORD)id->hotspotX;
        descriptor.hotspotY = (WORD)id->hotspotY;
        descriptor.DIBSize = id->DIBSize;
        descriptor.DIBOffset = bitsOffset;

        fwrite(&descriptor, 1, sizeof(ICOCURSORDESC), icoFile);

        bitsOffset += descriptor.DIBSize;
    }

    id = res;
    while (id)
    {
        fwrite(id->DIBInfo, 1, id->DIBSize, icoFile);
        id = id->next;
    }    

    fclose(icoFile);
    return TRUE;
}
void FreeImageResource(IMAGEDATA *res)
{
    while (res)
    {
        IMAGEDATA *next = res;
        FreeImageUndo(res);
        DeleteImageDC(res);
        free(res->DIBInfo);
        free(res);
        res = next;
    }
}

static void limit_undo(IMAGEDATA *res)
{
    int i;
    res = res->undo;
    for (i=0; i< UNDO_MAX && res; i++)
    {
        res = res->undo;
    }    
    if (res)
    {
        IMAGEDATA *undo = res->undo;
        res->undo = 0;
        while (undo)
        {
            res = undo;
            DeleteImageDC(undo);
            free(undo->DIBInfo);
            free(undo);
            undo = res;
        }
    }
}
void InsertImageUndo(IMAGEDATA *res)
{
    IMAGEDATA *undo ;
    undo = calloc(1, sizeof(IMAGEDATA));
    if (!undo)
        return;
    *undo = *res;
    undo->next = NULL;
    undo->DIBInfo = calloc(1, res->DIBSize);
    if (!undo->DIBInfo)
    {
        free(undo);
        return;
    }   
    memcpy(undo->DIBInfo, res->DIBInfo, res->DIBSize);
    limit_undo(res);
    res->undo = undo;
}
void FreeImageUndo(IMAGEDATA *res)
{
    IMAGEDATA *old = res;
    while (res->undo)
    {
        IMAGEDATA *next = res->undo;
        DeleteImageDC(res);
        free(res->DIBInfo);
        free(res);
        res->undo = next ;
    }
    old->undo = NULL;
}
void ApplyImageUndo(IMAGEDATA *res)
{
    IMAGEDATA hold , * oldundo;
    if (!res->undo)
        return;
    hold = *res;
    oldundo = res->undo;
    *res = *(oldundo);
    res->next = hold.next;
    DeleteImageDC(&hold);
    free(hold.DIBInfo);
    free(oldundo);
}    
HBITMAP LocalCreateBitmap(HDC dc, int width, int height, int colors)
{
    BITMAPINFOHEADER bmih;

    if (colors == 2) 
    {
        return CreateBitmap(width, height, 1, 1, NULL);
    }
    else 
    {
    
        bmih.biSize = sizeof(BITMAPINFOHEADER);
        bmih.biWidth = width;
        bmih.biHeight = height;
        bmih.biPlanes = 1;
        switch(colors)
        {
            case 16:
                bmih.biBitCount = 4;
                break;
            case 256:
                bmih.biBitCount = 8;
                break;
            case 0:
                bmih.biBitCount = 24;
                break;
        }
        bmih.biCompression = 0;
        bmih.biSizeImage = 0;
        bmih.biXPelsPerMeter = 0;
        bmih.biYPelsPerMeter = 0;
        bmih.biClrUsed = 0;
        bmih.biClrImportant = 0;

        return CreateDIBitmap(dc, &bmih, 0L, NULL, NULL, 0);
    }
}

int CreateImageDC(IMAGEDATA *res)
{
    HDC hdcParent;

    DeleteImageDC(res);

    hdcParent = GetDC(hwndParent);

    if (!(res->hdcImage = CreateCompatibleDC(hdcParent)))
    {
        return FALSE;
    }
    if (!(res->hbmpImage = LocalCreateBitmap(hdcParent, res->width, res->height, 16)))
    {
        ReleaseDC(hwndParent, res->hdcImage);
        return FALSE;   
    }
    SelectObject(res->hdcImage, res->hbmpImage);

    if (res->type == FT_ICO || res->type == FT_CUR) {
        if (!(res->hdcAndMask = CreateCompatibleDC(hdcParent)))
        {
            ReleaseDC(hwndParent, res->hdcImage);
            return FALSE;
        }

        if (!(res->hbmpAndMask = LocalCreateBitmap(hdcParent, res->width, res->height, 2)))
        {
            ReleaseDC(hwndParent, res->hdcImage);
            DeleteDC(res->hdcAndMask);
            return FALSE;
        }

        SelectObject(res->hdcAndMask, res->hbmpAndMask);
    }

    ClearImageDC(res);

    return TRUE;
}

void DeleteImageDC(IMAGEDATA *res)
{
    if (res->hdcImage) {
        DeleteDC(res->hdcImage);
        res->hdcImage = NULL;
        DeleteObject(res->hbmpImage);
        res->hbmpImage = NULL;
    }

    if (res->hdcAndMask) {
        DeleteDC(res->hdcAndMask);
        res->hdcAndMask = NULL;
        DeleteObject(res->hbmpAndMask);
        res->hbmpAndMask = NULL;
    }
}



void ClearImageDC(IMAGEDATA *res)
{
    PatBlt(res->hdcImage, 0, 0, res->width, res->height, WHITENESS);

    if (res->hdcAndMask)
        PatBlt(res->hdcAndMask, 0, 0, res->width, res->height, BLACKNESS);
}


void SplitImageDC(IMAGEDATA *res)
{
    HBITMAP hbmTemp;
    HDC hdcTemp;
    HBITMAP hbmOld;

    /*
     * for the mask bits
     */
    hdcTemp = CreateCompatibleDC(res->hdcImage);
    hbmTemp = CreateCompatibleBitmap(res->hdcImage, res->width, res->height);
    hbmOld = SelectObject(hdcTemp, hbmTemp);

    /*
     * colorize the mask with the screen color
     */
    SetBkColor(hdcTemp, res->rgbScreen);
    BitBlt(hdcTemp, 0, 0, res->width, res->height, res->hdcAndMask, 0, 0, SRCCOPY);

    /*
     * xor the screen colors against the image to recover the true xor mask
     */
    BitBlt(res->hdcImage, 0, 0, res->width, res->height, hdcTemp, 0, 0, SRCINVERT);

    SelectObject(hdcTemp, hbmOld);
    DeleteDC(hdcTemp);
    DeleteObject(hbmTemp);
}




void CombineImageDC(IMAGEDATA *res)
{
    HBITMAP hbmTemp;
    HDC hdcTemp;
    HBITMAP hbmOld;
    HBRUSH hbrOld;
    HBRUSH hbrScreen = CreateSolidBrush(res->rgbScreen);

    /*
     * image DC has an xor mask, make a copy
     */
    hdcTemp = CreateCompatibleDC(res->hdcImage);
    hbmTemp = CreateCompatibleBitmap(res->hdcImage, res->width, res->height);
    hbmOld = SelectObject(hdcTemp, hbmTemp);
    BitBlt(hdcTemp, 0, 0, res->width, res->height, res->hdcImage, 0, 0, SRCCOPY);

    /*
     * image DC is now the current screen color
     */
    hbrOld = SelectObject(res->hdcImage, hbrScreen);
    PatBlt(res->hdcImage, 0, 0, res->width, res->height , PATCOPY);
    SelectObject(res->hdcImage, hbrOld);

    /*
     * reconstruct the display image 
     */
    BitBlt(res->hdcImage, 0, 0, res->width, res->height, res->hdcAndMask, 0, 0, SRCAND);
    BitBlt(res->hdcImage, 0, 0, res->width, res->height, hdcTemp, 0, 0, SRCINVERT);

    SelectObject(hdcTemp, hbmOld);
    DeleteDC(hdcTemp);
    DeleteObject(hbmTemp);
    DeleteObject(hbrScreen);
}

void ChangeImageSize(IMAGEDATA *res, int width, int height)
{
    HBITMAP hbmTemp;
    HDC hdcTemp;

    /* create a temporary dc */
    hdcTemp = CreateCompatibleDC(res->hdcImage);
    hbmTemp = LocalCreateBitmap(res->hdcImage, width, height, res->colors);
    SelectObject(hdcTemp, hbmTemp);

    /* blit into temporary DC */
    BitBlt(hdcTemp, 0, 0, res->width, res->height, res->hdcImage, 0, 0, SRCCOPY);

    res->width = width;
    res->height = height;
    DeleteDC(res->hdcImage);
    DeleteObject(res->hbmpImage);
    res->hdcImage = hdcTemp;
    res->hbmpImage = hbmTemp;
}
void ChangeImageColorCount(IMAGEDATA *res, int colors)
{
    HBITMAP hbmTemp;
    HDC hdcTemp;
    HBITMAP hbmOld;

    /* create a temporary dc */
    hdcTemp = CreateCompatibleDC(res->hdcImage);
    hbmTemp = LocalCreateBitmap(res->hdcImage, res->width, res->height, colors);

    /* blit into temporary DC */
    BitBlt(hdcTemp, 0, 0, res->width, res->height, res->hdcImage, 0, 0, SRCCOPY);

    res->colors = colors;
    DeleteDC(res->hdcImage);
    DeleteObject(res->hbmpImage);
    res->hdcImage = hdcTemp;
    res->hbmpImage = hbmTemp;
}
    
    
    
IMAGEDATA *NewImage(IMAGEDATA *res)
{
    IMAGEDATA *id;
    id = calloc(1, sizeof(IMAGEDATA));
    if (!id)
    {
        return NULL;
    }
    id->colors = res->colors;
    id->height = res->height;
    id->width = res->width; 
    id->type = res->type;
    id->hotspotX = res->hotspotX;
    id->hotspotY = res->hotspotY;
    id->imageDirty = TRUE;
    
    if (!CreateImageDC(id))
    {
        free(id);
        return NULL;
    }
    return id;
}



int LoadImageToDC(IMAGEDATA *res)
{
    static DWORD monoBmpColorTable[] = {
        RGB(0, 0, 0),
        RGB(255, 255, 255)
    };
    LPBITMAPINFO lpbi;
    INT bitCount;
    DWORD colorTableBytes;
    DWORD bits;
    LPBYTE DIBBits;
    HBITMAP hbmMono;
    HBITMAP hbmpImage;
    PBITMAPINFO pbi;

    lpbi = (LPBITMAPINFO)res->DIBInfo;
    bitCount = lpbi->bmiHeader.biBitCount;

    if (!CreateImageDC(res))
        return FALSE;

    if (!(hbmMono = CreateBitmap(1, 1, 1, 1, NULL)))
        return FALSE;

    colorTableBytes = (1 << bitCount) * sizeof(RGBQUAD);
    DIBBits = res->DIBInfo + sizeof(BITMAPINFOHEADER) + colorTableBytes;
    bits = (((((DWORD)res->width * bitCount) + 31) & 0xffffffe0) >> 3) * res->height;

    if (!(pbi = (PBITMAPINFO)calloc(1,
            sizeof(BITMAPINFOHEADER) + colorTableBytes)))
    {
        DeleteObject(hbmMono);
        return FALSE;
    }

    memcpy(pbi, lpbi, sizeof(BITMAPINFOHEADER) + colorTableBytes);

    pbi->bmiHeader.biHeight = res->height;
    pbi->bmiHeader.biSizeImage = bits;

    /*
     * Set up the XOR mask
     */
    hbmpImage = SelectObject(res->hdcImage, hbmMono);
    SetDIBits(res->hdcImage, hbmpImage, 0, res->height, 
              DIBBits, pbi, DIB_RGB_COLORS);
    SelectObject(res->hdcImage, hbmpImage);

    /* and the and mask */    
    if (res->type != FT_BMP) {
        DIBBits += bits;

        colorTableBytes = 2 * sizeof(RGBQUAD);

        /* set up the copy of the bitmap info structure
         * to have a monochrom bitmap
         */
        pbi->bmiHeader.biBitCount = 1;
        pbi->bmiHeader.biSizeImage =
                res->height * ((((DWORD)res->width + 31) & 0xffffffe0) >> 3);
        pbi->bmiHeader.biClrImportant = 0;
        pbi->bmiHeader.biClrUsed = 0;
        memcpy(pbi->bmiColors, &monoBmpColorTable[0],
                colorTableBytes);

        /* move the bits into the and mask */
        hbmpImage = SelectObject(res->hdcAndMask, hbmMono);
        SetDIBits(res->hdcAndMask, hbmpImage, 0, res->height, 
                    DIBBits, pbi, DIB_RGB_COLORS);
        SelectObject(res->hdcAndMask, hbmpImage);

        /* make the viewable image */
        CombineImageDC(res);
    }

    res->imageDirty = FALSE;
    free(pbi);
    DeleteObject(hbmMono);

    return TRUE;

}



/************************************************************************
* ImageSave
*
* Saves the state of the current image into the image list (if it
* is dirty).
*
* History:
*
************************************************************************/

void SaveImage(IMAGEDATA *res)
{
    INT bitCount;
    DWORD colorTableBytes;
    DWORD XORBits;
    DWORD ANDBits;
    LPBYTE DIBInfo;
    DWORD DIBSize;
    LPBITMAPINFOHEADER lpbih;
    LPBYTE lpBits;
    HBITMAP hbmMono;
    HBITMAP hbmpImage;

    if (!res->imageDirty)
        return;

    if (res->type != FT_BMP)
        SplitImageDC(res);

    /*
     * Create a temporary bitmap.
     */
    if (!(hbmMono = CreateBitmap(1, 1, 1, 1, NULL)))
        return;

    switch (res->colors) {
        case 2:
            bitCount = 1;
            break;

        case 16:
            bitCount = 4;
            break;
        case 256:
            bitCount = 8;
            break;
        case 0:
            bitCount = 24;
            break;
    }

    colorTableBytes = (DWORD)res->colors * sizeof(RGBQUAD);
    XORBits = ((((res->width * bitCount) + 31)
            & 0xffffffe0) >> 3) * res->height;

    switch (res->type) {
        case FT_BMP:
            ANDBits = 0;
            break;

        case FT_ICO:
        case FT_CUR:
            ANDBits = res->height *
                    (((res->width + 31) & 0xffffffe0) >> 3);
            break;
    }

    DIBSize = sizeof(BITMAPINFOHEADER) + colorTableBytes + XORBits +
            ANDBits;

    if (!(DIBInfo = calloc( 1, DIBSize))) {
        DeleteObject(hbmMono);
        return;
    }

    lpbih = (LPBITMAPINFOHEADER)DIBInfo;

    if (res->type != FT_BMP) {
        /* get the and bits */
        lpBits = (LPBYTE)lpbih + sizeof(BITMAPINFOHEADER) +
                colorTableBytes + XORBits;

        lpbih->biSize          = sizeof(BITMAPINFOHEADER);
        lpbih->biWidth         = res->width;
        lpbih->biHeight        = res->height;
        lpbih->biPlanes        = 1;
        lpbih->biBitCount      = 1;
        lpbih->biCompression   = BI_RGB;
        lpbih->biSizeImage     = ANDBits;
        lpbih->biXPelsPerMeter = 0;
        lpbih->biYPelsPerMeter = 0;
        lpbih->biClrImportant  = 0;
        lpbih->biClrUsed       = 0;

        hbmpImage = SelectObject(res->hdcAndMask, hbmMono);
        GetDIBits(res->hdcAndMask, hbmpImage, 0, res->height, lpBits,
                (LPBITMAPINFO)lpbih, DIB_RGB_COLORS);
        SelectObject(res->hdcAndMask, hbmpImage);
    }

    /*
     * XOR mask
     */
    lpbih->biSize          = sizeof(BITMAPINFOHEADER);
    lpbih->biWidth         = res->width;
    lpbih->biHeight        = res->height;
    lpbih->biPlanes        = 1;
    lpbih->biBitCount      = bitCount;
    lpbih->biCompression   = BI_RGB;
    lpbih->biSizeImage     = XORBits;
    lpbih->biXPelsPerMeter = 0;
    lpbih->biYPelsPerMeter = 0;
    lpbih->biClrImportant  = 0;
    lpbih->biClrUsed       = 0;

    lpBits = (LPBYTE)lpbih + sizeof(BITMAPINFOHEADER) + colorTableBytes;

    hbmpImage = SelectObject(res->hdcImage, hbmMono);
    GetDIBits(res->hdcImage, hbmpImage, 0, res->height, lpBits,
            (LPBITMAPINFO)lpbih, DIB_RGB_COLORS);
    SelectObject(res->hdcImage, hbmpImage);

    /*
     * For icons and cursors, we have a few extra steps.
     */
    if (res->type != FT_BMP) {
        lpbih->biHeight *= 2;
        lpbih->biSizeImage = XORBits + ANDBits;

        CombineImageDC(res);
    }

    if (res->DIBInfo) {
        free(res->DIBInfo);
    }

    res->DIBSize = DIBSize;
    res->DIBInfo = DIBInfo;

    res->fileDirty = TRUE;
    res->imageDirty = FALSE;

    DeleteObject(hbmMono);
}
    
int CopyImageToClipboard(IMAGEDATA *res, RECT *pick)
{
    HBITMAP hBitmap;
    HDC hDC;
    HANDLE hOldObj;
    int cxPick, cyPick;
    HANDLE hClipboardMem = NULL;

    cxPick = pick->right- pick->left;
    cyPick = pick->top - pick->bottom;

    hDC = CreateCompatibleDC(res->hdcImage);
    hBitmap = LocalCreateBitmap(res->hdcImage,
               cxPick, cyPick, res->colors);

    hOldObj = SelectObject(hDC, hBitmap);
    BitBlt(hDC, 0, 0, cxPick, cyPick, res->hdcImage,
            pick->left, pick->top, SRCCOPY);
    SelectObject(hDC, hOldObj);
    DeleteDC(hDC);

    if (res->type != FT_BMP) {
        LPSTR lpBytes;
        HBITMAP hAndBitmap = CreateCompatibleBitmap(res->hdcAndMask, 
                                                     cxPick, cyPick);
        hDC = CreateCompatibleDC(res->hdcAndMask);

        hOldObj = SelectObject(hDC, hAndBitmap);
        BitBlt(hDC, 0, 0, cxPick, cyPick, res->hdcAndMask,
                pick->left, pick->top, SRCCOPY);
        SelectObject(hDC, hOldObj);
        DeleteDC(hDC);

        hClipboardMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
                (DWORD)((cxPick + 31) >> 3) * cyPick + sizeof(DWORD));
        if (!hClipboardMem) {
            DeleteObject(hAndBitmap);
            DeleteObject(hBitmap);
            return FALSE;
        }

        lpBytes = (LPSTR)GlobalLock(hClipboardMem);

        *((DWORD FAR *)lpBytes) = res->rgbScreen;

        GetBitmapBits(hAndBitmap, (DWORD)((cxPick + 31) >> 3) * cyPick,
                (LPSTR)lpBytes + sizeof(DWORD));
        
        DeleteObject(hAndBitmap);
        GlobalUnlock(hClipboardMem);

    }

    if (!OpenClipboard(hwndMain)) {
        GlobalFree(hClipboardMem);
        DeleteObject(hBitmap);
        return(FALSE);
    }
    /* should process WM_DESTROYCLIPBOARD */
    EmptyClipboard();

    if (res->type != FT_BMP) {
        if (!SetClipboardData(ClipboardFormat, hClipboardMem)) {
            GlobalFree(hClipboardMem);
            DeleteObject(hBitmap);
            CloseClipboard();
            return(FALSE);
        }
    }
    if (!SetClipboardData(CF_BITMAP, hBitmap)) {
        DeleteObject(hBitmap);
        CloseClipboard();
        return(FALSE);
    }

    CloseClipboard();
    
    return TRUE;
}




/* make a new IMAGEDATA with the value on the clipboard */
/* match gives data about DCs for matching the currently selected image */
IMAGEDATA *PasteImageFromClipboard(IMAGEDATA *match)
{
    INT cxClip;
    INT cyClip;
    INT colors;
    
    DWORD rgbClipScreen = 0;
    HBITMAP hbmpClipAnd = NULL;
    HBITMAP hbmpImage, hbmpDest;
    HBITMAP hOldImage;
    HDC hdcDest, hdcSource;
    IMAGEDATA *rv;
    BITMAP bmClip;

    if (!OpenClipboard(hwndMain)) {
        return NULL;
    }

    if (!(hbmpImage = GetClipboardData(CF_BITMAP))) {
        CloseClipboard();
        return NULL;
    }
    /* get size of bitmap */
    GetObject(hbmpImage, sizeof(BITMAP), (LPSTR)&bmClip);

    cxClip = (INT)bmClip.bmWidth;
    cyClip = (INT)bmClip.bmHeight;

    switch (bmClip.bmBitsPixel)
    {
        case 1:
            colors = 2;
            break ;
        case 4:
            colors = 16;
            break;
        case 8:
            colors = 256;
            break;
        case 24:
            colors = 0;
            break ;
        default:
            CloseClipboard();
            return NULL;
    }
    /* copy the clipboard bitmap */
    hdcSource=CreateCompatibleDC(match->hdcImage);
    hdcDest=CreateCompatibleDC(match->hdcImage);
    hbmpDest = CreateCompatibleBitmap(hdcDest, cxClip, cyClip);
    hOldImage = SelectObject(hdcSource, hbmpImage);
    SelectObject(hdcDest, hbmpDest);
    BitBlt(hdcDest, 0, 0, cxClip, cyClip, hdcSource, 0, 0, SRCCOPY);
    SelectObject(hdcSource, hOldImage);
    SelectObject(hdcDest, hOldImage);
        DeleteDC(hdcSource);

    /* copy and mask from clipboard if it exists */
    if (IsClipboardFormatAvailable(ClipboardFormat))
    {
        HANDLE hClipData = GetClipboardData(ClipboardFormat);
        LPBYTE lpClipData = (LPSTR)GlobalLock(hClipData);
        rgbClipScreen = *((DWORD FAR *)lpClipData);
        
        hbmpClipAnd = CreateBitmap(cxClip, cyClip, (BYTE)1, (BYTE)1,
                (LPSTR)lpClipData + sizeof(DWORD));
        GlobalUnlock(hClipData);
    }    
    CloseClipboard();
    
    /* make an object to store the data in */
    rv = calloc(1, sizeof(IMAGEDATA));
    if (!rv)
    {
        DeleteObject(hbmpClipAnd);
        DeleteObject(hbmpDest);
        DeleteDC(hdcDest);
        return NULL;
    }
    
    /* everything ok, fill in our structure and return it */
    rv->colors = colors;
    rv->width = cxClip;
    rv->height = cyClip;
    rv->hbmpAndMask = hbmpClipAnd;
    rv->hdcAndMask = CreateCompatibleDC(match->hdcAndMask);
    rv->hbmpImage = hbmpDest;
    rv->hdcImage = hdcDest;
    rv->type = match->type;
    rv->rgbScreen = rgbClipScreen;
    
    SelectObject(rv->hdcAndMask, rv->hbmpAndMask);
    SelectObject(rv->hdcImage, rv->hbmpImage);
    return rv;
}
/* return true if there is valid data on the clipboard */
int Pastable(void)
{
    HBITMAP hbmpImage;
    BITMAP bmClip;
    if (!OpenClipboard(hwndMain)) {
        return FALSE;
    }

    if (!(hbmpImage = GetClipboardData(CF_BITMAP))) {
        CloseClipboard();
        return FALSE;
    }
    /* get size of bitmap */
    GetObject(hbmpImage, sizeof(BITMAP), (LPSTR)&bmClip);

    CloseClipboard();
    
    switch (bmClip.bmBitsPixel)
    {
        case 1:
        case 4:
        case 8:
        case 24:
            return TRUE;
        default:
            return FALSE;
    }
}
RUBBER *CreateRubberBand(IMAGEDATA *res, int type, POINT pt)
{
    RUBBER *rv = calloc(1, sizeof(RUBBER));
    int bits;
    if (!rv)
        return NULL;
    
    rv->hbmpRubber = CreateBitmap(res->width, res->height, 1, 1, NULL);
    if (!rv->hbmpRubber)
    {
        free(rv);
        return NULL;
    }
    rv->hdcRubber = CreateCompatibleDC(res->hdcAndMask);
    if (!rv->hdcRubber)
    {
        DeleteObject(rv->hbmpRubber);
        free(rv);
        return NULL;
    }
    SelectObject(rv->hdcRubber, rv->hbmpRubber);

    rv->type = type;
    rv->x = pt.x;
    rv->y = pt.y;
    rv->width = 0;
    rv->height = 0;
    rv->bmpwidth = res->width;
    rv->bmpheight = res->height;
    return rv;
}
void DeleteRubberBand(RUBBER *r)
{
    DeleteDC(r->hdcRubber);
    DeleteObject(r->hbmpRubber);
    free(r);
}
void RubberBand(RUBBER *r, POINT pt)
{
    HBRUSH hBrush = CreateSolidBrush(RGB_WHITE);
    HBRUSH hOldBrush;
    r->width = pt.x - r->x;
    r->height = pt.y - r->y;
    
    PatBlt(r->hdcRubber, 0, 0, r->bmpwidth, r->bmpheight, WHITENESS);
    hOldBrush = SelectObject(r->hdcRubber, hBrush);
    switch(r->type)
    {
        case RT_LINE:
            MoveToEx(r->hdcRubber, r->x, r->y, NULL);
            LineTo(r->hdcRubber, r->x + r->width-1, r->y + r->height-1);
            break;
        case RT_RECTANGLE:
            Rectangle(r->hdcRubber, r->x, r->y, r->x + r->width, r->y + r->height);
            break;
        case RT_ELLIPSE:
            Ellipse(r->hdcRubber, r->x, r->y, r->x + r->width, r->y + r->height);
            break;
    }
    SelectObject(r->hdcRubber, hOldBrush);
}
void InstantiateRubberBand(IMAGEDATA *res, RUBBER *r, DWORD brushcolor, DWORD pencolor)
{
    HBRUSH hAndBrush, hOldBrush;
    HPEN hAndPen, hOldPen;
    if (res->type != FT_BMP)
    {
        if (brushcolor == res->rgbScreen)
            hAndBrush = CreateSolidBrush(RGB_WHITE);
        else
            hAndBrush = CreateSolidBrush(RGB_BLACK);
        if (pencolor == res->rgbScreen)
            hAndPen = CreatePen(PS_SOLID, 0, RGB_WHITE);
        else
            hAndPen = CreatePen(PS_SOLID, 0, RGB_BLACK);
        hOldBrush = SelectObject(res->hdcAndMask, hAndBrush);
        hOldPen = SelectObject(res->hbmpAndMask, hAndPen);
    }    
    switch(r->type)
    {
        case RT_LINE:
            MoveToEx(res->hdcImage, r->x, r->y, NULL);
            LineTo(res->hdcImage, r->x + r->width-1, r->y + r->height-1);
            if (res->type != FT_BMP)
            {
                MoveToEx(res->hdcAndMask, r->x, r->y, NULL);
                LineTo(res->hdcAndMask, r->x + r->width-1, r->y + r->height-1);
            }
            break;
        case RT_RECTANGLE:
            Rectangle(res->hdcImage, r->x, r->y, r->x + r->width, r->y + r->height);
            if (res->type != FT_BMP)
            {
                Rectangle(res->hdcAndMask, r->x, r->y, r->x + r->width, r->y + r->height);
            }
            break;
        case RT_ELLIPSE:
            Ellipse(res->hdcImage, r->x, r->y, r->x + r->width, r->y + r->height);
            if (res->type != FT_BMP)
            {
                Ellipse(res->hdcAndMask, r->x, r->y, r->x + r->width, r->y + r->height);
            }
            break;
    }
    if (res->type != FT_BMP)
    {
        SelectObject(res->hdcAndMask, hOldBrush);
        SelectObject(res->hbmpAndMask, hOldPen);
        DeleteObject(hAndBrush);
        DeleteObject(hAndPen);
    }
    res->imageDirty = TRUE;
}
void DrawPoint(IMAGEDATA *res, POINT pt, DWORD color)
{
    HPEN hPen, hOldPen;
    if (res->type != FT_BMP)
    {
        if (color == res->rgbScreen)
            hPen = CreatePen(PS_SOLID, 0, RGB_WHITE);
        else
            hPen = CreatePen(PS_SOLID, 0, RGB_BLACK);
        
        hOldPen = SelectObject(res->hdcAndMask, hPen);
        MoveToEx(res->hdcAndMask, pt.x, pt.y, NULL);
        LineTo(res->hdcAndMask, pt.x, pt.y);
        SelectObject(res->hdcAndMask, hOldPen);
        DeleteObject(hPen);
    }
    MoveToEx(res->hdcImage, pt.x, pt.y, NULL);
    LineTo(res->hdcImage, pt.x, pt.y);

    res->imageDirty = TRUE;
}
void Fill(IMAGEDATA *res, POINT pt)
{
    DWORD color;
    MoveToEx(res->hdcImage, pt.x, pt.y, NULL);
    LineTo(res->hdcImage, pt.x, pt.y);
    color = GetPixel(res->hdcImage, pt.x, pt.y);

    
    if (res->type != FT_BMP)
    {
        /* the following makes a monochrome bitmap where the white bits
         * are the bits with the color of the color to be filled
         */
        HDC bwdc = CreateCompatibleDC(res->hdcAndMask);
        HBITMAP bwbmp = CreateBitmap(res->width, res->height, 1, 1, NULL), oldbmp;
        HPEN pen = CreatePen(PS_SOLID, 0, RGB_BLACK), oldpen;
        oldbmp = SelectObject(bwdc, bwbmp);
        SetBkColor(res->hdcImage, color);
        BitBlt(bwdc, 0, 0, res->width, res->height, res->hdcImage, 0, 0, SRCCOPY);
        
        /* flood fill to black */
        oldpen = SelectObject(bwdc, pen);
        MoveToEx(bwdc, pt.x, pt.y, NULL);
        LineTo(bwdc, pt.x, pt.y);
        SelectObject(bwdc, oldpen);
        DeleteObject(pen);
        ExtFloodFill(bwdc, pt.x, pt.y, RGB_WHITE, FLOODFILLSURFACE);
        
        /* XOR with original mask to change flood filled area to white
         * and other areas with the original color to black
         */
        BitBlt(bwdc, 0, 0, res->width, res->height, res->hdcImage, 0, 0, SRCINVERT);
        
        /* update the and mask */
        if (color == res->rgbScreen)
        {
            /* screen color, or bytes into AND mask */
            BitBlt(res->hdcAndMask, 0, 0, res->width, res->height, bwdc, 0, 0, SRCPAINT);
        }
        else
        {
            /* not screen color, take bits out of AND mask */
            HDC hdc2 = CreateCompatibleDC(bwdc);
            BitBlt(hdc2, 0, 0, res->width, res->height, bwdc, 0, 0, SRCINVERT);
            
            BitBlt(res->hdcAndMask, 0, 0, res->width, res->height, hdc2, 0, 0, SRCAND);
            DeleteDC(hdc2);
        }
        SelectObject(bwdc, oldbmp);
        DeleteObject(bwbmp);
        DeleteDC(bwdc);
    }
    /* now flood fill the image */
    ExtFloodFill(res->hdcImage, pt.x, pt.y, color, FLOODFILLSURFACE);
    res->imageDirty = TRUE;
}