Skip to content

Commit 1d419d7

Browse files
committed
Display translucent 64-bit bitmaps
1 parent 342ddd0 commit 1d419d7

File tree

2 files changed

+157
-80
lines changed

2 files changed

+157
-80
lines changed

BmpHeaderViewer/DibApi.cpp

Lines changed: 129 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
// Determines the value of a color component using a color mask
2525
BYTE GetColorValue(DWORD dwPixel, DWORD dwMask);
26+
// Transforms a 16-bit sRGB64 color value in s2.13 format to 8-bit sRGB
27+
BYTE SRGB64ToSRGB(WORD wColor, BOOL bUseGammaEncoding = TRUE);
2628

2729
////////////////////////////////////////////////////////////////////////////////////////////////
2830

@@ -64,7 +66,7 @@ BOOL SaveBitmap(LPCTSTR lpszFileName, HANDLE hDib)
6466
}
6567

6668
SIZE_T cbDibSize = cbOffBits + dwImageSize + dwProfileSize;
67-
if (cbDibSize > cbSize)
69+
if (cbDibSize == 0 || cbDibSize > cbSize)
6870
{
6971
GlobalUnlock(hDib);
7072
return FALSE;
@@ -83,8 +85,8 @@ BOOL SaveBitmap(LPCTSTR lpszFileName, HANDLE hDib)
8385
return FALSE;
8486
}
8587

86-
// Write file header
8788
DWORD dwWrite = 0;
89+
// Write file header
8890
if (!WriteFile(hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwWrite, NULL) || dwWrite != sizeof(BITMAPFILEHEADER))
8991
{
9092
GlobalUnlock(hDib);
@@ -93,18 +95,19 @@ BOOL SaveBitmap(LPCTSTR lpszFileName, HANDLE hDib)
9395
return FALSE;
9496
}
9597

