WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/compatibledib.cpp
2020-09-30 16:53:49 +02:00

470 lines
15 KiB
C++

/**************************************************************************\
*
* Copyright (c) 2000 Microsoft Corporation
*
* Module Name:
*
* Compatible DIBSections
*
* Abstract:
*
* Create a DIB section with an optimal format w.r.t. the specified hdc.
* If the hdc format is <8bpp, returns an 8bpp DIBSection.
* If the hdc format is not recognized, returns a 32bpp DIBSection.
*
* Notes:
*
* History:
*
* 01/23/1996 gilmanw
* Created it.
* 01/21/2000 agodfrey
* Added it to GDI+ (from Gilman's 'fastdib.c'), and morphed it into
* 'CreateSemiCompatibleDIB'.
* 08/10/2000 agodfrey
* Hacked it further so that if we don't understand the format, we make
* a 32bpp section. Bug #96879.
*
\**************************************************************************/
#include "precomp.hpp"
#include "compatibleDIB.hpp"
const DWORD AlphaMaskFromPixelFormatIndex[PIXFMT_MAX] = {
0x00000000, // PixelFormatUndefined
0x00000000, // PixelFormat1bppIndexed
0x00000000, // PixelFormat4bppIndexed
0x00000000, // PixelFormat8bppIndexed
0x00000000, // PixelFormat16bppGrayScale
0x00000000, // PixelFormat16bppRGB555
0x00000000, // PixelFormat16bppRGB565
0x00008000, // PixelFormat16bppARGB1555
0x00000000, // PixelFormat24bppRGB
0x00000000, // PixelFormat32bppRGB
0xff000000, // PixelFormat32bppARGB
0xff000000, // PixelFormat32bppPARGB
0x00000000, // PixelFormat48bppRGB
0x00000000, // PixelFormat64bppARGB
0x00000000, // PixelFormat64bppPARGB
0x00000000 // PixelFormat24bppBGR
};
const DWORD RedMaskFromPixelFormatIndex[PIXFMT_MAX] = {
0x00000000, // PixelFormatUndefined
0x00000000, // PixelFormat1bppIndexed
0x00000000, // PixelFormat4bppIndexed
0x00000000, // PixelFormat8bppIndexed
0x00000000, // PixelFormat16bppGrayScale
0x00007c00, // PixelFormat16bppRGB555
0x0000f800, // PixelFormat16bppRGB565
0x00007c00, // PixelFormat16bppARGB1555
0x00ff0000, // PixelFormat24bppRGB
0x00ff0000, // PixelFormat32bppRGB
0x00ff0000, // PixelFormat32bppARGB
0x00ff0000, // PixelFormat32bppPARGB
0x00000000, // PixelFormat48bppRGB
0x00000000, // PixelFormat64bppARGB
0x00000000, // PixelFormat64bppPARGB
0x000000ff // PixelFormat24bppBGR
};
const DWORD GreenMaskFromPixelFormatIndex[PIXFMT_MAX] = {
0x00000000, // PixelFormatUndefined
0x00000000, // PixelFormat1bppIndexed
0x00000000, // PixelFormat4bppIndexed
0x00000000, // PixelFormat8bppIndexed
0x00000000, // PixelFormat16bppGrayScale
0x000003e0, // PixelFormat16bppRGB555
0x000007e0, // PixelFormat16bppRGB565
0x000003e0, // PixelFormat16bppARGB1555
0x0000ff00, // PixelFormat24bppRGB
0x0000ff00, // PixelFormat32bppRGB
0x0000ff00, // PixelFormat32bppARGB
0x0000ff00, // PixelFormat32bppPARGB
0x00000000, // PixelFormat48bppRGB
0x00000000, // PixelFormat64bppARGB
0x00000000, // PixelFormat64bppPARGB
0x0000ff00 // PixelFormat24bppBGR
};
const DWORD BlueMaskFromPixelFormatIndex[PIXFMT_MAX] = {
0x00000000, // PixelFormatUndefined
0x00000000, // PixelFormat1bppIndexed
0x00000000, // PixelFormat4bppIndexed
0x00000000, // PixelFormat8bppIndexed
0x00000000, // PixelFormat16bppGrayScale
0x0000001f, // PixelFormat16bppRGB555
0x0000001f, // PixelFormat16bppRGB565
0x0000001f, // PixelFormat16bppARGB1555
0x000000ff, // PixelFormat24bppRGB
0x000000ff, // PixelFormat32bppRGB
0x000000ff, // PixelFormat32bppARGB
0x000000ff, // PixelFormat32bppPARGB
0x00000000, // PixelFormat48bppRGB
0x00000000, // PixelFormat64bppARGB
0x00000000, // PixelFormat64bppPARGB
0x00ff0000 // PixelFormat24bppBGR
};
/**************************************************************************\
* CreatePBMIFromPixelFormat
*
* Fills in the fields of a BITMAPINFO so that we can create a bitmap
* that matches the format of the display.
*
* This is done by analyzing the pixelFormat
*
* Arguments:
* OUT pbmi : this must point to a valid BITMAPINFO structure
* which has enough space for the palette (RGBQUAD array)
* and MUST be zero initialized.
* palette : Input palette that will be copied into the BITMAPINFO
* if it's a palettized mode.
* pixelFormat : Input pixel format.
*
*
* History:
* 06/07/1995 gilmanw
* Created it.
* 01/21/2000 agodfrey
* Munged it for GDI+'s needs.
* 08/11/2000 asecchia
* Extracted the pixel format detection into a separate routine.
* It now analyzes the pixelformat for all its data.
*
\**************************************************************************/
static VOID
CreatePBMIFromPixelFormat(
OUT BITMAPINFO *pbmi,
IN ColorPalette *palette,
IN PixelFormatID pixelFormat
)
{
// NOTE: Contents of pbmi should be zero initialized by the caller.
ASSERT(pbmi != NULL);
if(pixelFormat == PixelFormatUndefined) { return; }
// GDI can't handle the following formats:
ASSERT(
pixelFormat != PixelFormatUndefined &&
pixelFormat != PixelFormat16bppGrayScale &&
pixelFormat != PixelFormat16bppARGB1555 &&
pixelFormat != PixelFormat48bppRGB &&
pixelFormat != PixelFormat64bppARGB &&
pixelFormat != PixelFormat64bppPARGB
);
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = 0;
pbmi->bmiHeader.biHeight = 0;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = (WORD)GetPixelFormatSize(pixelFormat);
pbmi->bmiHeader.biCompression = BI_RGB;
if (IsIndexedPixelFormat(pixelFormat))
{
// Fill the color table
// If there's no palette, assume the caller is going to
// set it up.
if(palette)
{
RGBQUAD *rgb = pbmi->bmiColors;
UINT i;
for (i=0; i<palette->Count; i++, rgb++)
{
GpColor color(palette->Entries[i]);
rgb->rgbRed = color.GetRed();
rgb->rgbGreen = color.GetGreen();
rgb->rgbBlue = color.GetBlue();
}
}
}
else
{
INT pfSize = GetPixelFormatSize(pixelFormat);
if( (pfSize==16) || (pfSize==32) )
{
// BI_BITFIELDS is only valid on 16- and 32-bpp formats.
pbmi->bmiHeader.biCompression = BI_BITFIELDS;
}
// Get the masks from the 16bpp, 24bpp and 32bpp formats.
DWORD* masks = reinterpret_cast<DWORD*>(&pbmi->bmiColors[0]);
INT formatIndex = GetPixelFormatIndex(pixelFormat);
masks[0] = RedMaskFromPixelFormatIndex[formatIndex];
masks[1] = GreenMaskFromPixelFormatIndex[formatIndex];
masks[2] = BlueMaskFromPixelFormatIndex[formatIndex];
}
}
/**************************************************************************\
* CreateSemiCompatibleDIB
*
* Create a DIB section with an optimal format w.r.t. the specified hdc.
*
* If DC format <= 8bpp, creates an 8bpp section using the specified palette.
* If the palette handle is NULL, then the system palette is used.
*
* Otherwise, if the DC format is not natively supported, creates a 32bpp
* section.
*
* Note: The hdc must be a direct DC (not an info or memory DC).
*
* Arguments:
*
* hdc - The reference hdc
* ulWidth - The width of the desired DIBSection
* ulHeight - The height of the desired DIBSection
* palette - The palette for <=8bpp modes
* [OUT] ppvBits - A pointer to the DIBSection's bits
* [OUT] pixelFormat - The pixel format of the returned DIBSection
*
* Returns:
* Valid bitmap handle if successful, NULL if error.
*
* History:
* 01/23/1996 gilmanw
* Created it.
* 01/21/2000 agodfrey
* Munged it for GDI+'s needs.
* 08/11/2000 asecchia
* Call more general pixel format determination code.
\**************************************************************************/
HBITMAP
CreateSemiCompatibleDIB(
HDC hdc,
ULONG ulWidth,
ULONG ulHeight,
ColorPalette *palette,
PVOID *ppvBits,
PixelFormatID *pixelFormat
)
{
HBITMAP hbmRet = (HBITMAP) NULL;
BYTE aj[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255)];
BITMAPINFO *pbmi = (BITMAPINFO *) aj;
ASSERT(GetDCType(hdc) == OBJ_DC);
ASSERT(pixelFormat && ppvBits);
// Zero initialize the pbmi. This is a requirement for
// CreatePBMIFromPixelFormat()
GpMemset(aj, 0, sizeof(aj));
*pixelFormat = ExtractPixelFormatFromHDC(hdc);
if(IsIndexedPixelFormat(*pixelFormat))
{
// For indexed modes, we only support 8bpp. Lower bit-depths
// are supported via 8bpp mode, if at all.
*pixelFormat = PixelFormat8bppIndexed;
}
// Not all printer HDC's have queriable palettes, the GpDevice()
// constructor doesn't support it. Fake 32bpp in this case.
// Also, if the format is undefined, use 32bpp, and the caller will use
// GDI to do the conversion.
if ( ( (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER)
&& (IsIndexedPixelFormat(*pixelFormat))
)
|| (*pixelFormat == PixelFormatUndefined)
)
{
*pixelFormat = PixelFormat32bppRGB;
}
CreatePBMIFromPixelFormat(pbmi, palette, *pixelFormat);
// Change bitmap size to match specified dimensions.
pbmi->bmiHeader.biWidth = ulWidth;
pbmi->bmiHeader.biHeight = ulHeight;
if (pbmi->bmiHeader.biCompression == BI_RGB)
{
pbmi->bmiHeader.biSizeImage = 0;
}
else
{
if ( pbmi->bmiHeader.biBitCount == 16 )
pbmi->bmiHeader.biSizeImage = ulWidth * ulHeight * 2;
else if ( pbmi->bmiHeader.biBitCount == 32 )
pbmi->bmiHeader.biSizeImage = ulWidth * ulHeight * 4;
else
pbmi->bmiHeader.biSizeImage = 0;
}
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
// Create the DIB section. Let Win32 allocate the memory and return
// a pointer to the bitmap surface.
hbmRet = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
if ( !hbmRet )
{
ONCE(WARNING(("CreateSemiCompatibleDIB: CreateDIBSection failed")));
}
return hbmRet;
}
/**************************************************************************\
* ExtractPixelFormatFromHDC
*
* Returns:
* PixelFormatID if successful, PixelFormatUndefined if not.
*
* History:
* 08/11/2000 asecchia
* Created it.
\**************************************************************************/
PixelFormatID
ExtractPixelFormatFromHDC(
HDC hdc
)
{
HBITMAP hbm;
BOOL bRet = FALSE;
PixelFormatID pixelFormat = PixelFormatUndefined;
BYTE bmi_buf[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255)];
BITMAPINFO *pbmi = (BITMAPINFO *) bmi_buf;
GpMemset(bmi_buf, 0, sizeof(bmi_buf));
// Create a dummy bitmap from which we can query color format info
// about the device surface.
if ( (hbm = CreateCompatibleBitmap(hdc, 1, 1)) != NULL )
{
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Call first time to fill in BITMAPINFO header.
GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
// First handle the 'simple' case of indexed formats.
if ( pbmi->bmiHeader.biBitCount <= 8 )
{
switch(pbmi->bmiHeader.biBitCount)
{
case 1: pixelFormat = PixelFormat1bppIndexed; break;
case 4: pixelFormat = PixelFormat4bppIndexed; break;
case 8: pixelFormat = PixelFormat8bppIndexed; break;
// Fallthrough on default - the pixelFormat is already
// initialized to PixelFormatUndefined.
default:
WARNING((
"BitDepth %d from GetDIBits is not supported.",
pbmi->bmiHeader.biBitCount
));
}
}
else
{
DWORD redMask = 0;
DWORD greenMask = 0;
DWORD blueMask = 0;
if ( pbmi->bmiHeader.biCompression == BI_BITFIELDS )
{
// Call a second time to get the color masks.
// It's a GetDIBits Win32 "feature".
GetDIBits(
hdc,
hbm,
0,
pbmi->bmiHeader.biHeight,
NULL,
pbmi,
DIB_RGB_COLORS
);
DWORD* masks = reinterpret_cast<DWORD*>(&pbmi->bmiColors[0]);
redMask = masks[0];
greenMask = masks[1];
blueMask = masks[2];
}
else if (pbmi->bmiHeader.biCompression == BI_RGB)
{
redMask = 0x00ff0000;
greenMask = 0x0000ff00;
blueMask = 0x000000ff;
}
if ((redMask == 0x00ff0000) &&
(greenMask == 0x0000ff00) &&
(blueMask == 0x000000ff))
{
if (pbmi->bmiHeader.biBitCount == 24)
{
pixelFormat = PixelFormat24bppRGB;
}
else if (pbmi->bmiHeader.biBitCount == 32)
{
pixelFormat = PixelFormat32bppRGB;
}
}
else if ((redMask == 0x000000ff) &&
(greenMask == 0x0000ff00) &&
(blueMask == 0x00ff0000) &&
(pbmi->bmiHeader.biBitCount == 24))
{
pixelFormat = PIXFMT_24BPP_BGR;
}
else if ((redMask == 0x00007c00) &&
(greenMask == 0x000003e0) &&
(blueMask == 0x0000001f) &&
(pbmi->bmiHeader.biBitCount == 16))
{
pixelFormat = PixelFormat16bppRGB555;
}
else if ((redMask == 0x0000f800) &&
(greenMask == 0x000007e0) &&
(blueMask == 0x0000001f) &&
(pbmi->bmiHeader.biBitCount == 16))
{
pixelFormat = PixelFormat16bppRGB565;
}
}
if (pixelFormat == PixelFormatUndefined)
{
ONCE(WARNING(("(once) ExtractPixelFormatFromHDC: Unrecognized pixel format")));
}
DeleteObject(hbm);
}
else
{
WARNING(("ExtractPixelFormatFromHDC: CreateCompatibleBitmap failed"));
}
return pixelFormat;
}