DIBUTIL.C

//********************************************************************** 
//
// dibutil.c
//
// Source file for Device-Independent Bitmap (DIB) API. Provides
// the following functions:
//
// CreateDIB() - Creates new DIB
// FindDIBBits() - Sets pointer to the DIB bits
// DIBWidth() - Gets the width of the DIB
// DIBHeight() - Gets the height of the DIB
// PaletteSize() - Calculates the buffer size required by a palette
// DIBNumColors() - Calculates number of colors in the DIB's color table
// CreateDIBPalette() - Creates a palette from a DIB
// DIBToBitmap() - Creates a bitmap from a DIB
// BitmapToDIB() - Creates a DIB from a bitmap
// PalEntriesOnDevice()- Gets the number of palette entries of a device
// GetSystemPalette() - Returns a handle to the current system palette
// AllocRoomForDIB() - Allocates memory for a DIB
// ChangeDIBFormat() - Changes a DIB's BPP and/or compression format
// ChangeBitmapFormat()- Changes a bitmap to a DIB with specified BPP and
// compression format
//
// Written by Microsoft Product Support Services, Developer Support.
// Copyright 1991-1998 Microsoft Corporation. All rights reserved.
//**********************************************************************

#define STRICT // enable strict type checking

#include <windows.h>
#include <assert.h>
#include "dibapi.h"
#include "dibutil.h"
#include <stdio.h>


/*************************************************************************
*
* CreateDIB()
*
* Parameters:
*
* DWORD dwWidth - Width for new bitmap, in pixels
* DWORD dwHeight - Height for new bitmap
* WORD wBitCount - Bit Count for new DIB (1, 4, 8, or 24)
*
* Return Value:
*
* HDIB - Handle to new DIB
*
* Description:
*
* This function allocates memory for and initializes a new DIB by
* filling in the BITMAPINFOHEADER, allocating memory for the color
* table, and allocating memory for the bitmap bits. As with all
* HDIBs, the header, colortable and bits are all in one contiguous
* memory block. This function is similar to the CreateBitmap()
* Windows API.
*
* The colortable and bitmap bits are left uninitialized (zeroed) in the
* returned HDIB.
*
*
************************************************************************/

HDIB CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount)
{
BITMAPINFOHEADER bi; // bitmap header
LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER
DWORD dwLen; // size of memory block
HDIB hDIB;
DWORD dwBytesPerLine; // Number of bytes per scanline


// Make sure bits per pixel is valid

if (wBitCount <= 1)
wBitCount = 1;
else if (wBitCount <= 4)
wBitCount = 4;
else if (wBitCount <= 8)
wBitCount = 8;
else if (wBitCount <= 24)
wBitCount = 24;
else
wBitCount = 4; // set default value to 4 if parameter is bogus

// initialize BITMAPINFOHEADER

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = dwWidth; // fill in width from parameter
bi.biHeight = dwHeight; // fill in height from parameter
bi.biPlanes = 1; // must be 1
bi.biBitCount = wBitCount; // from parameter
bi.biCompression = BI_RGB;
bi.biSizeImage = 0; // 0's here mean "default"
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

// calculate size of memory block required to store the DIB. This
// block should be big enough to hold the BITMAPINFOHEADER, the color
// table, and the bits

dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth);
dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + (dwBytesPerLine * dwHeight);

// alloc memory block to store our bitmap

hDIB = GlobalAlloc(GHND, dwLen);

// major bummer if we couldn't get memory block

if (!hDIB)
return NULL;

// lock memory and get pointer to it

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);

// use our bitmap info structure to fill in first part of
// our DIB with the BITMAPINFOHEADER

*lpbi = bi;

// Since we don't know what the colortable and bits should contain,
// just leave these blank. Unlock the DIB and return the HDIB.

GlobalUnlock(hDIB);

//return handle to the DIB

return hDIB;
}


/*************************************************************************
*
* FindDIBBits()
*
* Parameter:
*
* LPSTR lpDIB - pointer to packed-DIB memory block
*
* Return Value:
*
* LPSTR - pointer to the DIB bits
*
* Description:
*
* This function calculates the address of the DIB's bits and returns a
* pointer to the DIB bits.
*
************************************************************************/

