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