96-
// Write bitmap header
97-
if (!WriteFile(hFile, &bmhv5, dwHeaderSize, &dwWrite, NULL) || dwWrite != dwHeaderSize)
98-
{
99-
GlobalUnlock(hDib);
100-
CloseHandle(hFile);
101-
DeleteFile(lpszFileName);
102-
return FALSE;
98+
if (dwHeaderSize)
99+
{ // Write bitmap header
100+
if (!WriteFile(hFile, &bmhv5, dwHeaderSize, &dwWrite, NULL) || dwWrite != dwHeaderSize)
101+
{
102+
GlobalUnlock(hDib);
103+
CloseHandle(hFile);
104+
DeleteFile(lpszFileName);
105+
return FALSE;
106+
}
103107
}
104108

105-
// Write color masks
106109
if (dwMasksSize)
107-
{
110+
{ // Write color masks
108111
if (!WriteFile(hFile, lpbi + dwHeaderSize, dwMasksSize, &dwWrite, NULL) || dwWrite != dwMasksSize)
109112
{
110113
GlobalUnlock(hDib);
@@ -114,9 +117,8 @@ BOOL SaveBitmap(LPCTSTR lpszFileName, HANDLE hDib)
114117
}
115118
}
116119

117-
// Write color table
118120
if (dwPaletteSize)
119-
{
121+
{ // Write color table
120122
if (!WriteFile(hFile, FindDibPalette(lpbi), dwPaletteSize, &dwWrite, NULL) || dwWrite != dwPaletteSize)
121123
{
122124
GlobalUnlock(hDib);
@@ -126,18 +128,19 @@ BOOL SaveBitmap(LPCTSTR lpszFileName, HANDLE hDib)
126128
}
127129
}
128130

129-
// Write bitmap bits
130-
if (!WriteFile(hFile, FindDibBits(lpbi), dwImageSize, &dwWrite, NULL) || dwWrite != dwImageSize)
131-
{
132-
GlobalUnlock(hDib);
133-
CloseHandle(hFile);
134-
DeleteFile(lpszFileName);
135-
return FALSE;
131+
if (dwImageSize)
132+
{ // Write bitmap bits
133+
if (!WriteFile(hFile, FindDibBits(lpbi), dwImageSize, &dwWrite, NULL) || dwWrite != dwImageSize)
134+
{
135+
GlobalUnlock(hDib);
136+
CloseHandle(hFile);
137+
DeleteFile(lpszFileName);
138+
return FALSE;
139+
}
136140
}
137141

138-
// Write color profile
139142
if (bHasProfile)
140-
{
143+
{ // Write color profile
141144
if (!WriteFile(hFile, lpbi + ((LPBITMAPV5HEADER)lpbi)->bV5ProfileData, dwProfileSize, &dwWrite, NULL) ||
142145
dwWrite != dwProfileSize)
143146
{
@@ -206,22 +209,16 @@ HBITMAP CreatePremultipliedBitmap(HANDLE hDib)
206209
if (lpbi == NULL)
207210
return NULL;
208211

209-
BOOL bIsCore = IS_OS2PM_DIB(lpbi);
210-
WORD wBitCount = bIsCore ? ((LPBITMAPCOREHEADER)lpbi)->bcBitCount : lpbi->biBitCount;
211-
BOOL bIs16Bpp = (wBitCount == 16);
212-
213-
if ((wBitCount != 16 && wBitCount != 32) ||
214-
(lpbi->biSize >= sizeof(BITMAPINFOHEADER) &&
215-
(lpbi->biCompression != BI_RGB && lpbi->biCompression != BI_BITFIELDS)) ||
216-
DibIsCMYK((LPCSTR)lpbi))
212+
if (!DibHasAlphaChannel((LPCSTR)lpbi))
217213
{
218214
GlobalUnlock(hDib);
219215
return NULL;
220216
}
221217

222218
LONG lWidth = 0;
223219
LONG lHeight = 0;
224-
GetDIBDimensions((LPCSTR)lpbi, &lWidth, &lHeight);
220+
221+
GetDibDimensions((LPCSTR)lpbi, &lWidth, &lHeight);
225222

226223
BITMAPINFO bmi = { {0} };
227224
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
@@ -244,24 +241,27 @@ HBITMAP CreatePremultipliedBitmap(HANDLE hDib)
244241
return NULL;
245242
}
246243

247-
BYTE cAlpha;
248-
LPBYTE lpSrc, lpDest;
249-
DWORD dwColor, dwRedMask, dwGreenMask, dwBlueMask, dwAlphaMask;
250-
ULONG ulIncrement = WIDTHBYTES(lWidth * wBitCount);
251244
BOOL bHasVisiblePixels = FALSE;
252245
BOOL bHasTransparentPixels = FALSE;
253246

247+
BOOL bIsCore = IS_OS2PM_DIB(lpbi);
248+
WORD wBitCount = bIsCore ? ((LPBITMAPCOREHEADER)lpbi)->bcBitCount : lpbi->biBitCount;
249+
ULONG ulIncrement = WIDTHBYTES(lWidth * wBitCount);
250+
DWORD dwRedMask, dwGreenMask, dwBlueMask, dwAlphaMask;
251+
254252
__try
255253
{
256-
if (!bIsCore && lpbi->biCompression == BI_BITFIELDS)
254+
if (!bIsCore && (lpbi->biCompression == BI_BITFIELDS || lpbi->biCompression == BI_ALPHABITFIELDS))
257255
{
258256
LPDWORD lpdwColorMasks = (LPDWORD)&(((LPBITMAPINFO)lpbi)->bmiColors[0]);
259257
dwRedMask = lpdwColorMasks[0];
260258
dwGreenMask = lpdwColorMasks[1];
261259
dwBlueMask = lpdwColorMasks[2];
262-
dwAlphaMask = lpbi->biSize >= sizeof(BITMAPV3INFOHEADER) ? lpdwColorMasks[3] : 0;
260+
dwAlphaMask = 0x00000000;
261+
if (lpbi->biSize >= sizeof(BITMAPV3INFOHEADER) || lpbi->biCompression == BI_ALPHABITFIELDS)
262+
dwAlphaMask = lpdwColorMasks[3];
263263
}
264-
else if (bIs16Bpp)
264+
else if (wBitCount == 16)
265265
{
266266
dwRedMask = 0x00007C00;
267267
dwGreenMask = 0x000003E0;
@@ -276,30 +276,48 @@ HBITMAP CreatePremultipliedBitmap(HANDLE hDib)
276276
dwAlphaMask = 0xFF000000;
277277
}
278278

279-
if (dwAlphaMask)
279+
if (dwAlphaMask || wBitCount == 64)
280280
{
281281
for (LONG h = 0; h < lHeight; h++)
282282
{
283-
lpSrc = lpDIB + (ULONG_PTR)h * ulIncrement;
284-
lpDest = lpBGRA + (ULONG_PTR)h * lWidth * 4;
283+
LPBYTE lpSrc = lpDIB + (ULONG_PTR)h * ulIncrement;
284+
LPBYTE lpDest = lpBGRA + (ULONG_PTR)h * lWidth * 4;
285285

286286
for (LONG w = 0; w < lWidth; w++)
287287
{
288-
dwColor = MAKELONG(MAKEWORD(lpSrc[0], lpSrc[1]),
289-
bIs16Bpp ? 0 : MAKEWORD(lpSrc[2], lpSrc[3]));
290-
cAlpha = GetColorValue(dwColor, dwAlphaMask);
291-
292-
if (cAlpha != 0x00)
293-
bHasVisiblePixels = TRUE;
294-
if (cAlpha != 0xFF)
295-
bHasTransparentPixels = TRUE;
296-
297-
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwBlueMask), cAlpha);
298-
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwGreenMask), cAlpha);
299-
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwRedMask), cAlpha);
300-
*lpDest++ = cAlpha;
301-
302-
lpSrc += bIs16Bpp ? 2 : 4;
288+
if (wBitCount == 64)
289+
{
290+
LPWORD lpwSrc = (LPWORD)lpSrc;
291+
BYTE cAlpha = SRGB64ToSRGB(lpwSrc[3], FALSE);
292+
293+
if (cAlpha != 0x00)
294+
bHasVisiblePixels = TRUE;
295+
if (cAlpha != 0xFF)
296+
bHasTransparentPixels = TRUE;
297+
298+
*lpDest++ = Mul8Bit(SRGB64ToSRGB(lpwSrc[0]), cAlpha);
299+
*lpDest++ = Mul8Bit(SRGB64ToSRGB(lpwSrc[1]), cAlpha);
300+
*lpDest++ = Mul8Bit(SRGB64ToSRGB(lpwSrc[2]), cAlpha);
301+
*lpDest++ = cAlpha;
302+
}
303+
else
304+
{
305+
DWORD dwColor = MAKELONG(MAKEWORD(lpSrc[0], lpSrc[1]),
306+
wBitCount == 16 ? 0 : MAKEWORD(lpSrc[2], lpSrc[3]));
307+
BYTE cAlpha = GetColorValue(dwColor, dwAlphaMask);
308+
309+
if (cAlpha != 0x00)
310+
bHasVisiblePixels = TRUE;
311+
if (cAlpha != 0xFF)
312+
bHasTransparentPixels = TRUE;
313+
314+
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwBlueMask), cAlpha);
315+
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwGreenMask), cAlpha);
316+
*lpDest++ = Mul8Bit(GetColorValue(dwColor, dwRedMask), cAlpha);
317+
*lpDest++ = cAlpha;
318+
}
319+
320+
lpSrc += wBitCount >> 3;
303321
}
304322
}
305323
}
@@ -835,7 +853,7 @@ HPALETTE CreateDibPalette(HANDLE hDib)
835853