LPSTR FindDIBBits(LPSTR lpDIB)
{
return (lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB));
}


/*************************************************************************
*
* DIBWidth()
*
* Parameter:
*
* LPSTR lpDIB - pointer to packed-DIB memory block
*
* Return Value:
*
* DWORD - width of the DIB
*
* Description:
*
* This function gets the width of the DIB from the BITMAPINFOHEADER
* width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER
* width field if it is an OS/2-style DIB.
*
************************************************************************/


DWORD DIBWidth(LPSTR lpDIB)
{
LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB
LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB

// point to the header (whether Win 3.0 and OS/2)

lpbmi = (LPBITMAPINFOHEADER)lpDIB;
lpbmc = (LPBITMAPCOREHEADER)lpDIB;

// return the DIB width if it is a Win 3.0 DIB

if (lpbmi->biSize == sizeof(BITMAPINFOHEADER))
return lpbmi->biWidth;
else // it is an OS/2 DIB, so return its width
return (DWORD)lpbmc->bcWidth;
}


/*************************************************************************
*
* DIBHeight()
*
* Parameter:
*
* LPSTR lpDIB - pointer to packed-DIB memory block
*
* Return Value:
*
* DWORD - height of the DIB
*
* Description:
*
* This function gets the height of the DIB from the BITMAPINFOHEADER
* height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER
* height field if it is an OS/2-style DIB.
*
************************************************************************/

DWORD DIBHeight(LPSTR lpDIB)
{
LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB
LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB

// point to the header (whether OS/2 or Win 3.0

lpbmi = (LPBITMAPINFOHEADER)lpDIB;
lpbmc = (LPBITMAPCOREHEADER)lpDIB;

// return the DIB height if it is a Win 3.0 DIB
if (lpbmi->biSize == sizeof(BITMAPINFOHEADER))
return lpbmi->biHeight;
else // it is an OS/2 DIB, so return its height
return (DWORD)lpbmc->bcHeight;
}


/*************************************************************************
*
* PaletteSize()
*
* Parameter:
*
* LPSTR lpDIB - pointer to packed-DIB memory block
*
* Return Value:
*
* WORD - size of the color palette of the DIB
*
* Description:
*
* This function gets the size required to store the DIB's palette by
* multiplying the number of colors by the size of an RGBQUAD (for a
* Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2-
* style DIB).
*
************************************************************************/

WORD PaletteSize(LPSTR lpDIB)
{
// calculate the size required by the palette
if (IS_WIN30_DIB (lpDIB))
return (DIBNumColors(lpDIB) * sizeof(RGBQUAD));
else
return (DIBNumColors(lpDIB) * sizeof(RGBTRIPLE));
}


/*************************************************************************
*
* DIBNumColors()
*
* Parameter:
*
* LPSTR lpDIB - pointer to packed-DIB memory block
*
* Return Value:
*
* WORD - number of colors in the color table
*
* Description:
*
* This function calculates the number of colors in the DIB's color table
* by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style
* DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256,
* if 24, no colors in color table.
*
************************************************************************/

WORD DIBNumColors(LPSTR lpDIB)
{
WORD wBitCount; // DIB bit count

// If this is a Windows-style DIB, the number of colors in the
// color table can be less than the number of bits per pixel
// allows for (i.e. lpbi->biClrUsed can be set to some value).
// If this is the case, return the appropriate value.


if (IS_WIN30_DIB(lpDIB))
{
DWORD dwClrUsed;

dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed;
if (dwClrUsed)

return (WORD)dwClrUsed;
}

// Calculate the number of colors in the color table based on
// the number of bits per pixel for the DIB.

if (IS_WIN30_DIB(lpDIB))
wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
else
wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;

// return number of colors based on bits per pixel

switch (wBitCount)
{
case 1:
return 2;

case 4:
return 16;

case 8:
return 256;

default:
return 0;
}
}


