/*++ Copyright (c) 2000 Microsoft Corporation Module Name: CheckBmp.cpp Abstract: BMP file format checking routines Author: Hakki T. Bostanci (hakkib) 17-Dec-1999 Revision History: --*/ #include "stdafx.h" #include "Wrappers.h" #include "LogWindow.h" #include "LogWrappers.h" #include "Argv.h" #include "CheckBmp.h" #ifdef _CONSOLE TCHAR szDecVal[] = _T("%-15s = %d"); TCHAR szHexVal[] = _T("%-15s = 0x%08x"); TCHAR szStrVal[] = _T("%-15s = %s"); TCHAR szDblVal[] = _T("%-15s = %.4lf"); #else //_CONSOLE TCHAR szDecVal[] = _T("%s = %d"); TCHAR szHexVal[] = _T("%s = 0x%08x"); TCHAR szStrVal[] = _T("%s = %s"); TCHAR szDblVal[] = _T("%s = %.4lf"); #endif //_CONSOLE ////////////////////////////////////////////////////////////////////////// // // HeaderSize // // Routine Description: // returns the size of the bitmap header // // Arguments: // // Return Value: // inline DWORD HeaderSize( const VOID *pBitmapInfo ) { return *(PDWORD)pBitmapInfo; } ////////////////////////////////////////////////////////////////////////// // // WidthBytes // // Routine Description: // calculates the number of bytes in a BMP row (that should be DWORD aligned) // // Arguments: // // Return Value: // inline DWORD WidthBytes( DWORD dwWidth, DWORD dwBitCount ) { return (((dwWidth * dwBitCount) + 31) & ~31) >> 3; } ////////////////////////////////////////////////////////////////////////// // // FXPT_TO_FLPT, FXPT2DOT30_TO_FLPT, FXPT16DOT16_TO_FLPT // // Routine Description: // floating point to fixed point conversion macros // // Arguments: // // Return Value: // inline double FXPT_TO_FLPT( long nFixed, int nPoint ) { double nInteger = nFixed >> nPoint; double nFraction = ldexp(nFixed & ((1 << nPoint) - 1), -nPoint); return nInteger + nFraction; } inline double FXPT2DOT30_TO_FLPT( FXPT2DOT30 nFixed ) { return FXPT_TO_FLPT(nFixed, 30); } inline double FXPT16DOT16_TO_FLPT( FXPT16DOT16 nFixed ) { return FXPT_TO_FLPT(nFixed, 16); } ////////////////////////////////////////////////////////////////////////// // // Contiguous // // Routine Description: // Tests whether the 1's are contiguous in an integers, i.e. 00000111, // 00011100, 11100000, 00000000, 11111111 are OK but 00101100 is not // // Arguments: // // Return Value: // template BOOL Contiguous(T Bits) { const NumBits = sizeof(T) * 8; T i = 0; while (i < NumBits && (Bits & (1 << i)) == 0) ++i; while (i < NumBits && (Bits & (1 << i)) != 0) ++i; while (i < NumBits && (Bits & (1 << i)) == 0) ++i; return i == NumBits; } ////////////////////////////////////////////////////////////////////////// // // CheckColorMasks // // Routine Description: // checks the validity of the color masks in a bitfields type bmp. // the masks should not overlap and the bits should be contiguous // within each map // // Arguments: // // Return Value: // BOOL CheckColorMasks( DWORD dwRedMask, DWORD dwGreenMask, DWORD dwBlueMask ) { return (dwRedMask & dwGreenMask) == 0 && (dwRedMask & dwBlueMask) == 0 && (dwGreenMask & dwBlueMask) == 0 && Contiguous(dwRedMask) && Contiguous(dwGreenMask) && Contiguous(dwBlueMask); } ////////////////////////////////////////////////////////////////////////// // // // // Routine Description: // compares two RGBTRIPLE's // // Arguments: // // Return Value: // inline bool __cdecl operator <(const RGBTRIPLE &lhs, const RGBTRIPLE &rhs) { return RGB(lhs.rgbtRed, lhs.rgbtGreen, lhs.rgbtBlue) < RGB(rhs.rgbtRed, rhs.rgbtGreen, rhs.rgbtBlue); } ////////////////////////////////////////////////////////////////////////// // // // // Routine Description: // // // Arguments: // // Return Value: // inline void __cdecl LOG_INFO(PCTSTR pFormat, ...) { va_list arglist; va_start(arglist, pFormat); g_pLog->LogV(TLS_INFO | TLS_VARIATION, 0, 0, pFormat, arglist); } inline void __cdecl LOG_ERROR(DWORD dwLogLevel, PCTSTR pFormat, ...) { va_list arglist; va_start(arglist, pFormat); g_pLog->LogV(dwLogLevel | TLS_VARIATION, 0, 0, pFormat, arglist); } ////////////////////////////////////////////////////////////////////////// // // CheckBmp // // Routine Description: // Main entry point for this module. Checks the validity of a BMP // file or an in-memory image. // // Arguments: // // Return Value: // BOOL CheckBmp(PVOID pDIB, DWORD dwDIBSize, BOOL bSkipFileHeader) { return CCheckBmp().Check(pDIB, dwDIBSize, bSkipFileHeader); } BOOL CheckBmp(PCTSTR pszFileName) { CInFile TheFile(pszFileName); CFileMapping TheMap(TheFile, 0, PAGE_READONLY); CMapViewOfFile TheData(TheMap, FILE_MAP_READ); return CCheckBmp().Check(TheData, GetFileSize(TheFile, 0), FALSE); } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::Check // // Routine Description: // // Arguments: // // Return Value: // CCheckBmp::Check(PVOID pDIB, DWORD dwDIBSize, BOOL bSkipFileHeader) { ZeroMemory(this, sizeof(*this)); LOG_INFO(szDecVal, _T("DibSize"), dwDIBSize); m_pDIB = pDIB; m_nDIBSize = dwDIBSize; return (bSkipFileHeader || CheckFileHeader()) && CheckBitmapInfo() && CheckPalette() && CheckPixelData(); } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckFileHeader // // Routine Description: // Checks BITMAPFILEHEADER // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckFileHeader() { BOOL bResult = TRUE; m_pFileHeader = m_pDIB; m_nFileHeaderSize = sizeof(BITMAPFILEHEADER); PBITMAPFILEHEADER pbmfh = (PBITMAPFILEHEADER) m_pDIB; // bfType // should read "BM" LOG_INFO(szHexVal, _T("Type"), pbmfh->bfType); if (LOBYTE(pbmfh->bfType) != 'B' || HIBYTE(pbmfh->bfType) != 'M') { LOG_ERROR(TLS_SEV2, _T("Unexpected bitmap file type")); bResult = FALSE; } // bfSize // should equal file size LOG_INFO(szDecVal, _T("Size"), pbmfh->bfSize); if (pbmfh->bfSize != m_nDIBSize) { LOG_ERROR(TLS_SEV2, _T("Unexpected bitmap file size")); bResult = FALSE; } // bfReserved1, bfReserved2 // should be zero LOG_INFO(szDecVal, _T("Reserved1"), pbmfh->bfReserved1); LOG_INFO(szDecVal, _T("Reserved2"), pbmfh->bfReserved2); if (pbmfh->bfReserved1 != 0 || pbmfh->bfReserved2 != 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Reserved value")); bResult = FALSE; } // bfOffBits // will be checked in CheckPixelData LOG_INFO(szDecVal, _T("OffBits"), pbmfh->bfOffBits); m_pPixelData = (PBYTE) pbmfh + pbmfh->bfOffBits; return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckBitmapInfo // // Routine Description: // Checks the bitmap header according to the header size // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckBitmapInfo() { BOOL bResult = TRUE; m_pInfoHeader = (PBYTE) m_pDIB + m_nFileHeaderSize; PBITMAPINFO pbmi = (PBITMAPINFO) m_pInfoHeader; m_nInfoHeaderSize = pbmi->bmiHeader.biSize; LOG_INFO(szDecVal, _T("Size"), pbmi->bmiHeader.biSize); switch (pbmi->bmiHeader.biSize) { case sizeof(BITMAPCOREHEADER): bResult = CheckBitmapCoreHeader(); break; case sizeof(BITMAPINFOHEADER): bResult = CheckBitmapInfoHeader(); break; case sizeof(BITMAPV4HEADER): bResult = CheckBitmapV4Header(); break; case sizeof(BITMAPV5HEADER): bResult = CheckBitmapV5Header(); break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected header size")); bResult = FALSE; } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckPalette // // Routine Description: // Checks the bitmap color palette // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckPalette() { BOOL bResult = TRUE; m_pPalette = (PBYTE) m_pInfoHeader + m_nInfoHeaderSize; std::set UsedColors; if (m_nInfoHeaderSize == sizeof(BITMAPCOREHEADER)) { RGBTRIPLE *prgbt = (RGBTRIPLE *) m_pPalette; for (int i = 0; i < m_nPaletteSize / sizeof(RGBTRIPLE); ++i) { /*LOG_INFO( _T("Color %3d: R=%02x G=%02x B=%02x"), i, prgbt[i].rgbtRed, prgbt[i].rgbtGreen, prgbt[i].rgbtBlue );*/ if (!UsedColors.insert(prgbt[i]).second) { LOG_ERROR( TLS_SEV3, _T("Repeated palette entry %3d: R=%02x G=%02x B=%02x"), i, prgbt[i].rgbtRed, prgbt[i].rgbtGreen, prgbt[i].rgbtBlue ); bResult = FALSE; } } } else { LPRGBQUAD prgbq = (LPRGBQUAD) m_pPalette; for (int i = 0; i < m_nPaletteSize / sizeof(RGBQUAD); ++i) { /*LOG_INFO( _T("Color %3d: R=%02x G=%02x B=%02x A=%02x"), i, prgbq[i].rgbRed, prgbq[i].rgbGreen, prgbq[i].rgbBlue, prgbq[i].rgbReserved );*/ if (prgbq[i].rgbReserved != 0) { LOG_ERROR( TLS_SEV3, _T("Unexpected rgbReserved value in palette entry %3d: R=%02x G=%02x B=%02x A=%02x"), i, prgbq[i].rgbRed, prgbq[i].rgbGreen, prgbq[i].rgbBlue, prgbq[i].rgbReserved ); bResult = FALSE; } RGBTRIPLE rgbt; rgbt.rgbtRed = prgbq[i].rgbRed; rgbt.rgbtGreen = prgbq[i].rgbGreen; rgbt.rgbtBlue = prgbq[i].rgbBlue; if (!UsedColors.insert(rgbt).second) { LOG_ERROR( TLS_SEV3, _T("Repeated palette entry %3d: R=%02x G=%02x B=%02x A=%02x"), i, prgbq[i].rgbRed, prgbq[i].rgbGreen, prgbq[i].rgbBlue, prgbq[i].rgbReserved ); bResult = FALSE; } } } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckPixelData // // Routine Description: // Checks the bitmap color palette // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckPixelData() { BOOL bResult = TRUE; PVOID pExpectedPixelData = (PBYTE) m_pPalette + m_nPaletteSize; if (m_pProfile != 0 && m_pProfile <= pExpectedPixelData) { pExpectedPixelData = (PBYTE) m_pProfile + m_nProfileSize; } if (m_pPixelData != 0 && m_pPixelData != pExpectedPixelData) { LOG_ERROR(TLS_SEV3, _T("Unexpected OffBits value")); bResult = FALSE; } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckBitmapCoreHeader // // Routine Description: // Checks BITMAPCOREHEADER // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckBitmapCoreHeader() { BOOL bResult = TRUE; PBITMAPCOREHEADER pbih = (PBITMAPCOREHEADER) m_pInfoHeader; // bcWidth // should be positive LOG_INFO(szDecVal, _T("Width"), pbih->bcWidth); if (pbih->bcWidth <= 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Width value")); bResult = FALSE; } // bcHeight // should be positive LOG_INFO(szDecVal, _T("Height"), pbih->bcHeight); if (pbih->bcHeight <= 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Height value")); bResult = FALSE; } // bcPlanes // should be 1 LOG_INFO(szDecVal, _T("Planes"), pbih->bcPlanes); if (pbih->bcPlanes != 1) { LOG_ERROR(TLS_SEV2, _T("Unexpected Planes value")); } // bcBitCount // can be 1, 4, 8 or 24 LOG_INFO(szDecVal, _T("BitCount"), pbih->bcBitCount); switch (pbih->bcBitCount) { case 1: case 4: case 8: m_nPaletteSize = (1 << pbih->bcBitCount) * sizeof(RGBTRIPLE); break; case 24: m_nPaletteSize = 0; break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected BitCount value")); bResult = FALSE; break; } m_nPixelDataSize = WidthBytes(pbih->bcWidth, pbih->bcBitCount) * pbih->bcHeight; return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckBitmapInfoHeader // // Routine Description: // Checks BITMAPINFOHEADER // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckBitmapInfoHeader() { PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) m_pInfoHeader; BOOL bResult = TRUE; // biWidth // should be positive LOG_INFO(szDecVal, _T("Width"), pbih->biWidth); if (pbih->biWidth <= 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Width value")); bResult = FALSE; } // biHeight // should not be zero, if negative, should be BI_RGB or BI_BITFIELDS LOG_INFO(szDecVal, _T("Height"), pbih->biHeight); if (pbih->biHeight == 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Height value")); bResult = FALSE; } if (pbih->biHeight < 0) { switch (pbih->biCompression) { case BI_RGB: case BI_BITFIELDS: case BI_JPEG: case BI_PNG: break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected Compression value for negative Height")); bResult = FALSE; break; } } // biPlanes // should be 1 LOG_INFO(szDecVal, _T("Planes"), pbih->biPlanes); if (pbih->biPlanes != 1) { LOG_ERROR(TLS_SEV2, _T("Unexpected Planes value")); } // biBitCount // can be 0 (only for BI_JPEG or BI_PNG), 1, 4, 8, 16, 24 or 32 LOG_INFO(szDecVal, _T("BitCount"), pbih->biBitCount); switch (pbih->biBitCount) { case 0: switch (pbih->biCompression) { case BI_JPEG: case BI_PNG: break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected Compression value for zero BitCount")); bResult = FALSE; break; } break; case 1: case 4: case 8: m_nPaletteSize = (1 << pbih->biBitCount) * sizeof(RGBQUAD); break; case 16: case 24: case 32: m_nPaletteSize = 0; break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected BitCount value")); bResult = FALSE; break; } // biCompression switch (pbih->biCompression) { case BI_RGB: LOG_INFO(szStrVal, _T("Compression"), _T("BI_RGB")); break; case BI_RLE8: LOG_INFO(szStrVal, _T("Compression"), _T("BI_RLE8")); if (pbih->biBitCount != 8) { LOG_ERROR(TLS_SEV2, _T("Unexpected BitCount value for BI_RLE8")); bResult = FALSE; } break; case BI_RLE4: LOG_INFO(szStrVal, _T("Compression"), _T("BI_RLE4")); if (pbih->biBitCount != 4) { LOG_ERROR(TLS_SEV2, _T("Unexpected BitCount value for BI_RLE4")); bResult = FALSE; } break; case BI_BITFIELDS: { LOG_INFO(szStrVal, _T("Compression"), _T("BI_BITFIELDS")); DWORD dwRedMask = ((PDWORD)(pbih + 1))[0]; DWORD dwGreenMask = ((PDWORD)(pbih + 1))[1]; DWORD dwBlueMask = ((PDWORD)(pbih + 1))[2]; if (pbih->biSize == sizeof(BITMAPINFOHEADER)) { m_nInfoHeaderSize += 3 * sizeof(DWORD); LOG_INFO(szHexVal, _T("RedMask"), dwRedMask); LOG_INFO(szHexVal, _T("GreenMask"), dwGreenMask); LOG_INFO(szHexVal, _T("BlueMask"), dwBlueMask); } switch (pbih->biBitCount) { case 16: if ( (dwRedMask != 0x7C00 || dwGreenMask != 0x03E0 || dwBlueMask != 0x001F) && (dwRedMask != 0xF800 || dwGreenMask != 0x07E0 || dwBlueMask != 0x001F) ) { LOG_ERROR(TLS_WARN, _T("Unexpected color masks for Win9x BI_BITFIELDS")); } else { if ((dwRedMask | dwGreenMask | dwBlueMask) & 0xFFFF0000) { LOG_ERROR(TLS_SEV2, _T("Unexpected color masks for 16-bit BI_BITFIELDS")); bResult = FALSE; } if (!CheckColorMasks(dwRedMask, dwGreenMask, dwBlueMask)) { LOG_ERROR(TLS_SEV2, _T("Unexpected color masks for BI_BITFIELDS")); bResult = FALSE; } } break; case 32: if (dwRedMask != 0x00FF0000 || dwGreenMask != 0x0000FF00 || dwBlueMask != 0x000000FF) { LOG_ERROR(TLS_WARN, _T("Unexpected color masks for Win9x BI_BITFIELDS")); } else { if (!CheckColorMasks(dwRedMask, dwGreenMask, dwBlueMask)) { LOG_ERROR(TLS_SEV2, _T("Unexpected color masks for BI_BITFIELDS")); bResult = FALSE; } } break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected BitCount for BI_BITFIELDS")); bResult = FALSE; break; } break; } case BI_JPEG: LOG_INFO(szStrVal, _T("Compression"), _T("BI_JPEG")); break; case BI_PNG: LOG_INFO(szStrVal, _T("Compression"), _T("BI_PNG")); break; default: LOG_INFO(szDecVal, _T("Compression"), pbih->biCompression); LOG_ERROR(TLS_SEV2, _T("Unexpected Compression value")); bResult = FALSE; break; } // biSizeImage // should not be negative, can be zero only for BI_RGB LOG_INFO(szDecVal, _T("SizeImage"), pbih->biSizeImage); if ((LONG) pbih->biSizeImage < 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected SizeImage value")); bResult = FALSE; } m_nPixelDataSize = WidthBytes(Abs(pbih->biWidth), pbih->biBitCount) * Abs(pbih->biHeight); if (pbih->biSizeImage != 0) { if (pbih->biCompression == BI_RGB && m_nPixelDataSize != pbih->biSizeImage) { LOG_ERROR(TLS_SEV2, _T("Unexpected SizeImage value")); bResult = FALSE; } m_nPixelDataSize = pbih->biSizeImage; } else { if (pbih->biCompression != BI_RGB) { LOG_ERROR(TLS_SEV2, _T("Unexpected SizeImage value for non BI_RGB bitmap")); bResult = FALSE; } } // biClrUsed // should not be greater than the max number of bitdepth colors LOG_INFO(szDecVal, _T("ClrUsed"), pbih->biClrUsed); if (pbih->biClrUsed != 0) { if (pbih->biBitCount < 16 && pbih->biClrUsed * sizeof(RGBQUAD) > m_nPaletteSize) { LOG_ERROR(TLS_SEV2, _T("Unexpected ClrUsed value for the BitCount")); bResult = FALSE; } else { m_nPaletteSize = pbih->biClrUsed * sizeof(RGBQUAD); } } // biClrImportant // should be equal to or less than biClrUsed LOG_INFO(szDecVal, _T("ClrImportant"), pbih->biClrImportant); if (pbih->biClrUsed != 0 && pbih->biClrImportant > pbih->biClrUsed) { LOG_ERROR(TLS_SEV2, _T("Unexpected ClrImportant value")); bResult = FALSE; } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckBitmapV4Header // // Routine Description: // Checks BITMAPV4HEADER // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckBitmapV4Header() { BOOL bResult = CheckBitmapInfoHeader(); PBITMAPV4HEADER pbih = (PBITMAPV4HEADER) m_pInfoHeader; // bV4RedMask, bV4GreenMask, bV4BlueMask, bV4AlphaMask // already checked in the info header LOG_INFO(szHexVal, _T("RedMask"), pbih->bV4RedMask); LOG_INFO(szHexVal, _T("GreenMask"), pbih->bV4GreenMask); LOG_INFO(szHexVal, _T("BlueMask"), pbih->bV4BlueMask); LOG_INFO(szHexVal, _T("AlphaMask"), pbih->bV4AlphaMask); // bV4CSType // should be one of LCS_ types switch (pbih->bV4CSType) { case LCS_CALIBRATED_RGB: LOG_INFO(szStrVal, _T("CSType"), _T("LCS_CALIBRATED_RGB")); break; case LCS_sRGB: LOG_INFO(szStrVal, _T("CSType"), _T("LCS_sRGB")); break; case LCS_WINDOWS_COLOR_SPACE: LOG_INFO(szStrVal, _T("CSType"), _T("LCS_WINDOWS_COLOR_SPACE")); break; case PROFILE_LINKED: LOG_INFO(szStrVal, _T("CSType"), _T("PROFILE_LINKED")); break; case PROFILE_EMBEDDED: LOG_INFO(szStrVal, _T("CSType"), _T("PROFILE_EMBEDDED")); break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected CSType value")); bResult = FALSE; break; } // bV4Endpoints, bV4GammaRed, bV4GammaGreen, bV4GammaBlue // should be present only for LCS_CALIBRATED_RGB LOG_INFO(szDblVal, _T("EndpointRedX"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzRed.ciexyzX)); LOG_INFO(szDblVal, _T("EndpointRedY"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzRed.ciexyzY)); LOG_INFO(szDblVal, _T("EndpointRedZ"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzRed.ciexyzZ)); LOG_INFO(szDblVal, _T("EndpointGreenX"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzGreen.ciexyzX)); LOG_INFO(szDblVal, _T("EndpointGreenY"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzGreen.ciexyzY)); LOG_INFO(szDblVal, _T("EndpointGreenZ"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzGreen.ciexyzZ)); LOG_INFO(szDblVal, _T("EndpointBlueX"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzBlue.ciexyzX)); LOG_INFO(szDblVal, _T("EndpointBlueY"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzBlue.ciexyzY)); LOG_INFO(szDblVal, _T("EndpointBlueZ"), FXPT2DOT30_TO_FLPT(pbih->bV4Endpoints.ciexyzBlue.ciexyzZ)); LOG_INFO(szDblVal, _T("GammaRed"), FXPT16DOT16_TO_FLPT(pbih->bV4GammaRed)); LOG_INFO(szDblVal, _T("GammaGreen"), FXPT16DOT16_TO_FLPT(pbih->bV4GammaGreen)); LOG_INFO(szDblVal, _T("GammaBlue"), FXPT16DOT16_TO_FLPT(pbih->bV4GammaBlue)); if (pbih->bV4CSType != LCS_CALIBRATED_RGB) { // bugbug: how can I check a valid colorspace? if ( pbih->bV4Endpoints.ciexyzRed.ciexyzX != 0 || pbih->bV4Endpoints.ciexyzRed.ciexyzY != 0 || pbih->bV4Endpoints.ciexyzRed.ciexyzZ != 0 || pbih->bV4Endpoints.ciexyzGreen.ciexyzX != 0 || pbih->bV4Endpoints.ciexyzGreen.ciexyzY != 0 || pbih->bV4Endpoints.ciexyzGreen.ciexyzZ != 0 || pbih->bV4Endpoints.ciexyzBlue.ciexyzX != 0 || pbih->bV4Endpoints.ciexyzBlue.ciexyzY != 0 || pbih->bV4Endpoints.ciexyzBlue.ciexyzZ != 0 || pbih->bV4GammaRed != 0 || pbih->bV4GammaGreen != 0 || pbih->bV4GammaBlue != 0 ) { LOG_ERROR(TLS_SEV2, _T("Unexpected colorspace values for CSType")); bResult = FALSE; } } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CCheckBmp::CheckBitmapV5Header // // Routine Description: // Checks BITMAPV5HEADER // // Arguments: // // Return Value: // BOOL CCheckBmp::CheckBitmapV5Header() { BOOL bResult = CheckBitmapV4Header(); PBITMAPV5HEADER pbih = (PBITMAPV5HEADER) m_pInfoHeader; // bV5Intent // should be one of the LCS_ values switch (pbih->bV5Intent) { case LCS_GM_BUSINESS: LOG_INFO(szStrVal, _T("Intent"), _T("LCS_GM_BUSINESS")); break; case LCS_GM_GRAPHICS: LOG_INFO(szStrVal, _T("Intent"), _T("LCS_GM_GRAPHICS")); break; case LCS_GM_IMAGES: LOG_INFO(szStrVal, _T("Intent"), _T("LCS_GM_IMAGES")); break; case LCS_GM_ABS_COLORIMETRIC: LOG_INFO(szStrVal, _T("Intent"), _T("LCS_GM_ABS_COLORIMETRIC")); break; default: LOG_ERROR(TLS_SEV2, _T("Unexpected Intent value")); bResult = FALSE; break; } // bV5ProfileData, bV5ProfileSize // check profile data LOG_INFO(szDecVal, _T("ProfileData"), pbih->bV5ProfileData); LOG_INFO(szDecVal, _T("ProfileSize"), pbih->bV5ProfileSize); switch (pbih->bV5CSType) { case LCS_CALIBRATED_RGB: case LCS_sRGB: case LCS_WINDOWS_COLOR_SPACE: if (pbih->bV5ProfileData != 0 || pbih->bV5ProfileSize != 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected profile info for CSType")); bResult = FALSE; } break; case PROFILE_LINKED: { PCSTR pName = (PCSTR) ((PBYTE) pbih + pbih->bV5ProfileData); if (MultiByteToWideChar(1252, MB_ERR_INVALID_CHARS, pName, -1, 0, 0) == 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected profile name for PROFILE_LINKED")); bResult = FALSE; } // continue, } case PROFILE_EMBEDDED: if (pbih->bV5ProfileData == 0 || pbih->bV5ProfileSize == 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected profile info for CSType")); bResult = FALSE; } m_pProfile = (PBYTE) pbih + pbih->bV5ProfileData; m_nProfileSize = pbih->bV5ProfileSize; break; } // Reserved // should be zero LOG_INFO(szHexVal, _T("Reserved"), pbih->bV5Reserved); if (pbih->bV5Reserved != 0) { LOG_ERROR(TLS_SEV2, _T("Unexpected Reserved value")); bResult = FALSE; } return bResult; } ////////////////////////////////////////////////////////////////////////// // // CheckBMPMain // // Routine Description: // _tmain() function in case someone runs this as a standalone program // // Arguments: // // Return Value: // void CheckBMPMain() { AllocCRTConsole(); INT argc; CGlobalMem argv(CommandLineToArgv(GetCommandLine(), &argc)); CFullPathName FileName(argv[1]); for ( CFindFile FindFile(FileName); FindFile.Found(); FindFile.FindNextFile() ) { FileName.SetFileName(FindFile.cFileName); LOG_INFO(FileName); CheckBmp((PCTSTR) FileName); } }