    /****************************************************************************/
    /*                                                                          */
    /*                 Copyright (c) Microsoft Corp.  1987, 1991                */
    /*                           All Rights Reserved                            */
    /*                                                                          */
    /****************************************************************************/
    /****************************** Module Header *******************************
    * Module Name: image.c
    *
    * Routines for opening and saving images.
    *
    * History:
    *
    ****************************************************************************/
    
    #include "imagedit.h"
    
    #include <stdio.h>
    #include <string.h>
    
    
    
    /************************************************************************
    * ImageNew
    *
    * Creates a new image for the specified device.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL ImageNew(
        PDEVICE pDevice)
    {
        PIMAGEINFO pImage;
    
        if (!(pImage = ImageLinkAlloc(pDevice, pDevice->cx, pDevice->cy,
                0, 0, pDevice->nColors)))
            return FALSE;
    
        /*
         * Allocate work space for the new image.
         */
        if (!ImageDCCreate(pDevice->iType, pImage->cx, pImage->cy,
                pImage->nColors)) {
            ImageLinkFree(pImage);
            return FALSE;
        }
    
        gpImageCur = pImage;
        gnImages++;
        giType = pDevice->iType;
    
        /*
         * Initialize the pick rectangle to encompass the entire image.
         */
        PickSetRect(0, 0, gcxImage - 1, gcyImage - 1);
    
        /*
         * Mark the newly created image as dirty to be sure it gets saved.
         */
        fImageDirty = TRUE;
    
        /*
         * Update the palettes.
         */
        SetColorPalette(gnColors, giType, FALSE);
        PropBarUpdate();
        ToolboxUpdate();
        ViewReset();
    
        /*
         * Reset the workspace window and then show it.
         */
        WorkReset();
        ShowWindow(ghwndWork, SW_SHOWNORMAL);
    
        return TRUE;
    }
    
    
    
    /************************************************************************
    * ImageNewBitmap
    *
    * Creates a new bitmap image given a set of characteristics.  After
    * device link is created for those characteristics, ImageNew() is
    * called to do the actual work.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL ImageNewBitmap(
        INT cx,
        INT cy,
        INT nColors)
    {
        PDEVICE pDevice;
    
        if (!(pDevice = DeviceLinkAlloc(FT_BITMAP, NULL, nColors, cx, cy)))
            return FALSE;
    
        return ImageNew(pDevice);
    }
    
    
    
    /************************************************************************
    * ImageOpen
    *
    * Determines what has to be done to open the specified image.  If it
    * is not already the current image, it will save the current image
    * then will call ImageOpen2() to open the new one.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL ImageOpen(
        PIMAGEINFO pImage)
    {
        /*
         * New image is already current.  Return success.
         */
        if (pImage == gpImageCur)
            return TRUE;
    
        /*
         * Is this an image for a known device?
         */
        if (pImage->pDevice) {
            /*
             * Save away the current image.
             */
            ImageSave();
    
            /*
             * Do the real open of the new image.
             */
            return ImageOpen2(pImage);
        }
        else {
            Message(MSG_CANTEDITIMAGE);
            return FALSE;
        }
    }
    
    
    
    /************************************************************************
    * ImageOpen2
    *
    * Unconditionally opens up the specified image for editing.  This involves
    * parsing the DIB into various globals, putting the bits onto the screen
    * and updating the different palettes appropriately.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL ImageOpen2(
        PIMAGEINFO pImage)
    {
        HCURSOR hcurOld;
        LPBITMAPINFO lpbi;
        INT iBitCount;
        INT cx;
        INT cy;
        INT nColors;
        DWORD cbColorTable;
        DWORD cbBits;
        LPBYTE lpDIBBits;
        HBITMAP hbmMono;
        HBITMAP hbmImage;
        PBITMAPINFO pbi;
    
        hcurOld = SetCursor(hcurWait);
    
        lpbi = (LPBITMAPINFO)pImage->DIBPtr;
        iBitCount = lpbi->bmiHeader.biBitCount;
    
        cx = (INT)lpbi->bmiHeader.biWidth;
        cy = (INT)lpbi->bmiHeader.biHeight;
        if (giType != FT_BITMAP)
            cy /= 2;
    
        nColors = pImage->nColors;
    
        /*
         * Allocate work space for the image.
         */
        if (!ImageDCCreate(giType, cx, cy, nColors))
            goto Error1;
    
        /*
         * Create a temporary bitmap.
         */
        if (!(hbmMono = CreateBitmap(1, 1, 1, 1, NULL))) {
            Message(MSG_OUTOFMEMORY);
            goto Error2;
        }
    
        cbColorTable = (1 << iBitCount) * sizeof(RGBQUAD);
        lpDIBBits = (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + cbColorTable;
        cbBits = (((((DWORD)cx * iBitCount) + 31) & 0xffffffe0) >> 3) * cy;
    
        /*
         * Make a copy of the info header and color table.
         */
        if (!(pbi = (PBITMAPINFO)MyAlloc(
                sizeof(BITMAPINFOHEADER) + (INT)cbColorTable)))
            goto Error3;
    
        memcpy((LPBYTE)pbi, lpbi, sizeof(BITMAPINFOHEADER) + (INT)cbColorTable);
    
        /*     * Adjust some fields.  The size field in an icon/cursor dib
         * includes the AND mask bits, which we don't want to include
         * right now.
         */
        pbi->bmiHeader.biHeight = cy;
        pbi->bmiHeader.biSizeImage = cbBits;
    
        /*
         * Set the bits into the XOR mask.
         */
        hbmImage = SelectObject(ghdcImage, hbmMono);
        SetDIBits(ghdcImage, hbmImage, 0, cy, lpDIBBits, pbi, DIB_RGB_COLORS);
        SelectObject(ghdcImage, hbmImage);
    
        /*
         * If we are editing an icon or cursor, we need to set the bits
         * for the AND mask also now.
         */
        if (giType != FT_BITMAP) {
            /*
             * Skip past the XOR mask bits to the AND bits that follow.
             */
            lpDIBBits += cbBits;
    
            cbColorTable = 2 * sizeof(RGBQUAD);
    
            /*
             * Adjust some fields in the copy of the bitmap info structure,
             * then copy a monochrome color table into it.  Note that we
             * are using the same allocated copy, which is ok because there
             * will always be enough room allocated for the monochrome
             * color table.
             */
            pbi->bmiHeader.biBitCount = 1;
            pbi->bmiHeader.biSizeImage =
                    cy * ((((DWORD)cx + 31) & 0xffffffe0) >> 3);
            pbi->bmiHeader.biClrImportant = 0;
            pbi->bmiHeader.biClrUsed = 0;
            memcpy((PBYTE)pbi->bmiColors, (PBYTE)gargbColorTable2,
                    (INT)cbColorTable);
    
            /*
             * Set the bits into the AND mask.
             */
            hbmImage = SelectObject(ghdcANDMask, hbmMono);
            SetDIBits(ghdcANDMask, hbmImage, 0, cy, lpDIBBits, pbi,
                    DIB_RGB_COLORS);
            SelectObject(ghdcANDMask, hbmImage);
    
            /*
             * Combine the XOR and AND masks into a viewable image.
             */
            ImageDCCombine(ghdcImage, gcxImage, gcyImage, ghdcANDMask);
        }
    
        MyFree(pbi);
        DeleteObject(hbmMono);
    
        /*
         * Set the current image pointer.
         */
        gpImageCur = pImage;
        fImageDirty = FALSE;
        /*
         * Initialize the pick rectangle to encompass the entire image.
         */
        PickSetRect(0, 0, gcxImage - 1, gcyImage - 1);
    
        SetColorPalette(gnColors, giType, FALSE);
    
        /*
         * Update the properties bar info and toolbox.
         */
        PropBarUpdate();
        ToolboxUpdate();
    
        ViewReset();
    
        /*
         * Reset the workspace window and then show it.
         */
        WorkReset();
        ShowWindow(ghwndWork, SW_SHOWNORMAL);
    
        SetCursor(hcurOld);
    
        return TRUE;
    
    Error3:
        DeleteObject(hbmMono);
    
    Error2:
        ImageDCDelete();
    
    Error1:
        SetCursor(hcurOld);
    
        return FALSE;
    }
    
    
    
    /************************************************************************
    * ImageSave
    *
    * Saves the state of the current image into the image list (if it
    * is dirty).
    *
    * History:
    *
    ************************************************************************/
    
    VOID ImageSave(VOID)
    {
        HCURSOR hcurOld;
        INT iBitCount;
        DWORD cbColorTable;
        DWORD cbXORBits;
        DWORD cbANDBits;
        HANDLE hDIB;
        DWORD dwDIBSize;
        LPBITMAPINFOHEADER lpbih;
        LPBYTE lpBits;
        HBITMAP hbmMono;
        HBITMAP hbmImage;
    
        if (!fImageDirty)
            return;
    
        hcurOld = SetCursor(hcurWait);
    
        /*
         * Separate out the XOR and AND masks for ico/cur images.
         */
        if (giType != FT_BITMAP)
            ImageDCSeparate(ghdcImage, gcxImage, gcyImage, ghdcANDMask, grgbScreen);
    
        /*
         * Create a temporary bitmap.
         */
        if (!(hbmMono = CreateBitmap(1, 1, 1, 1, NULL))) {
            Message(MSG_OUTOFMEMORY);
            goto Error1;
        }
    
        switch (gpImageCur->nColors) {
            case 2:
                iBitCount = 1;
                break;
    
            case 16:
                iBitCount = 4;
                break;
        }
    
        cbColorTable = (DWORD)gpImageCur->nColors * sizeof(RGBQUAD);
        cbXORBits = (((((DWORD)gpImageCur->cx * iBitCount) + 31)
                & 0xffffffe0) >> 3) * gpImageCur->cy;
    
        switch (giType) {
            case FT_BITMAP:
                cbANDBits = 0;
                break;
    
            case FT_ICON:
            case FT_CURSOR:
                cbANDBits = (DWORD)gpImageCur->cy *
                        ((((DWORD)gpImageCur->cx + 31) & 0xffffffe0) >> 3);
                break;
        }
    
        dwDIBSize = sizeof(BITMAPINFOHEADER) + cbColorTable + cbXORBits +
                cbANDBits;
    
        /*
         * Allocate space for the DIB for this image.
         */
        if (!(hDIB = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwDIBSize))) {
            Message(MSG_OUTOFMEMORY);
            goto Error2;
        }
    
        lpbih = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
    
        /*
         * For icons and cursors, we need to get the AND mask bits first.
         */
        if (giType != FT_BITMAP) {
            /*
             * Point to where the AND bits should go.
             */
            lpBits = (LPBYTE)lpbih + sizeof(BITMAPINFOHEADER) +
                    cbColorTable + cbXORBits;
    
            /*
             * Fill in the bitmap info header for getting the AND bits.
             */
            lpbih->biSize          = sizeof(BITMAPINFOHEADER);
            lpbih->biWidth         = gpImageCur->cx;
            lpbih->biHeight        = gpImageCur->cy;
            lpbih->biPlanes        = 1;
            lpbih->biBitCount      = 1;
            lpbih->biCompression   = BI_RGB;
            lpbih->biSizeImage     = cbANDBits;
            lpbih->biXPelsPerMeter = 0;
            lpbih->biYPelsPerMeter = 0;
            lpbih->biClrImportant  = 0;
            lpbih->biClrUsed       = 0;
    
            /*
             * Get the bits from the AND mask.
             */
            hbmImage = SelectObject(ghdcANDMask, hbmMono);
            GetDIBits(ghdcANDMask, hbmImage, 0, gpImageCur->cy, lpBits,
                    (LPBITMAPINFO)lpbih, DIB_RGB_COLORS);
            SelectObject(ghdcANDMask, hbmImage);
        }
    
        /*
         * Fill in the bitmap info header for getting the XOR bits.
         */
        lpbih->biSize          = sizeof(BITMAPINFOHEADER);
        lpbih->biWidth         = gpImageCur->cx;
        lpbih->biHeight        = gpImageCur->cy;
        lpbih->biPlanes        = 1;
        lpbih->biBitCount      = iBitCount;
        lpbih->biCompression   = BI_RGB;
        lpbih->biSizeImage     = cbXORBits;
        lpbih->biXPelsPerMeter = 0;
        lpbih->biYPelsPerMeter = 0;
        lpbih->biClrImportant  = 0;
        lpbih->biClrUsed       = 0;
    
        /*
         * Point to where the XOR bits should go.
         */
        lpBits = (LPBYTE)lpbih + sizeof(BITMAPINFOHEADER) + cbColorTable;
    
        /*
         * Get the bits from the XOR mask.
         */
        hbmImage = SelectObject(ghdcImage, hbmMono);
        GetDIBits(ghdcImage, hbmImage, 0, gpImageCur->cy, lpBits,
                (LPBITMAPINFO)lpbih, DIB_RGB_COLORS);
        SelectObject(ghdcImage, hbmImage);
    
        /*
         * For icons and cursors, we have a few extra steps.
         */
        if (giType != FT_BITMAP) {
            /*
             * Set the fields in the info structure to their final
             * values.  The saved value in the bitmap info header for the
             * height in an icon/cursor DIB is really twice the height of
             * the image, and the size of the image is the size of both
             * the XOR and the AND mask bits.
             */
            lpbih->biHeight *= 2;
            lpbih->biSizeImage = cbXORBits + cbANDBits;
    
            /*
             * Recombine the XOR and AND masks now that we have their bits.
             */
            ImageDCCombine(ghdcImage, gcxImage, gcyImage, ghdcANDMask);
        }
    
        /*
         * Free any old DIB.
         */
        if (gpImageCur->DIBhandle) {
            GlobalUnlock(gpImageCur->DIBhandle);
            GlobalFree(gpImageCur->DIBhandle);
        }
    
        /*
         * Set the image structure to point to the newly created DIB.
         */
        gpImageCur->DIBSize = dwDIBSize;
        gpImageCur->DIBhandle = hDIB;
        gpImageCur->DIBPtr = (LPBYTE)lpbih;
    
        fFileDirty = TRUE;
        fImageDirty = FALSE;
    
    Error2:
        DeleteObject(hbmMono);
    
    Error1:
        SetCursor (hcurOld);
    }