/*************************************************************************
*
* CreateDIBPalette()
*
* Parameter:
*
* HDIB hDIB - specifies the DIB
*
* Return Value:
*
* HPALETTE - specifies the palette
*
* Description:
*
* This function creates a palette from a DIB by allocating memory for the
* logical palette, reading and storing the colors from the DIB's color table
* into the logical palette, creating a palette from this logical palette,
* and then returning the palette's handle. This allows the DIB to be
* displayed using the best possible colors (important for DIBs with 256 or
* more colors).
*
************************************************************************/

HPALETTE CreateDIBPalette(HDIB hDIB)
{
LPLOGPALETTE lpPal; // pointer to a logical palette
HANDLE hLogPal; // handle to a logical palette
HPALETTE hPal = NULL; // handle to a palette
int i, wNumColors; // loop index, number of colors in color table
LPSTR lpbi; // pointer to packed-DIB
LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0)
LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (OS/2)
BOOL bWinStyleDIB; // Win3.0 DIB?

// if handle to DIB is invalid, return NULL

if (!hDIB)
return NULL;

// lock DIB memory block and get a pointer to it

lpbi = GlobalLock(hDIB);

// get pointer to BITMAPINFO (Win 3.0)

lpbmi = (LPBITMAPINFO)lpbi;

// get pointer to BITMAPCOREINFO (OS/2 1.x)

lpbmc = (LPBITMAPCOREINFO)lpbi;

// get the number of colors in the DIB

wNumColors = DIBNumColors(lpbi);

// is this a Win 3.0 DIB?

bWinStyleDIB = IS_WIN30_DIB(lpbi);
if (wNumColors)
{
// allocate memory block for logical palette

hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) +
sizeof(PALETTEENTRY) * wNumColors);

// if not enough memory, clean up and return NULL

if (!hLogPal)
{
GlobalUnlock(hDIB);
return NULL;
}

// lock memory block and get pointer to it

lpPal = (LPLOGPALETTE)GlobalLock(hLogPal);

// set version and number of palette entries

lpPal->palVersion = PALVERSION;
lpPal->palNumEntries = wNumColors;

// store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB)
// into palette

for (i = 0; i < wNumColors; i++)
{
if (bWinStyleDIB)
{
lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed;
lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue;
lpPal->palPalEntry[i].peFlags = 0;
}
else
{
lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed;
lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen;
lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue;
lpPal->palPalEntry[i].peFlags = 0;
}
}

// create the palette and get handle to it

hPal = CreatePalette(lpPal);

// if error getting handle to palette, clean up and return NULL

if (!hPal)
{
GlobalUnlock(hLogPal);
GlobalFree(hLogPal);
return NULL;
}
}

// clean up

GlobalUnlock(hLogPal);
GlobalFree(hLogPal);
GlobalUnlock(hDIB);

// return handle to DIB's palette
return hPal;
}


/*************************************************************************
*
* DIBToBitmap()
*
* Parameters:
*
* HDIB hDIB - specifies the DIB to convert
*
* HPALETTE hPal - specifies the palette to use with the bitmap
*
* Return Value:
*
* HBITMAP - identifies the device-dependent bitmap
*
* Description:
*
* This function creates a bitmap from a DIB using the specified palette.
* If no palette is specified, default is used.
*
* NOTE:
*
* The bitmap returned from this funciton is always a bitmap compatible
* with the screen (e.g. same bits/pixel and color planes) rather than
* a bitmap with the same attributes as the DIB. This behavior is by
* design, and occurs because this function calls CreateDIBitmap to
* do its work, and CreateDIBitmap always creates a bitmap compatible
* with the hDC parameter passed in (because it in turn calls
* CreateCompatibleBitmap).
*
* So for instance, if your DIB is a monochrome DIB and you call this
* function, you will not get back a monochrome HBITMAP -- you will
* get an HBITMAP compatible with the screen DC, but with only 2
* colors used in the bitmap.
*
* If your application requires a monochrome HBITMAP returned for a
* monochrome DIB, use the function SetDIBits().
*
* Also, the DIBpassed in to the function is not destroyed on exit. This
* must be done later, once it is no longer needed.
*
************************************************************************/