836854
////////////////////////////////////////////////////////////////////////////////////////////////
837855

838-
BOOL GetDIBDimensions(LPCSTR lpbi, LPLONG lplWidth, LPLONG lplHeight, BOOL bAbsolute)
856+
BOOL GetDibDimensions(LPCSTR lpbi, LPLONG lplWidth, LPLONG lplHeight, BOOL bAbsolute)
839857
{
840858
if (lpbi == NULL || lplWidth == NULL || lplHeight == NULL)
841859
return FALSE;
@@ -973,6 +991,36 @@ LPBYTE FindDibBits(LPCSTR lpbi)
973991
return (LPBYTE)lpbi + DibBitsOffset(lpbi);
974992
}
975993

994+
////////////////////////////////////////////////////////////////////////////////////////////////
995+
// When loading an 8-bit BMP file, DirectX can assign a logical palette in which
996+
// the peFlags member is to be interpreted as a single 8-bit alpha value for a
997+
// texture. However, this is not specified for the color table of the file format.
998+
999+
BOOL DibHasAlphaChannel(LPCSTR lpbi)
1000+
{
1001+
LPBITMAPINFOHEADER lpbih = (LPBITMAPINFOHEADER)lpbi;
1002+
if (lpbih == NULL)
1003+
return NULL;
1004+
1005+
BOOL bIsCore = IS_OS2PM_DIB(lpbi);
1006+
WORD wBitCount = bIsCore ? ((LPBITMAPCOREHEADER)lpbi)->bcBitCount : lpbih->biBitCount;
1007+
1008+
// Check whether the bit depth offers space for an alpha component
1009+
if (wBitCount != 16 && wBitCount != 32 && wBitCount != 64)
1010+
return FALSE;
1011+
1012+
// Even without color masks, some programs interpret the unused bits as an alpha channel
1013+
if (bIsCore || lpbih->biCompression == BI_RGB)
1014+
return TRUE;
1015+
1016+
// However, the space unused by the color masks cannot be clearly assigned to an alpha
1017+
// component. Therefore, check whether an alpha channel bit mask is present and set.
1018+
if (lpbih->biSize >= sizeof(BITMAPV3INFOHEADER) || lpbih->biCompression == BI_ALPHABITFIELDS)
1019+
return (*(LPDWORD)&(((LPBITMAPINFO)lpbi)->bmiColors[3]) != 0);
1020+
1021+
return FALSE;
1022+
}
1023+
9761024
////////////////////////////////////////////////////////////////////////////////////////////////
9771025

