/******************************Module*Header*******************************\ * Module Name: fastdib.c * * CreateCompatibleDIB implementation. * * Created: 23-Jan-1996 21:08:18 * Author: Gilman Wong [gilmanw] * * Copyright (c) 1996 Microsoft Corporation * \**************************************************************************/ #include "stdafx.h" #include #include "FastDib.h" static BOOL bFillBitmapInfo(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi); static BOOL bFillColorTable(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi); static UINT MyGetSystemPaletteEntries(HDC hdc, UINT iStartIndex, UINT nEntries, LPPALETTEENTRY lppe); static BOOL bComputeLogicalToSurfaceMap(HDC hdc, HPALETTE hpal, BYTE *pajVector); /******************************Public*Routine******************************\ * CreateCompatibleDIB * * Create a DIB section with an optimal format w.r.t. the specified hdc. * * If DIB <= 8bpp, then the DIB color table is initialized based on the * specified palette. If the palette handle is NULL, then the system * palette is used. * * Note: The hdc must be a direct DC (not an info or memory DC). * * Note: On palettized displays, if the system palette changes the * UpdateDIBColorTable function should be called to maintain * the identity palette mapping between the DIB and the display. * * Returns: * Valid bitmap handle if successful, NULL if error. * * History: * 23-Jan-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ HBITMAP APIENTRY CreateCompatibleDIB( IN HDC hdc, IN HPALETTE hpal, IN ULONG ulWidth, IN ULONG ulHeight, OUT PVOID *ppvBits, OUT BITMAPINFOHEADER * pbmih) { HBITMAP hbmRet = (HBITMAP) NULL; BYTE aj[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) * 255)]; BITMAPINFO *pbmi = (BITMAPINFO *) aj; // // Validate hdc. // if ( GetObjectType(hdc) != OBJ_DC ) { Trace("CreateCompatibleDIB: not OBJ_DC\n"); return hbmRet; } ZeroMemory(aj, sizeof(aj)); if ( bFillBitmapInfo(hdc, hpal, pbmi) ) { // // 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; if (pbmih != NULL) { DWORD cbSize = min(pbmih->biSize, pbmi->bmiHeader.biSize); CopyMemory(pbmih, &pbmi->bmiHeader, cbSize); pbmih->biSize = cbSize; } // // 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 ) { Trace("CreateCompatibleDIB: CreateDIBSection failed\n"); } } else { Trace("CreateCompatibleDIB: bFillBitmapInfo failed\n"); } return hbmRet; } /******************************Public*Routine******************************\ * UpdateDIBColorTable * * Synchronize the DIB color table to the specified palette hpal. * If hpal is NULL, then use the system palette. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 23-Jan-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL APIENTRY UpdateDIBColorTable(HDC hdcMem, HDC hdc, HPALETTE hpal) { BOOL bRet = FALSE; HBITMAP hbm; DIBSECTION ds; BYTE aj[(sizeof(RGBQUAD) + sizeof(PALETTEENTRY)) * 256]; LPPALETTEENTRY lppe = (LPPALETTEENTRY) aj; LPRGBQUAD prgb = (LPRGBQUAD) (lppe + 256); ULONG cColors; // // Validate hdc. // if ( GetObjectType(hdc) != OBJ_DC ) { Trace("UpdateDIBColorTable: not OBJ_DC\n"); return bRet; } if ( GetObjectType(hdcMem) != OBJ_MEMDC ) { Trace("UpdateDIBColorTable: not OBJ_MEMDC\n"); return bRet; } // // Get the bitmap handle out of the memdc. // hbm = (HBITMAP) GetCurrentObject(hdcMem, OBJ_BITMAP); // // Validate bitmap (must be DIB section). // if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds)) && ds.dsBm.bmBits ) { // // Get palette entries from specified palette or system palette. // cColors = 1 << ds.dsBmih.biBitCount; if ( hpal ? GetPaletteEntries(hpal, 0, cColors, lppe) : MyGetSystemPaletteEntries(hdc, 0, cColors, lppe) ) { UINT i; // // Convert to RGBQUAD. // for (i = 0; i < cColors; i++) { prgb[i].rgbRed = lppe[i].peRed; prgb[i].rgbGreen = lppe[i].peGreen; prgb[i].rgbBlue = lppe[i].peBlue; prgb[i].rgbReserved = 0; } // // Set the DIB color table. // bRet = (BOOL) SetDIBColorTable(hdcMem, 0, cColors, prgb); if (!bRet) { Trace("UpdateDIBColorTable: SetDIBColorTable failed\n"); } } else { Trace("UpdateDIBColorTable: MyGetSystemPaletteEntries failed\n"); } } else { Trace("UpdateDIBColorTable: GetObject failed\n"); } return bRet; } /******************************Public*Routine******************************\ * GetCompatibleDIBInfo * * Copies pointer to bitmap origin to ppvBase and bitmap stride to plStride. * Win32 DIBs can be created bottom-up (the default) with the origin at the * lower left corner or top-down with the origin at the upper left corner. * If the bitmap is top-down, *plStride is positive; if bottom-up, *plStride * us negative. * * Also, because of restrictions on the alignment of scan lines the width * the bitmap is often not the same as the stride (stride is the number of * bytes between vertically adjacent pixels). * * The ppvBase and plStride value returned will allow you to address any * given pixel (x, y) in the bitmap as follows: * * PIXEL *ppix; * * ppix = (PIXEL *) (((BYTE *)*ppvBase) + (y * *plStride) + (x * sizeof(PIXEL))); * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 02-Feb-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL APIENTRY GetCompatibleDIBInfo(HBITMAP hbm, PVOID *ppvBase, LONG *plStride) { BOOL bRet = FALSE; DIBSECTION ds; // // Call GetObject to return a DIBSECTION. If successful, the // bitmap is a DIB section and we can retrieve the pointer to // the bitmap bits and other parameters. // if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds)) && ds.dsBm.bmBits ) { //!!!dbug -- GDI Bug 19374: bmWidthBytes returns pitch assuming //!!! that DIB scanlines are WORD aligned (as they //!!! are in Win95). But NT DIBs are DWORD aligned. //!!! When bug if corrected, we can remove this block of //!!! code. { OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(osvi); if (GetVersionEx(&osvi)) { if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT ) { ds.dsBm.bmWidthBytes = (ds.dsBm.bmWidthBytes + 3) & ~3; } } else { Trace("GetCompatibleDIBInfo: GetVersionEx failed with %d\n", GetLastError()); return bRet; } } // // If biHeight is positive, then the bitmap is a bottom-up DIB. // If biHeight is negative, then the bitmap is a top-down DIB. // if ( ds.dsBmih.biHeight > 0 ) { *ppvBase = (PVOID) (((BYTE *) ds.dsBm.bmBits) + (ds.dsBm.bmWidthBytes * (ds.dsBm.bmHeight - 1))); *plStride = (ULONG) (-ds.dsBm.bmWidthBytes); } else { *ppvBase = ds.dsBm.bmBits; *plStride = ds.dsBm.bmWidthBytes; } bRet = TRUE; } else { Trace("GetCompatibleDIBInfo: cannot get pointer to DIBSECTION bmBits\n"); } return bRet; } /******************************Public*Routine******************************\ * GetDIBTranslationVector * * Copies the translation vector that maps colors in the specified palette, * hpal, to the DIB selected into the specified DC, hdcMem. * * Effects: * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 02-Feb-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL APIENTRY GetDIBTranslationVector(HDC hdcMem, HPALETTE hpal, BYTE *pbVector) { BOOL bRet = FALSE; HBITMAP hbm; DIBSECTION ds; // // Validate parameters. // if ( GetObjectType(hdcMem) != OBJ_MEMDC || GetObjectType(hpal) != OBJ_PAL || !pbVector ) { Trace("GetDIBTranslationVector: bad parameter\n"); return bRet; } // // The function bComputeLogicalToSurfaceMap cannot handle palettes // greater than 256 entries. // if ( GetPaletteEntries(hpal, 0, 1, NULL) > 256 ) { Trace("GetDIBTranslationVector: palette too big\n"); return bRet; } // // The DIB must have a color table. // hbm = (HBITMAP) GetCurrentObject(hdcMem, OBJ_BITMAP); if ( (GetObject(hbm, sizeof(ds), &ds) == sizeof(ds)) && (ds.dsBmih.biBitCount <= 8) ) { bRet = bComputeLogicalToSurfaceMap(hdcMem, hpal, pbVector); } else { Trace("GetDIBTranslationVector: not a DIB section\n"); return bRet; } return bRet; } //////////////////// Below here are internal-only routines //////////////////// /******************************Public*Routine******************************\ * bFillBitmapInfo * * 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 creating a compatible bitmap and calling GetDIBits * to return the color masks. This is done with two calls. The first * call passes in biBitCount = 0 to GetDIBits which will fill in the * base BITMAPINFOHEADER data. The second call to GetDIBits (passing * in the BITMAPINFO filled in by the first call) will return the color * table or bitmasks, as appropriate. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 07-Jun-1995 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ static BOOL bFillBitmapInfo(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi) { HBITMAP hbm; BOOL bRet = FALSE; // // 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); if ( pbmi->bmiHeader.biBitCount <= 8 ) { bRet = bFillColorTable(hdc, hpal, pbmi); } else { 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); } bRet = TRUE; } DeleteObject(hbm); } else { Trace("bFillBitmapInfo: CreateCompatibleBitmap failed\n"); } return bRet; } /******************************Public*Routine******************************\ * bFillColorTable * * Initialize the color table of the BITMAPINFO pointed to by pbmi. Colors * are set to the current system palette. * * Note: call only valid for displays of 8bpp or less. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 23-Jan-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ static BOOL bFillColorTable(HDC hdc, HPALETTE hpal, BITMAPINFO *pbmi) { BOOL bRet = FALSE; BYTE aj[sizeof(PALETTEENTRY) * 256]; LPPALETTEENTRY lppe = (LPPALETTEENTRY) aj; RGBQUAD *prgb = (RGBQUAD *) &pbmi->bmiColors[0]; ULONG cColors; cColors = 1 << pbmi->bmiHeader.biBitCount; if ( cColors <= 256 ) { if ( hpal ? GetPaletteEntries(hpal, 0, cColors, lppe) : MyGetSystemPaletteEntries(hdc, 0, cColors, lppe) ) { UINT i; for (i = 0; i < cColors; i++) { prgb[i].rgbRed = lppe[i].peRed; prgb[i].rgbGreen = lppe[i].peGreen; prgb[i].rgbBlue = lppe[i].peBlue; prgb[i].rgbReserved = 0; } bRet = TRUE; } else { Trace("bFillColorTable: MyGetSystemPaletteEntries failed\n"); } } return bRet; } /******************************Public*Routine******************************\ * MyGetSystemPaletteEntries * * Internal version of GetSystemPaletteEntries. * * GetSystemPaletteEntries fails on some 4bpp devices. This version * will detect the 4bpp case and supply the hardcoded 16-color VGA palette. * Otherwise, it will pass the call on to GDI's GetSystemPaletteEntries. * * It is expected that this call will only be called in the 4bpp and 8bpp * cases as it is not necessary for OpenGL to query the system palette * for > 8bpp devices. * * History: * 17-Aug-1995 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ static PALETTEENTRY gapeVgaPalette[16] = { { 0, 0, 0, 0 }, { 0x80,0, 0, 0 }, { 0, 0x80,0, 0 }, { 0x80,0x80,0, 0 }, { 0, 0, 0x80, 0 }, { 0x80,0, 0x80, 0 }, { 0, 0x80,0x80, 0 }, { 0x80,0x80,0x80, 0 }, { 0xC0,0xC0,0xC0, 0 }, { 0xFF,0, 0, 0 }, { 0, 0xFF,0, 0 }, { 0xFF,0xFF,0, 0 }, { 0, 0, 0xFF, 0 }, { 0xFF,0, 0xFF, 0 }, { 0, 0xFF,0xFF, 0 }, { 0xFF,0xFF,0xFF, 0 } }; static UINT MyGetSystemPaletteEntries(HDC hdc, UINT iStartIndex, UINT nEntries, LPPALETTEENTRY lppe) { int nDeviceBits; nDeviceBits = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES); // // Some 4bpp displays will fail the GetSystemPaletteEntries call. // So if detected, return the hardcoded table. // if ( nDeviceBits == 4 ) { if ( lppe ) { nEntries = min(nEntries, (16 - iStartIndex)); memcpy(lppe, &gapeVgaPalette[iStartIndex], nEntries * sizeof(PALETTEENTRY)); } else nEntries = 16; return nEntries; } else { return GetSystemPaletteEntries(hdc, iStartIndex, nEntries, lppe); } } /******************************Public*Routine******************************\ * bComputeLogicalToSurfaceMap * * Copy logical palette to surface palette translation vector to the buffer * pointed to by pajVector. The logical palette is specified by hpal. The * surface is specified by hdc. * * Note: The hdc may identify either a direct (display) dc or a DIB memory dc. * If hdc is a display dc, then the surface palette is the system palette. * If hdc is a memory dc, then the surface palette is the DIB color table. * * History: * 27-Jan-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ static BOOL bComputeLogicalToSurfaceMap(HDC hdc, HPALETTE hpal, BYTE *pajVector) { BOOL bRet = FALSE; HPALETTE hpalSurf; ULONG cEntries, cSysEntries; DWORD dwDcType = GetObjectType(hdc); LPPALETTEENTRY lppeTmp, lppeEnd; BYTE aj[sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 512) + (sizeof(RGBQUAD) * 256)]; LOGPALETTE *ppal = (LOGPALETTE *) aj; LPPALETTEENTRY lppeSurf = &ppal->palPalEntry[0]; LPPALETTEENTRY lppe = lppeSurf + 256; RGBQUAD *prgb = (RGBQUAD *) (lppe + 256); // // Determine number of colors in each palette. // cEntries = GetPaletteEntries(hpal, 0, 1, NULL); if ( dwDcType == OBJ_DC ) cSysEntries = MyGetSystemPaletteEntries(hdc, 0, 1, NULL); else cSysEntries = 256; // // Get the logical palette entries. // cEntries = GetPaletteEntries(hpal, 0, cEntries, lppe); // // Get the surface palette entries. // if ( dwDcType == OBJ_DC ) { cSysEntries = MyGetSystemPaletteEntries(hdc, 0, cSysEntries, lppeSurf); lppeTmp = lppeSurf; lppeEnd = lppeSurf + cSysEntries; for (; lppeTmp < lppeEnd; lppeTmp++) lppeTmp->peFlags = 0; } else { RGBQUAD *prgbTmp; // // First get RGBQUADs from DIB color table... // cSysEntries = GetDIBColorTable(hdc, 0, cSysEntries, prgb); // // ...then convert RGBQUADs into PALETTEENTRIES. // prgbTmp = prgb; lppeTmp = lppeSurf; lppeEnd = lppeSurf + cSysEntries; while ( lppeTmp < lppeEnd ) { lppeTmp->peRed = prgbTmp->rgbRed; lppeTmp->peGreen = prgbTmp->rgbGreen; lppeTmp->peBlue = prgbTmp->rgbBlue; lppeTmp->peFlags = 0; lppeTmp++; prgbTmp++; } } // // Construct a translation vector by using GetNearestPaletteIndex to // map each entry in the logical palette to the surface palette. // if ( cEntries && cSysEntries ) { // // Create a temporary logical palette that matches the surface // palette retrieved above. // ppal->palVersion = 0x300; ppal->palNumEntries = (USHORT) cSysEntries; if ((hpalSurf = CreatePalette(ppal)) != NULL) { // // Translate each logical palette entry into a surface palette // index. // lppeTmp = lppe; lppeEnd = lppe + cEntries; for ( ; lppeTmp < lppeEnd; lppeTmp++, pajVector++) { *pajVector = (BYTE) GetNearestPaletteIndex( hpalSurf, RGB(lppeTmp->peRed, lppeTmp->peGreen, lppeTmp->peBlue) ); } bRet = TRUE; DeleteObject(hpalSurf); } else { Trace("bComputeLogicalToSurfaceMap: CreatePalette failed\n"); } } else { Trace("bComputeLogicalToSurfaceMap: failed to get pal info\n"); } return bRet; }