HBITMAP DIBToBitmap(HDIB hDIB, HPALETTE hPal)
{
LPSTR lpDIBHdr, lpDIBBits; // pointer to DIB header, pointer to DIB bits
HBITMAP hBitmap; // handle to device-dependent bitmap
HDC hDC; // handle to DC
HPALETTE hOldPal = NULL; // handle to a palette

// if invalid handle, return NULL

if (!hDIB)
return NULL;

// lock memory block and get a pointer to it

lpDIBHdr = GlobalLock(hDIB);

// get a pointer to the DIB bits

lpDIBBits = FindDIBBits(lpDIBHdr);

// get a DC

hDC = GetDC(NULL);
if (!hDC)
{
// clean up and return NULL

GlobalUnlock(hDIB);
return NULL;
}

// select and realize palette

if (hPal)
hOldPal = SelectPalette(hDC, hPal, FALSE);

RealizePalette(hDC);

// create bitmap from DIB info. and bits
hBitmap = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT,
lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS);

// restore previous palette
if (hOldPal)
SelectPalette(hDC, hOldPal, FALSE);

// clean up
ReleaseDC(NULL, hDC);
GlobalUnlock(hDIB);

// return handle to the bitmap
return hBitmap;
}


/*************************************************************************
*
* BitmapToDIB()
*
* Parameters:
*
* HBITMAP hBitmap - specifies the bitmap to convert
*
* HPALETTE hPal - specifies the palette to use with the bitmap
*
* Return Value:
*
* HDIB - identifies the device-dependent bitmap
*
* Description:
*
* This function creates a DIB from a bitmap using the specified palette.
*
************************************************************************/

HDIB BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal)
{
BITMAP bm; // bitmap structure
BITMAPINFOHEADER bi; // bitmap header
LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER
DWORD dwLen; // size of memory block
HANDLE hDIB, h; // handle to DIB, temp handle
HDC hDC; // handle to DC
WORD biBits; // bits per pixel

// check if bitmap handle is valid

if (!hBitmap)
return NULL;

// fill in BITMAP structure, return NULL if it didn't work

if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm))
return NULL;

// if no palette is specified, use default palette

if (hPal == NULL)
hPal = GetStockObject(DEFAULT_PALETTE);

// calculate bits per pixel

biBits = bm.bmPlanes * bm.bmBitsPixel;

// make sure bits per pixel is valid

if (biBits <= 1)
biBits = 1;
else if (biBits <= 4)
biBits = 4;
else if (biBits <= 8)
biBits = 8;
else // if greater than 8-bit, force to 24-bit
biBits = 24;

// initialize BITMAPINFOHEADER

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = biBits;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

// calculate size of memory block required to store BITMAPINFO

dwLen = bi.biSize + PaletteSize((LPSTR)&bi);

// get a DC

hDC = GetDC(NULL);

// select and realize our palette

hPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);

// alloc memory block to store our bitmap

hDIB = GlobalAlloc(GHND, dwLen);

// if we couldn't get memory block

if (!hDIB)
{
// clean up and return NULL

SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}

// lock memory and get pointer to it

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);

/// use our bitmap info. to fill BITMAPINFOHEADER

*lpbi = bi;

// call GetDIBits with a NULL lpBits param, so it will calculate the
// biSizeImage field for us

GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS);

// get the info. returned by GetDIBits and unlock memory block

bi = *lpbi;
GlobalUnlock(hDIB);

// if the driver did not fill in the biSizeImage field, make one up
if (bi.biSizeImage == 0)
bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;

// realloc the buffer big enough to hold all the bits

dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage;

if (h = GlobalReAlloc(hDIB, dwLen, 0))
hDIB = h;
else
{
// clean up and return NULL

GlobalFree(hDIB);
hDIB = NULL;
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}

// lock memory block and get pointer to it */

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);

// call GetDIBits with a NON-NULL lpBits param, and actualy get the
// bits this time

