windows-nt/Source/XPSP1/NT/net/rras/cm/common/source/bmpimage.cpp
2020-09-26 16:20:57 +08:00

791 lines
22 KiB
C++

//+----------------------------------------------------------------------------
//
// File: image.cpp
//
// Module: CMDIAL and CMAK
//
// Synopsis: CMDIAL/CMAK specific imaging support routines
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// Author: nickball Created Header 03/30/98
// quintinb moved to common\source 08/06/98
//
//+----------------------------------------------------------------------------
//+---------------------------------------------------------------------------
//
// Function: CmGetBitmapInfo
//
// Synopsis: Helper function to retrieve the contents of a bitmap from an HBITMAP
//
// Arguments: hbm - Hanhdle of the target bitmap
//
// Returns: A pointer to a LPBITMAPINFO that contains the INFOHEADER,
// ColorTable and bits for the bitmap.
//
// Note: When accessing this value, or passing it on to other BITMAP APIs
// it is recommended that the value be cast as an (LPBYTE).
//
// History: a-nichb - Cleaned-up and commented - 3/21/97
//
//----------------------------------------------------------------------------
LPBITMAPINFO CmGetBitmapInfo(HBITMAP hbm)
{
LPBITMAPINFO pbmi = NULL;
HDC hDC = NULL;
int nNumColors = 0;
int iRes;
LPBITMAPINFO lpbmih = NULL;
DWORD dwInfoSize = 0;
WORD wbiBits = 0;
if (!hbm)
{
return NULL;
}
// Get the basic bmp object info
BITMAP BitMap;
if (!GetObjectA(hbm, sizeof(BITMAP), &BitMap))
{
goto Cleanup;
}
// Calc the color bits and num colors
wbiBits = BitMap.bmPlanes * BitMap.bmBitsPixel;
if (wbiBits <= 8)
{
nNumColors = 1 << wbiBits;
}
// Allocate a BITMAPINFO structure large enough to hold header + color palette
dwInfoSize = sizeof(BITMAPINFOHEADER) + (nNumColors * sizeof(RGBQUAD));
lpbmih = (LPBITMAPINFO) CmMalloc(dwInfoSize);
if (!lpbmih)
{
goto Cleanup;
}
// Pre-fill the info that we have about the bmp
lpbmih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpbmih->bmiHeader.biWidth = BitMap.bmWidth;
lpbmih->bmiHeader.biHeight = BitMap.bmHeight;
lpbmih->bmiHeader.biPlanes = 1;
lpbmih->bmiHeader.biBitCount = wbiBits;
// Call GetDiBits() w/ 5th Param to NULL, this is treated by the system as
// a query in which case it validates the lpbmih contents and fills in the
// biSizeImage member of the structure
hDC = GetDC(NULL);
if (!hDC)
{
goto Cleanup;
}
iRes = GetDIBits(hDC,hbm,0,BitMap.bmHeight,NULL,(LPBITMAPINFO) lpbmih,DIB_RGB_COLORS);
#ifdef DEBUG
if (!iRes)
{
CMTRACE(TEXT("CmGetBitmapInfo() GetDIBits() failed."));
}
#endif
if (iRes)
{
DWORD dwFullSize = dwInfoSize;
// Create a complete DIB structure with room for bits and fill it
if (lpbmih->bmiHeader.biSizeImage)
{
dwFullSize += lpbmih->bmiHeader.biSizeImage;
}
else
{
dwFullSize += (((WORD) (lpbmih->bmiHeader.biWidth * lpbmih->bmiHeader.biBitCount) / 8) * (WORD) BitMap.bmHeight);
}
pbmi = (LPBITMAPINFO) CmMalloc(dwFullSize + sizeof(DWORD));
#ifdef DEBUG
*((DWORD *) (((PBYTE) pbmi)+dwFullSize)) = 0x12345678;
*((DWORD *) (((PBYTE) pbmi)+dwFullSize-sizeof(DWORD))) = 0x23456789;
#endif
if (pbmi)
{
// Load the new larger LPBITMAPINFO struct with existing info,
// and get the data bits. Release the existing LPBITMAPINFO.
CopyMemory(pbmi, lpbmih, dwInfoSize);
//
// We have a handle, we want the exact bits.
//
iRes = GetDIBits(hDC,
hbm,
0,
BitMap.bmHeight,
((LPBYTE) pbmi) + dwInfoSize,
pbmi,
DIB_RGB_COLORS);
#ifdef DEBUG
if (*((DWORD *) (((PBYTE) pbmi) + dwFullSize)) != 0x12345678)
{
CMTRACE(TEXT("CmGetBitmapInfo() GetDIBits() copied too much."));
}
if (*((DWORD *) (((PBYTE) pbmi) + dwFullSize - sizeof(DWORD))) == 0x23456789)
{
CMTRACE(TEXT("CmGetBitmapInfo() GetDIBits() didn't copy enough."));
}
#endif
// If GetDiBits() failed, free the BITMAPINFO buffer
if (!iRes)
{
CmFree(pbmi);
pbmi = NULL;
}
}
}
// Cleanup
Cleanup:
if (lpbmih)
{
CmFree(lpbmih);
}
if (hDC)
{
ReleaseDC(NULL, hDC);
}
return pbmi;
}
static HPALETTE CmCreateDIBPalette(LPBITMAPINFO pbmi)
{
WORD wNumColors = 0;
HPALETTE hRes = NULL;
if (!pbmi)
{
return (NULL);
}
// Get num colors according to color depth
// Note: 24-bit bitmaps have no color table
if (pbmi->bmiHeader.biBitCount <= 8)
{
wNumColors = 1 << pbmi->bmiHeader.biBitCount;
}
// Fill logical palette based upon color table
if (wNumColors)
{
LPLOGPALETTE pLogPal;
int idx;
pLogPal = (LPLOGPALETTE) CmMalloc(sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*wNumColors);
if (pLogPal)
{
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = wNumColors;
for (idx=0;idx<wNumColors;idx++)
{
pLogPal->palPalEntry[idx].peRed = pbmi->bmiColors[idx].rgbRed;
pLogPal->palPalEntry[idx].peGreen = pbmi->bmiColors[idx].rgbGreen;
pLogPal->palPalEntry[idx].peBlue = pbmi->bmiColors[idx].rgbBlue;
pLogPal->palPalEntry[idx].peFlags = 0;
}
// Create a new palette
hRes = CreatePalette(pLogPal);
#ifdef DEBUG
if (!hRes)
{
CMTRACE1(TEXT("CmCreateDIBPalette() CreatePalette() failed, GLE=%u."), GetLastError());
}
#endif
CmFree(pLogPal);
}
}
return hRes;
}
HBITMAP CmLoadBitmap(HINSTANCE hInst, LPCTSTR pszSpec)
{
return ((HBITMAP) CmLoadImage(hInst, pszSpec, IMAGE_BITMAP, 0, 0));
}
//+----------------------------------------------------------------------------
//
// Function: ReleaseBitmapData
//
// Synopsis: Releases resources and memory acquired during CreateBitmapData. Note
// that if you are using this with the BmpWndProc function below, that you
// should call an STM_SETIMAGE with a NULL image pointer param in order to
// clear out the window procedures window long. Otherwise, it could get
// a WM_PAINT message and try to use the freed memory before you can
// clear it out or have the window destroyed by the dialog manager.
//
// Arguments: LPBMPDATA pBmpData - Ptr to the BmpData to be released
//
// Returns: Nothing
//
// History: nickball Created 3/27/98
//
//+----------------------------------------------------------------------------
void ReleaseBitmapData(LPBMPDATA pBmpData)
{
MYDBGASSERT(pBmpData);
if (NULL == pBmpData)
{
return;
}
if (pBmpData->hDIBitmap)
{
DeleteObject(pBmpData->hDIBitmap);
pBmpData->hDIBitmap = NULL;
}
if (pBmpData->hDDBitmap)
{
DeleteObject(pBmpData->hDDBitmap);
pBmpData->hDDBitmap = NULL;
}
if (pBmpData->pBmi)
{
CmFree(pBmpData->pBmi);
pBmpData->pBmi = NULL;
}
}
//+----------------------------------------------------------------------------
//
// Function: CreateBitmapData
//
// Synopsis: Fills a BMPDATA struct with all data necessary to display a bitmap.
//
// Arguments: HBITMAP hBmp - Handle of the source bitmap
// LPBMPDATA lpBmpData - Ptr to the BmpData struct to be filled
// HWND hwnd - The hwnd that the bitmap will be displayed in.
// BOOL fCustomPalette - Indicates that the DDB should be created with a palette specific to the bitmap.
//
// Returns: BOOL - TRUE on succes
//
// History: nickball Created 3/27/98
//
//+----------------------------------------------------------------------------
BOOL CreateBitmapData(HBITMAP hDIBmp,
LPBMPDATA lpBmpData,
HWND hwnd,
BOOL fCustomPalette)
{
MYDBGASSERT(hDIBmp);
MYDBGASSERT(lpBmpData);
MYDBGASSERT(lpBmpData->phMasterPalette);
if (NULL == hDIBmp || NULL == lpBmpData)
{
return NULL;
}
//
// Params look good, get busy
//
HPALETTE hPaletteNew = NULL;
LPBITMAPINFO pBmi = NULL;
HBITMAP hDDBmp = NULL;
HDC hDC;
int iRes = 0;
//
// If we already have a pBmi value, we will assume it is up to date, as
// both it and the DIB do not change throughout the life of the BMP.
// Note: If BmpData is not zero initialized, you will have problems.
//
if (lpBmpData->pBmi)
{
pBmi = lpBmpData->pBmi;
}
else
{
//
// Use the bitmap handle to retrieve a BITMAPINFO ptr complete w/ data
//
pBmi = CmGetBitmapInfo(lpBmpData->hDIBitmap);
if (NULL == pBmi)
{
return FALSE;
}
}
//
// we need a DC
//
hDC = GetDC(hwnd);
if (!hDC)
{
CMTRACE(TEXT("MyCreateDDBitmap() GetDC() failed."));
return FALSE;
}
//
// If CM is localized so that it is RTL (Right to Left => arabic and Hebrew),
// then we need to call SetLayout on the hDC from above. If we don't
// set the layout back to LTR, the bitmap will show up as all black instead of as
// an image.
//
HMODULE hLib = LoadLibrary(TEXT("gdi32.dll"));
if (hLib)
{
#ifndef LAYOUT_RTL
#define LAYOUT_RTL 0x00000001 // Right to left
#endif
DWORD dwLayout;
typedef DWORD (WINAPI* pfnSetLayoutType)(HDC, DWORD);
typedef DWORD (WINAPI* pfnGetLayoutType)(HDC);
pfnSetLayoutType pfnSetLayout = (pfnSetLayoutType)GetProcAddress(hLib, "SetLayout");
pfnGetLayoutType pfnGetLayout = (pfnGetLayoutType)GetProcAddress(hLib, "GetLayout");
if (pfnSetLayout && pfnGetLayout)
{
DWORD dwLayout = pfnGetLayout(hDC);
if (LAYOUT_RTL & dwLayout)
{
dwLayout ^= LAYOUT_RTL; // toggle LAYOUT_RTL off
pfnSetLayout(hDC, dwLayout);
CMTRACE(TEXT("CreateBitmapData -- Toggling off LAYOUT_RTL on the device context"));
}
}
FreeLibrary(hLib);
}
//
// If fCustomPalette is set then create a palette based on our bits
// and realize it in the current DC.
//
if (fCustomPalette)
{
hPaletteNew = CmCreateDIBPalette(pBmi);
if (hPaletteNew)
{
//
// Select and realize the new palette so that the DDB is created with it below
//
HPALETTE hPalettePrev = SelectPalette(hDC,
hPaletteNew, lpBmpData->bForceBackground); // FALSE == Foreground app behavior);
// TRUE == Background app behavior);
if (hPalettePrev)
{
iRes = RealizePalette(hDC);
#ifdef DEBUG
if (GDI_ERROR == iRes)
{
CMTRACE1(TEXT("MyCreateDDBitmap() RealizePalette() failed, GLE=%u."), GetLastError());
}
}
else
{
CMTRACE1(TEXT("MyCreateDDBitmap() SelectPalette() failed, GLE=%u."), GetLastError());
#endif
}
}
}
//
// Determine number of color entries based upon color depth
//
int nNumColors = 0;
if (pBmi->bmiHeader.biBitCount <= 8)
{
nNumColors = (1 << pBmi->bmiHeader.biBitCount);
}
//
// Create the DDB from the bits
//
hDDBmp = CreateDIBitmap(hDC,
&pBmi->bmiHeader,
CBM_INIT,
((LPBYTE) pBmi) + sizeof(BITMAPINFOHEADER) + (nNumColors * sizeof(RGBQUAD)), //dib.dsBm.bmBits,
pBmi,
DIB_RGB_COLORS);
#ifdef DEBUG
if (!hDDBmp)
{
CMTRACE(TEXT("MyCreateDDBitmap() CreateDIBitmap() failed."));
}
#endif
ReleaseDC(NULL, hDC);
//
// Fill in the bitmap data
//
if (hDDBmp)
{
lpBmpData->hDIBitmap = hDIBmp;
lpBmpData->pBmi = pBmi;
//
// Delete existing DDB, if any
//
if (lpBmpData->hDDBitmap)
{
DeleteObject(lpBmpData->hDDBitmap);
}
lpBmpData->hDDBitmap = hDDBmp;
if (hPaletteNew)
{
//
// Delete existing Palette, if any
//
if (*lpBmpData->phMasterPalette)
{
DeleteObject(*lpBmpData->phMasterPalette);
}
*lpBmpData->phMasterPalette = hPaletteNew;
}
return TRUE;
}
//
// Something went wrong, cleanup
//
CmFree(pBmi);
return FALSE;
}
//
// Bitmap window procedure
//
LRESULT CALLBACK BmpWndProc(HWND hwndBmp,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
LPBMPDATA pBmpData = (LPBMPDATA) GetWindowLongU(hwndBmp,0);
BOOL bRes;
switch (uMsg)
{
case WM_CREATE:
{
return FALSE;
}
case WM_DESTROY:
SetWindowLongU(hwndBmp,sizeof(LPBMPDATA),(LONG_PTR) NULL);
break;
case WM_PAINT:
if (pBmpData && pBmpData->pBmi)
{
LPBITMAPINFO pBmi = pBmpData->pBmi;
RECT rWnd;
RECT rSrc = {0,0,(int)pBmpData->pBmi->bmiHeader.biWidth,
(int)pBmpData->pBmi->bmiHeader.biHeight};
PAINTSTRUCT ps;
HDC hdcBmp;
HBITMAP hbmpPrev;
int iPrevStretchMode;
//
// Start painting
//
HDC hdc = BeginPaint(hwndBmp,&ps);
if (hdc)
{
//
// Select and realize our current palette in the current DC
//
//UnrealizeObject(*pBmpData->phMasterPalette);
SelectPalette(hdc, *pBmpData->phMasterPalette, pBmpData->bForceBackground);
RealizePalette(hdc);
//
// Create a compatible DC, we'll create the BMP here then BLT it to the real DC
//
hdcBmp = CreateCompatibleDC(hdc);
if (hdcBmp)
{
//
// Select and realize our current palette in the compatible DC
//
SelectPalette(hdcBmp, *pBmpData->phMasterPalette, pBmpData->bForceBackground);
RealizePalette(hdcBmp);
if (!hdcBmp)
{
CMTRACE(TEXT("BmpWndProc() CreateCompatibleDC() failed."));
}
if (!pBmpData->hDDBitmap)
{
CMTRACE(TEXT("BmpWndProc() - WM_PAINT - hDDBitmap is NULL."));
}
//
// Select the bitmap into the compatible DC
//
hbmpPrev = (HBITMAP) SelectObject(hdcBmp,pBmpData->hDDBitmap);
bRes = GetWindowRect(hwndBmp,&rWnd);
if (!bRes)
{
CMTRACE1(TEXT("BmpWndProc() GetWindowRect() failed, GLE=%u."), GetLastError());
}
//
// Now set the mode, and StretchBlt the bitmap from the compatible DC to the active DC
//
CMTRACE(TEXT("BmpWndProc() : Changing stretch mode"));
iPrevStretchMode = SetStretchBltMode(hdc, STRETCH_DELETESCANS);
bRes = StretchBlt(hdc,
rWnd.left-rWnd.left,
rWnd.top-rWnd.top,
rWnd.right-rWnd.left,
rWnd.bottom-rWnd.top,
hdcBmp,
rSrc.left-rSrc.left,
rSrc.top-rSrc.top,
rSrc.right-rSrc.left,
rSrc.bottom-rSrc.top,
SRCCOPY);
if (!bRes)
{
CMTRACE1(TEXT("BmpWndProc() StretchBlt() failed, GLE=%u."), GetLastError());
}
//
// Restore the mode in the active DC
//
CMTRACE(TEXT("BmpWndProc() Restoring stretch mode"));
iPrevStretchMode = SetStretchBltMode(hdc, iPrevStretchMode);
//
// Restore the compatible DC and release it
//
SelectObject(hdcBmp,hbmpPrev);
DeleteDC(hdcBmp);
}
else
{
CMTRACE1(TEXT("BmpWndProc() CreateCompatibleDC() failed, GLE=%u."), GetLastError());
}
bRes = EndPaint(hwndBmp,&ps);
if (!bRes)
{
CMTRACE(TEXT("BmpWndProc() EndPaint() failed."));
}
}
else
{
CMTRACE1(TEXT("BmpWndProc() BeginPaint() failed, GLE=%u."), GetLastError());
}
}
break;
case STM_SETIMAGE:
if (wParam == IMAGE_BITMAP)
{
CMTRACE2(TEXT("STM_SETIMAGE: wParam=%u, lParam=%u"), wParam, lParam);
//
// lParam contains a handle to the bitmap data, store it in extra bytes
//
SetWindowLongU(hwndBmp,0, lParam); // pBmpData
CMTRACE2(TEXT("SetWindowLongU called with hwndBmp = %u, lParam=%u"), hwndBmp, lParam);
//
// Force a repaint
//
bRes = InvalidateRect(hwndBmp,NULL,TRUE);
CMTRACE2(TEXT("InvalidateRect called with hwndBmp = %u, lParam=%u"), hwndBmp, lParam);
#ifdef DEBUG
if (!bRes)
{
CMTRACE(TEXT("BmpWndProc() InvalidateRect() failed."));
}
#endif
if (pBmpData && pBmpData->hDDBitmap)
{
return ((LRESULT) pBmpData->hDDBitmap);
}
else
{
return NULL;
}
}
break;
}
return (DefWindowProcU(hwndBmp,uMsg,wParam,lParam));
}
//+---------------------------------------------------------------------------
//
// Function: QueryNewPalette
//
// Synopsis: Helper function to encapsulate handling of WM_QUERYNEWPALETTE
//
// Arguments: hwndDlg - Handle of the dialog receiving the message
// lpBmpData - Struct containing handles for bmp to display
// iBmpCtrl - Bitmap control ID
//
// Returns: Nothing
//
// History: a-nichb - Created - 7/14/97
//
//----------------------------------------------------------------------------
void QueryNewPalette(LPBMPDATA lpBmpData, HWND hwndDlg, int iBmpCtrl)
{
MYDBGASSERT(lpBmpData);
if (lpBmpData)
{
//
// We just handle this as a standard palette change because we
// want to ensure that we create a new DDB using a palette based
// upon our bitmap.
//
PaletteChanged(lpBmpData, hwndDlg, iBmpCtrl);
}
}
//+---------------------------------------------------------------------------
//
// Function: PaletteChanged
//
// Synopsis: Helper function to encapsulate handling of WM_PALETTECHANGED
//
// Arguments: hwndDlg - Handle of the dialog receiving the message
// lpBmpData - Struct containing handles for bmp to display
// iBmpCtrl - Bitmap control ID
//
// Returns: Nothing
//
// History: a-nichb - Created - 7/14/97
//
//----------------------------------------------------------------------------
void PaletteChanged(LPBMPDATA lpBmpData, HWND hwndDlg, int iBmpCtrl)
{
MYDBGASSERT(lpBmpData);
if (NULL == lpBmpData || NULL == lpBmpData->phMasterPalette)
{
return;
}
//
// Unrealize the master palette if it exists
//
if (*lpBmpData->phMasterPalette)
{
UnrealizeObject(*lpBmpData->phMasterPalette);
}
//
// Create a device dependent bitmap and appropriate palette
//
if (CreateBitmapData(lpBmpData->hDIBitmap, lpBmpData, hwndDlg, TRUE))
{
//
// SetImage to update handles for painting and force draw
//
HBITMAP hbmpTmp = (HBITMAP) SendDlgItemMessageA(hwndDlg, iBmpCtrl, STM_SETIMAGE,
IMAGE_BITMAP,(LPARAM) lpBmpData);
#ifdef DEBUUG
if (!hbmpTmp)
{
CMTRACE(TEXT("PaletteChanged().WM_PALETTECHANGED - STM_SETIMAGE returned NULL."));
}
#endif
}
}