9781026
BOOL DibHasEmbeddedProfile(LPCSTR lpbi)
@@ -1001,10 +1049,10 @@ BOOL DibHasColorProfile(LPCSTR lpbi)
10011049

10021050
BOOL DibHasColorSpaceData(LPCSTR lpbi)
10031051
{
1004-
if (lpbi == NULL)
1052+
LPBITMAPV5HEADER lpbiv5 = (LPBITMAPV5HEADER)lpbi;
1053+
if (lpbiv5 == NULL)
10051054
return FALSE;
10061055

1007-
LPBITMAPV5HEADER lpbiv5 = (LPBITMAPV5HEADER)lpbi;
10081056
DWORD dwSize = lpbiv5->bV5Size;
10091057

10101058
// We ignore linked profiles because they don't work with ICM inside DC
@@ -1106,13 +1154,35 @@ static BYTE GetColorValue(DWORD dwPixel, DWORD dwMask)
11061154

11071155
DWORD dwColor = dwPixel & dwMask;
11081156

1157+
// Shift the mask and color value left until the highest bit of the mask is set
11091158
while ((dwMask & 0x80000000) == 0)
11101159
{
11111160
dwColor = dwColor << 1;
11121161
dwMask = dwMask << 1;
11131162
}
11141163

1164+
// Return the high-order byte of the color value
11151165
return HIBYTE(HIWORD(dwColor));
11161166
}
11171167

11181168
////////////////////////////////////////////////////////////////////////////////////////////////
1169+
// Transformation from 16-bit sRGB64 values to 8-bit sRGB, as described in ANNEX A.1 of the
1170+
// IEC 61966-2-2 working draft (http://www.colour.org/tc8-05/Docs/colorspace/61966-2-2NPa.pdf)
1171+
1172+
static BYTE SRGB64ToSRGB(WORD wColor, BOOL bUseGammaEncoding)
1173+
{
1174+
wColor = min(max((SHORT)wColor, 0), 8192);
1175+
1176+
if (!bUseGammaEncoding)
1177+
return (BYTE)(255 * wColor / 8192);
1178+
1179+
double fColor = (double)wColor / 8192;
1180+
if (fColor < 0.0031308)
1181+
fColor *= 12.92;
1182+
else
1183+
fColor = pow(fColor, 1.0 / 2.4) * 1.055 - 0.055;
1184+
1185+
return (BYTE)(fColor * 255.0 + 0.5);
1186+
}
1187+
1188+
////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)