if (GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, (LPSTR)lpbi +
(WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS) == 0)
{
// clean up and return NULL

GlobalUnlock(hDIB);
hDIB = NULL;
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}

bi = *lpbi;

// clean up
GlobalUnlock(hDIB);
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);

// return handle to the DIB
return hDIB;
}


/*************************************************************************
*
* PalEntriesOnDevice()
*
* Parameter:
*
* HDC hDC - device context
*
* Return Value:
*
* int - number of palette entries on device
*
* Description:
*
* This function gets the number of palette entries on the specified device
*
************************************************************************/

int PalEntriesOnDevice(HDC hDC)
{
int nColors; // number of colors

// Find out the number of colors on this device.

nColors = (1 << (GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES)));

assert(nColors);
return nColors;
}


/*************************************************************************
*
* GetSystemPalette()
*
* Parameters:
*
* None
*
* Return Value:
*
* HPALETTE - handle to a copy of the current system palette
*
* Description:
*
* This function returns a handle to a palette which represents the system
* palette. The system RGB values are copied into our logical palette using
* the GetSystemPaletteEntries function.
*
************************************************************************/

HPALETTE GetSystemPalette(void)
{
HDC hDC; // handle to a DC
static HPALETTE hPal = NULL; // handle to a palette
HANDLE hLogPal; // handle to a logical palette
LPLOGPALETTE lpLogPal; // pointer to a logical palette
int nColors; // number of colors

// Find out how many palette entries we want.

hDC = GetDC(NULL);

if (!hDC)
return NULL;

nColors = PalEntriesOnDevice(hDC); // Number of palette entries

// Allocate room for the palette and lock it.

hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors *
sizeof(PALETTEENTRY));

// if we didn't get a logical palette, return NULL

if (!hLogPal)
return NULL;

// get a pointer to the logical palette

lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal);

// set some important fields

lpLogPal->palVersion = PALVERSION;
lpLogPal->palNumEntries = nColors;

// Copy the current system palette into our logical palette

GetSystemPaletteEntries(hDC, 0, nColors,
(LPPALETTEENTRY)(lpLogPal->palPalEntry));

// Go ahead and create the palette. Once it's created,
// we no longer need the LOGPALETTE, so free it.

hPal = CreatePalette(lpLogPal);

// clean up

GlobalUnlock(hLogPal);
GlobalFree(hLogPal);
ReleaseDC(NULL, hDC);

return hPal;
}


/*************************************************************************
*
* AllocRoomForDIB()
*
* Parameters:
*
* BITMAPINFOHEADER - bitmap info header stucture
*
* HBITMAP - handle to the bitmap
*
* Return Value:
*
* HDIB - handle to memory block
*
* Description:
*
* This routine takes a BITMAPINOHEADER, and returns a handle to global
* memory which can contain a DIB with that header. It also initializes
* the header portion of the global memory. GetDIBits() is used to determine
* the amount of room for the DIB's bits. The total amount of memory
* needed = sizeof(BITMAPINFOHEADER) + size of color table + size of bits.
*
************************************************************************/

HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap)
{
DWORD dwLen;
HANDLE hDIB;
HDC hDC;
LPBITMAPINFOHEADER lpbi;
HANDLE hTemp;

// Figure out the size needed to hold the BITMAPINFO structure
// (which includes the BITMAPINFOHEADER and the color table).

dwLen = bi.biSize + PaletteSize((LPSTR) &bi);
hDIB = GlobalAlloc(GHND,dwLen);

// Check that DIB handle is valid

if (!hDIB)
return NULL;

// Set up the BITMAPINFOHEADER in the newly allocated global memory,
// then call GetDIBits() with lpBits = NULL to have it fill in the
// biSizeImage field for us.

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
*lpbi = bi;

hDC = GetDC(NULL);

GetDIBits(hDC, hBitmap, 0, (UINT) bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS);
ReleaseDC(NULL, hDC);

// If the driver did not fill in the biSizeImage field,
// fill it in -- NOTE: this is a bug in the driver!

if (lpbi->biSizeImage == 0)
lpbi->biSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth *
lpbi->biBitCount) * lpbi->biHeight;

