IMAGE.C

/****************************************************************************/ 
/* */
/* Copyright (C) 1987-1996 Microsoft Corp. */
/* 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);
}