Fix for CreateDIBitmap: Don't create monochrome bitmaps
Michael Kaufmann
hallo at michael-kaufmann.ch
Wed Aug 25 19:17:52 CDT 2004
This patch fixes a bug in CreateDIBitmap and changes all WINE code that
is relying on this bug. According to MSDN, CreateDIBitmap should create
a bitmap that has the same depth as the reference DC. It should not
return monochrome bitmaps, even if the DIB is black & white.
I have created a new function "is_dib_monochrome". I need this function
in two files (cursoricon.c and enhmetafile.c), and I don't know how to
share this code so I have put it in both files. I noticed that this also
happened for two other functions in cursoricon.c / dib.c:
get_bitmap_width_bytes() == DIB_GetDIBWidthBytes(),
bitmap_info_size() == DIB_BitmapInfoSize()
I have replaced all code that depended on the bug. If you write new code
and you want to create a monochrome bitmap, first check if it's possible
with is_dib_monochrome, then create the bitmap with CreateBitmap and
finally set its bits with SetDIBits.
Changelog:
Michael Kaufmann <hallo at michael-kaufmann.ch>:
Don't create monochrome bitmaps in CreateDIBitmap()
-------------- next part --------------
Index: dlls/gdi/dib.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/dib.c,v
retrieving revision 1.3
diff -u -r1.3 dib.c
--- dlls/gdi/dib.c 12 Aug 2004 20:02:39 -0000 1.3
+++ dlls/gdi/dib.c 25 Aug 2004 23:06:54 -0000
@@ -773,13 +773,15 @@
/***********************************************************************
* CreateDIBitmap (GDI32.@)
+ *
+ * Creates a DDB (device dependent bitmap) from a DIB.
+ * The DDB will have the same color depth as the reference DC.
*/
HBITMAP WINAPI CreateDIBitmap( HDC hdc, const BITMAPINFOHEADER *header,
DWORD init, LPCVOID bits, const BITMAPINFO *data,
UINT coloruse )
{
HBITMAP handle;
- BOOL fColor;
DWORD width;
int height;
WORD bpp;
@@ -789,79 +791,10 @@
if (DIB_GetBitmapInfo( header, &width, &height, &bpp, &compr ) == -1) return 0;
if (height < 0) height = -height;
- /* Check if we should create a monochrome or color bitmap. */
- /* We create a monochrome bitmap only if it has exactly 2 */
- /* colors, which are black followed by white, nothing else. */
- /* In all other cases, we create a color bitmap. */
-
- if (bpp != 1) fColor = TRUE;
- else if ((coloruse != DIB_RGB_COLORS) || !data) fColor = FALSE;
- else
- {
- if (data->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
- {
- RGBQUAD *rgb = data->bmiColors;
- DWORD col = RGB( rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue );
-
- /* Check if the first color of the colormap is black */
- if ((col == RGB(0,0,0)))
- {
- rgb++;
- col = RGB( rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue );
- /* If the second color is white, create a monochrome bitmap */
- fColor = (col != RGB(0xff,0xff,0xff));
- }
- /* Note : If the first color of the colormap is white
- followed by black, we have to create a color bitmap.
- If we don't the white will be displayed in black later on!*/
- else fColor = TRUE;
- }
- else if (data->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
- {
- RGBTRIPLE *rgb = ((BITMAPCOREINFO *)data)->bmciColors;
- DWORD col = RGB( rgb->rgbtRed, rgb->rgbtGreen, rgb->rgbtBlue );
- if ((col == RGB(0,0,0)))
- {
- rgb++;
- col = RGB( rgb->rgbtRed, rgb->rgbtGreen, rgb->rgbtBlue );
- fColor = (col != RGB(0xff,0xff,0xff));
- }
- else fColor = TRUE;
- }
- else if (data->bmiHeader.biSize == sizeof(BITMAPV4HEADER))
- { /* FIXME: correct ? */
- RGBQUAD *rgb = data->bmiColors;
- DWORD col = RGB( rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue );
-
- /* Check if the first color of the colormap is black */
- if ((col == RGB(0,0,0)))
- {
- rgb++;
- col = RGB( rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue );
- /* If the second color is white, create a monochrome bitmap */
- fColor = (col != RGB(0xff,0xff,0xff));
- }
- /* Note : If the first color of the colormap is white
- followed by black, we have to create a color bitmap.
- If we don't the white will be displayed in black later on!*/
- else fColor = TRUE;
- }
- else
- {
- ERR("(%ld): wrong/unknown size for data\n",
- data->bmiHeader.biSize );
- return 0;
- }
- }
-
- /* Now create the bitmap */
-
if (!(dc = DC_GetDCPtr( hdc ))) return 0;
- if (fColor)
- handle = CreateBitmap( width, height, GetDeviceCaps( hdc, PLANES ),
- GetDeviceCaps( hdc, BITSPIXEL ), NULL );
- else handle = CreateBitmap( width, height, 1, 1, NULL );
+ handle = CreateBitmap( width, height, GetDeviceCaps( hdc, PLANES ),
+ GetDeviceCaps( hdc, BITSPIXEL ), NULL );
if (handle)
{
Index: dlls/gdi/enhmetafile.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/enhmetafile.c,v
retrieving revision 1.1
diff -u -r1.1 enhmetafile.c
--- dlls/gdi/enhmetafile.c 21 Jul 2004 04:07:28 -0000 1.1
+++ dlls/gdi/enhmetafile.c 25 Aug 2004 23:06:55 -0000
@@ -196,6 +196,55 @@
return NULL;
}
+/***********************************************************************
+ * is_dib_monochrome
+ *
+ * Returns whether a DIB can be converted to a monochrome DDB.
+ *
+ * A DIB can be converted if its color table contains only black and
+ * white. Black must be the first color in the color table.
+ *
+ * Note : If the first color in the color table is white followed by
+ * black, we can't convert it to a monochrome DDB with
+ * SetDIBits, because black and white would be inverted.
+ */
+static BOOL is_dib_monochrome( const BITMAPINFO* info )
+{
+ if (info->bmiHeader.biBitCount != 1) return FALSE;
+
+ if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
+ {
+ RGBTRIPLE *rgb = ((BITMAPCOREINFO *) info)->bmciColors;
+
+ /* Check if the first color is black */
+ if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
+ {
+ rgb++;
+
+ /* Check if the second color is white */
+ return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
+ && (rgb->rgbtBlue == 0xff));
+ }
+ else return FALSE;
+ }
+ else /* assume BITMAPINFOHEADER */
+ {
+ RGBQUAD *rgb = info->bmiColors;
+
+ /* Check if the first color is black */
+ if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
+ (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
+ {
+ rgb++;
+
+ /* Check if the second color is white */
+ return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
+ && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
+ }
+ else return FALSE;
+ }
+}
+
/****************************************************************************
* EMF_Create_HENHMETAFILE
*/
@@ -1617,11 +1666,30 @@
case EMR_CREATEMONOBRUSH:
{
- PEMRCREATEMONOBRUSH pCreateMonoBrush = (PEMRCREATEMONOBRUSH)mr;
- BITMAPINFO *pbi = (BITMAPINFO *)((BYTE *)mr + pCreateMonoBrush->offBmi);
- HBITMAP hBmp = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
- (BYTE *)mr + pCreateMonoBrush->offBits, pbi, pCreateMonoBrush->iUsage);
+ PEMRCREATEMONOBRUSH pCreateMonoBrush = (PEMRCREATEMONOBRUSH)mr;
+ BITMAPINFO *pbi = (BITMAPINFO *)((BYTE *)mr + pCreateMonoBrush->offBmi);
+ HBITMAP hBmp;
+
+ /* Need to check if the bitmap is monochrome, and if the
+ two colors are really black and white */
+ if (is_dib_monochrome(pbi))
+ {
+ /* Top-down DIBs have a negative height */
+ DWORD height = pbi->bmiHeader.biHeight;
+ if (height < 0) height = -height;
+
+ hBmp = CreateBitmap(pbi->bmiHeader.biWidth, height, 1, 1, NULL);
+ SetDIBits(hdc, hBmp, 0, pbi->bmiHeader.biHeight,
+ (BYTE *)mr + pCreateMonoBrush->offBits, pbi, pCreateMonoBrush->iUsage);
+ }
+ else
+ {
+ hBmp = CreateDIBitmap(hdc, (BITMAPINFOHEADER *)pbi, CBM_INIT,
+ (BYTE *)mr + pCreateMonoBrush->offBits, pbi, pCreateMonoBrush->iUsage);
+ }
+
(handletable->objectHandle)[pCreateMonoBrush->ihBrush] = CreatePatternBrush(hBmp);
+
/* CreatePatternBrush created a copy of the bitmap */
DeleteObject(hBmp);
break;
@@ -1650,7 +1718,7 @@
SelectObject(hdcSrc, hBrushOld);
DeleteObject(hBrush);
- hBmp = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
+ hBmp = CreateDIBitmap(hdcSrc, (BITMAPINFOHEADER *)pbi, CBM_INIT,
(BYTE *)mr + pBitBlt->offBitsSrc, pbi, pBitBlt->iUsageSrc);
hBmpOld = SelectObject(hdcSrc, hBmp);
@@ -1692,7 +1760,7 @@
SelectObject(hdcSrc, hBrushOld);
DeleteObject(hBrush);
- hBmp = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
+ hBmp = CreateDIBitmap(hdcSrc, (BITMAPINFOHEADER *)pbi, CBM_INIT,
(BYTE *)mr + pStretchBlt->offBitsSrc, pbi, pStretchBlt->iUsageSrc);
hBmpOld = SelectObject(hdcSrc, hBmp);
@@ -1726,11 +1794,13 @@
DeleteObject(hBrush);
pbi = (BITMAPINFO *)((BYTE *)mr + pMaskBlt->offBmiMask);
- hBmpMask = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
- (BYTE *)mr + pMaskBlt->offBitsMask, pbi, pMaskBlt->iUsageMask);
+ hBmpMask = CreateBitmap(pbi->bmiHeader.biWidth, pbi->bmiHeader.biHeight,
+ 1, 1, NULL);
+ SetDIBits(hdc, hBmpMask, 0, pbi->bmiHeader.biHeight,
+ (BYTE *)mr + pMaskBlt->offBitsMask, pbi, pMaskBlt->iUsageMask);
pbi = (BITMAPINFO *)((BYTE *)mr + pMaskBlt->offBmiSrc);
- hBmp = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
+ hBmp = CreateDIBitmap(hdc, (BITMAPINFOHEADER *)pbi, CBM_INIT,
(BYTE *)mr + pMaskBlt->offBitsSrc, pbi, pMaskBlt->iUsageSrc);
hBmpOld = SelectObject(hdcSrc, hBmp);
MaskBlt(hdc,
@@ -1776,11 +1846,13 @@
DeleteObject(hBrush);
pbi = (BITMAPINFO *)((BYTE *)mr + pPlgBlt->offBmiMask);
- hBmpMask = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
- (BYTE *)mr + pPlgBlt->offBitsMask, pbi, pPlgBlt->iUsageMask);
+ hBmpMask = CreateBitmap(pbi->bmiHeader.biWidth, pbi->bmiHeader.biHeight,
+ 1, 1, NULL);
+ SetDIBits(hdc, hBmpMask, 0, pbi->bmiHeader.biHeight,
+ (BYTE *)mr + pPlgBlt->offBitsMask, pbi, pPlgBlt->iUsageMask);
pbi = (BITMAPINFO *)((BYTE *)mr + pPlgBlt->offBmiSrc);
- hBmp = CreateDIBitmap(0, (BITMAPINFOHEADER *)pbi, CBM_INIT,
+ hBmp = CreateDIBitmap(hdcSrc, (BITMAPINFOHEADER *)pbi, CBM_INIT,
(BYTE *)mr + pPlgBlt->offBitsSrc, pbi, pPlgBlt->iUsageSrc);
hBmpOld = SelectObject(hdcSrc, hBmp);
PlgBlt(hdc,
Index: windows/cursoricon.c
===================================================================
RCS file: /home/wine/wine/windows/cursoricon.c,v
retrieving revision 1.71
diff -u -r1.71 cursoricon.c
--- windows/cursoricon.c 12 Feb 2004 00:35:01 -0000 1.71
+++ windows/cursoricon.c 25 Aug 2004 23:07:02 -0000
@@ -224,6 +224,56 @@
}
+/***********************************************************************
+ * is_dib_monochrome
+ *
+ * Returns whether a DIB can be converted to a monochrome DDB.
+ *
+ * A DIB can be converted if its color table contains only black and
+ * white. Black must be the first color in the color table.
+ *
+ * Note : If the first color in the color table is white followed by
+ * black, we can't convert it to a monochrome DDB with
+ * SetDIBits, because black and white would be inverted.
+ */
+static BOOL is_dib_monochrome( const BITMAPINFO* info )
+{
+ if (info->bmiHeader.biBitCount != 1) return FALSE;
+
+ if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
+ {
+ RGBTRIPLE *rgb = ((BITMAPCOREINFO *) info)->bmciColors;
+
+ /* Check if the first color is black */
+ if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
+ {
+ rgb++;
+
+ /* Check if the second color is white */
+ return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
+ && (rgb->rgbtBlue == 0xff));
+ }
+ else return FALSE;
+ }
+ else /* assume BITMAPINFOHEADER */
+ {
+ RGBQUAD *rgb = info->bmiColors;
+
+ /* Check if the first color is black */
+ if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
+ (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
+ {
+ rgb++;
+
+ /* Check if the second color is white */
+ return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
+ && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
+ }
+ else return FALSE;
+ }
+}
+
+
/**********************************************************************
* CURSORICON_FindSharedIcon
*/
@@ -657,8 +707,17 @@
}
if (!res) { DeleteObject(hXorBits); hXorBits = 0; }
}
- } else hXorBits = CreateDIBitmap( screen_dc, &pInfo->bmiHeader,
- CBM_INIT, (char*)bmi + size, pInfo, DIB_RGB_COLORS );
+ } else {
+ if (is_dib_monochrome(bmi)) {
+ hXorBits = CreateBitmap(width, height, 1, 1, NULL);
+ SetDIBits(screen_dc, hXorBits, 0, height,
+ (char*)bmi + size, pInfo, DIB_RGB_COLORS);
+ }
+ else
+ hXorBits = CreateDIBitmap(screen_dc, &pInfo->bmiHeader,
+ CBM_INIT, (char*)bmi + size, pInfo, DIB_RGB_COLORS);
+ }
+
if( hXorBits )
{
char* xbits = (char *)bmi + size +
@@ -700,9 +759,13 @@
}
if (!res) { DeleteObject(hAndBits); hAndBits = 0; }
}
- } else hAndBits = CreateDIBitmap( screen_dc, &pInfo->bmiHeader,
- CBM_INIT, xbits, pInfo, DIB_RGB_COLORS );
+ } else {
+ hAndBits = CreateBitmap(width, height, 1, 1, NULL);
+
+ if (hAndBits) SetDIBits(screen_dc, hAndBits, 0, height,
+ xbits, pInfo, DIB_RGB_COLORS);
+ }
if( !hAndBits ) DeleteObject( hXorBits );
}
HeapFree( GetProcessHeap(), 0, pInfo );
@@ -1170,7 +1233,7 @@
iinfo.hbmColor = CreateDIBitmap( hdc, &bmi.bmiHeader,
CBM_INIT, lpXORbits,
&bmi, DIB_RGB_COLORS );
-
+
hIcon=CreateIconIndirect(&iinfo);
DeleteObject(iinfo.hbmMask);
DeleteObject(iinfo.hbmColor);
@@ -1977,7 +2040,7 @@
/**********************************************************************
* BITMAP_Load
*/
-static HBITMAP BITMAP_Load( HINSTANCE instance,LPCWSTR name, UINT loadflags )
+static HBITMAP BITMAP_Load( HINSTANCE instance, LPCWSTR name, UINT loadflags )
{
HBITMAP hbitmap = 0;
HRSRC hRsrc;
@@ -1995,6 +2058,7 @@
if (HIWORD(name)) return 0;
instance = user32_module;
}
+
if (!(hRsrc = FindResourceW( instance, name, (LPWSTR)RT_BITMAP ))) return 0;
if (!(handle = LoadResource( instance, hRsrc ))) return 0;
@@ -2005,8 +2069,10 @@
if (!(ptr = map_fileW( name ))) return 0;
info = (BITMAPINFO *)(ptr + sizeof(BITMAPFILEHEADER));
}
+
size = bitmap_info_size(info, DIB_RGB_COLORS);
if ((hFix = GlobalAlloc(0, size))) fix_info=GlobalLock(hFix);
+
if (fix_info) {
BYTE pix;
@@ -2014,9 +2080,11 @@
pix = *((LPBYTE)info + size);
DIB_FixColorsToLoadflags(fix_info, loadflags, pix);
if (!screen_dc) screen_dc = CreateDCA( "DISPLAY", NULL, NULL, NULL );
+
if (screen_dc)
{
char *bits = (char *)info + size;
+
if (loadflags & LR_CREATEDIBSECTION) {
DIBSECTION dib;
hbitmap = CreateDIBSection(screen_dc, fix_info, DIB_RGB_COLORS, NULL, 0, 0);
@@ -2025,14 +2093,28 @@
DIB_RGB_COLORS);
}
else {
- hbitmap = CreateDIBitmap( screen_dc, &fix_info->bmiHeader, CBM_INIT,
- bits, fix_info, DIB_RGB_COLORS );
+ /* If it's possible, create a monochrome bitmap */
+
+ DWORD height = fix_info->bmiHeader.biHeight;
+ if (height < 0) height = -height;
+
+ if (is_dib_monochrome(fix_info))
+ hbitmap = CreateBitmap(fix_info->bmiHeader.biWidth, height, 1, 1, NULL);
+ else
+ hbitmap = CreateBitmap(fix_info->bmiHeader.biWidth, height,
+ GetDeviceCaps(screen_dc, PLANES),
+ GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
+
+ SetDIBits(screen_dc, hbitmap, 0, height, bits, info, DIB_RGB_COLORS);
}
}
+
GlobalUnlock(hFix);
GlobalFree(hFix);
}
+
if (loadflags & LR_LOADFROMFILE) UnmapViewOfFile( ptr );
+
return hbitmap;
}
More information about the wine-patches
mailing list