// Get the size of the memory block we need

dwLen = lpbi->biSize + PaletteSize((LPSTR) &bi) + lpbi->biSizeImage;

// Unlock the memory block

GlobalUnlock(hDIB);

// ReAlloc the buffer big enough to hold all the bits

if (hTemp = GlobalReAlloc(hDIB,dwLen,0))
return hTemp;
else
{
// Else free memory block and return failure

GlobalFree(hDIB);
return NULL;
}
}


/*************************************************************************
*
* ChangeDIBFormat()
*
* Parameter:
*
* HDIB - handle to packed-DIB in memory
*
* WORD - desired bits per pixel
*
* DWORD - desired compression format
*
* Return Value:
*
* HDIB - handle to the new DIB if successful, else NULL
*
* Description:
*
* This function will convert the bits per pixel and/or the compression
* format of the specified DIB. Note: If the conversion was unsuccessful,
* we return NULL. The original DIB is left alone. Don't use code like the
* following:
*
* hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4);
*
* The conversion will fail, but hMyDIB will now be NULL and the original
* DIB will now hang around in memory. We could have returned the old
* DIB, but we wanted to allow the programmer to check whether this
* conversion succeeded or failed.
*
************************************************************************/

HDIB ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression)
{
HDC hDC; // Handle to DC
HBITMAP hBitmap; // Handle to bitmap
BITMAP Bitmap; // BITMAP data structure
BITMAPINFOHEADER bi; // Bitmap info header
LPBITMAPINFOHEADER lpbi; // Pointer to bitmap info
HDIB hNewDIB = NULL; // Handle to new DIB
HPALETTE hPal, hOldPal; // Handle to palette, prev pal
WORD DIBBPP, NewBPP; // DIB bits per pixel, new bpp
DWORD DIBComp, NewComp;// DIB compression, new compression

// Check for a valid DIB handle

if (!hDIB)
return NULL;

// Get the old DIB's bits per pixel and compression format

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
DIBBPP = ((LPBITMAPINFOHEADER)lpbi)->biBitCount;
DIBComp = ((LPBITMAPINFOHEADER)lpbi)->biCompression;
GlobalUnlock(hDIB);

// Validate wBitCount and dwCompression
// They must match correctly (i.e., BI_RLE4 and 4 BPP or
// BI_RLE8 and 8BPP, etc.) or we return failure
if (wBitCount == 0)
{
NewBPP = DIBBPP;
if ((dwCompression == BI_RLE4 && NewBPP == 4) ||
(dwCompression == BI_RLE8 && NewBPP == 8) ||
(dwCompression == BI_RGB))
NewComp = dwCompression;
else
return NULL;
}
else if (wBitCount == 1 && dwCompression == BI_RGB)
{
NewBPP = wBitCount;

NewComp = BI_RGB; 
}
else if (wBitCount == 4)
{
NewBPP = wBitCount;
if (dwCompression == BI_RGB || dwCompression == BI_RLE4)
NewComp = dwCompression;
else
return NULL;
}
else if (wBitCount == 8)
{
NewBPP = wBitCount;
if (dwCompression == BI_RGB || dwCompression == BI_RLE8)
NewComp = dwCompression;
else
return NULL;
}
else if (wBitCount == 24 && dwCompression == BI_RGB)
{
NewBPP = wBitCount;
NewComp = BI_RGB;
}
else
return NULL;

// Save the old DIB's palette

hPal = CreateDIBPalette(hDIB);
if (!hPal)
return NULL;

// Convert old DIB to a bitmap

hBitmap = DIBToBitmap(hDIB, hPal);
if (!hBitmap)
{
DeleteObject(hPal);
return NULL;
}

// Get info about the bitmap
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);

// Fill in the BITMAPINFOHEADER appropriately

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = NewBPP;
bi.biCompression = NewComp;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

// Go allocate room for the new DIB

hNewDIB = AllocRoomForDIB(bi, hBitmap);
if (!hNewDIB)
return NULL;

// Get a pointer to the new DIB

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB);

