2323
2424// Determines the value of a color component using a color mask
2525BYTE 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
9781026BOOL DibHasEmbeddedProfile (LPCSTR lpbi)
@@ -1001,10 +1049,10 @@ BOOL DibHasColorProfile(LPCSTR lpbi)
10011049
10021050BOOL 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