// Get a DC and select/realize our palette in it

hDC = GetDC(NULL);
hOldPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);

// Call GetDIBits and get the new DIB bits

if (!GetDIBits(hDC, hBitmap, 0, (UINT) lpbi->biHeight,
(LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi),
(LPBITMAPINFO)lpbi, DIB_RGB_COLORS))
{
GlobalUnlock(hNewDIB);
GlobalFree(hNewDIB);
hNewDIB = NULL;
}

// Clean up and return

SelectPalette(hDC, hOldPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);

// Unlock the new DIB's memory block
if (hNewDIB)
GlobalUnlock(hNewDIB);

DeleteObject(hBitmap);
DeleteObject(hPal);

return hNewDIB;
}


/*************************************************************************
*
* ChangeBitmapFormat()
*
* Parameter:
*
* HBITMAP - handle to a bitmap
*
* WORD - desired bits per pixel
*
* DWORD - desired compression format
*
* HPALETTE - handle to palette
*
* Return Value:
*
* HDIB - handle to the new DIB if successful, else NULL
*
* Description:
*
* This function will convert a bitmap to the specified bits per pixel
* and compression format. The bitmap and it's palette will remain
* after calling this function.
*
************************************************************************/

HDIB ChangeBitmapFormat(HBITMAP hBitmap, WORD wBitCount, DWORD dwCompression,
HPALETTE hPal)
{
HDC hDC; // Screen DC
HDIB hNewDIB=NULL; // Handle to new DIB
BITMAP Bitmap; // BITMAP data structure
BITMAPINFOHEADER bi; // Bitmap info. header
LPBITMAPINFOHEADER lpbi; // Pointer to bitmap header
HPALETTE hOldPal=NULL; // Handle to palette
WORD NewBPP; // New bits per pixel
DWORD NewComp; // New compression format

// Check for a valid bitmap handle

if (!hBitmap)
return NULL;

// Validate wBitCount and dwCompression
// They must match correctly (i.e., BI_RLE4 and 4 BPP or
// BI_RLE8 and 8BPP, etc.) or we return failure

if (wBitCount == 0)
{
NewComp = dwCompression;
if (NewComp == BI_RLE4)
NewBPP = 4;
else if (NewComp == BI_RLE8)
NewBPP = 8;
else // Not enough info */
return NULL;
}
else if (wBitCount == 1 && dwCompression == BI_RGB)
{
NewBPP = wBitCount;
NewComp = BI_RGB;
}
else if (wBitCount == 4)
{
NewBPP = wBitCount;
if (dwCompression == BI_RGB || dwCompression == BI_RLE4)
NewComp = dwCompression;
else
return NULL;
}
else if (wBitCount == 8)
{
NewBPP = wBitCount;
if (dwCompression == BI_RGB || dwCompression == BI_RLE8)
NewComp = dwCompression;
else
return NULL;
}
else if (wBitCount == 24 && dwCompression == BI_RGB)
{
NewBPP = wBitCount;
NewComp = BI_RGB;
}
else
return NULL;

// Get info about the bitmap

GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);

// Fill in the BITMAPINFOHEADER appropriately

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = NewBPP;
bi.biCompression = NewComp;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

// Go allocate room for the new DIB

hNewDIB = AllocRoomForDIB(bi, hBitmap);
if (!hNewDIB)
return NULL;

// Get a pointer to the new DIB

lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB);

// If we have a palette, get a DC and select/realize it

if (hPal)
{
hDC = GetDC(NULL);
hOldPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);
}

// Call GetDIBits and get the new DIB bits

if (!GetDIBits(hDC, hBitmap, 0, (UINT) lpbi->biHeight, (LPSTR)lpbi +
(WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS))
{
GlobalUnlock(hNewDIB);
GlobalFree(hNewDIB);
hNewDIB = NULL;
}

// Clean up and return

if (hOldPal)
{
SelectPalette(hDC, hOldPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
}

// Unlock the new DIB's memory block

if (hNewDIB)
GlobalUnlock(hNewDIB);

return hNewDIB;
}