/// <summary> /// Fills in the fields of a BITMAPINFO so that we can create a bitmap /// that matches the format of the display. /// /// This is done by creating a compatible bitmap and calling GetDIBits /// to return the color masks. This is done with two calls. The first /// call passes in biBitCount = 0 to GetDIBits which will fill in the /// base BITMAPINFOHEADER data. The second call to GetDIBits (passing /// in the BITMAPINFO filled in by the first call) will return the color /// table or bitmasks, as appropriate. /// </summary> /// <returns>True if successful, false otherwise.</returns> private unsafe bool FillBitmapInfo(IntPtr hdc, IntPtr hpal, ref Interop.Gdi32.BITMAPINFO_FLAT pbmi) { IntPtr hbm = IntPtr.Zero; bool bRet = false; try { // Create a dummy bitmap from which we can query color format info // about the device surface. hbm = Interop.Gdi32.CreateCompatibleBitmap(new HandleRef(null, hdc), 1, 1); if (hbm == IntPtr.Zero) { throw new OutOfMemoryException(SR.GraphicsBufferQueryFail); } pbmi.bmiHeader_biSize = sizeof(NativeMethods.BITMAPINFOHEADER); // Call first time to fill in BITMAPINFO header. Interop.Gdi32.GetDIBits(new HandleRef(null, hdc), new HandleRef(null, hbm), 0, 0, IntPtr.Zero, ref pbmi, NativeMethods.DIB_RGB_COLORS); if (pbmi.bmiHeader_biBitCount <= 8) { bRet = FillColorTable(hdc, hpal, ref pbmi); } else { if (pbmi.bmiHeader_biCompression == NativeMethods.BI_BITFIELDS) { // Call a second time to get the color masks. Interop.Gdi32.GetDIBits(new HandleRef(null, hdc), new HandleRef(null, hbm), 0, pbmi.bmiHeader_biHeight, IntPtr.Zero, ref pbmi, NativeMethods.DIB_RGB_COLORS); } bRet = true; } } finally { if (hbm != IntPtr.Zero) { Interop.Gdi32.DeleteObject(hbm); hbm = IntPtr.Zero; } } return(bRet); }
/// <summary> /// Initialize the color table of the BITMAPINFO pointed to by pbmi. Colors /// are set to the current system palette. /// /// Note: call only valid for displays of 8bpp or less. /// </summary> /// <returns>True is successful, false otherwise.</returns> private unsafe bool FillColorTable(IntPtr hdc, IntPtr hpal, ref Interop.Gdi32.BITMAPINFO_FLAT pbmi) { byte[] aj = new byte[sizeof(NativeMethods.PALETTEENTRY) * 256]; fixed(byte *pcolors = pbmi.bmiColors) { fixed(byte *ppal = aj) { NativeMethods.RGBQUAD * prgb = (NativeMethods.RGBQUAD *)pcolors; NativeMethods.PALETTEENTRY *lppe = (NativeMethods.PALETTEENTRY *)ppal; int cColors = 1 << pbmi.bmiHeader_biBitCount; if (cColors <= 256) { // Note: we don't support 4bpp displays. uint palRet; IntPtr palHalftone = IntPtr.Zero; if (hpal == IntPtr.Zero) { palHalftone = Graphics.GetHalftonePalette(); palRet = Interop.Gdi32.GetPaletteEntries(new HandleRef(null, palHalftone), 0, cColors, aj); } else { palRet = Interop.Gdi32.GetPaletteEntries(new HandleRef(null, hpal), 0, cColors, aj); } if (palRet != 0) { for (int i = 0; i < cColors; i++) { prgb[i].rgbRed = lppe[i].peRed; prgb[i].rgbGreen = lppe[i].peGreen; prgb[i].rgbBlue = lppe[i].peBlue; prgb[i].rgbReserved = 0; } return(true); } } } } return(false); }
/// <summary> /// Create a DIB section with an optimal format w.r.t. the specified hdc. /// /// If DIB <= 8bpp, then the DIB color table is initialized based on the /// specified palette. If the palette handle is NULL, then the system /// palette is used. /// /// Note: The hdc must be a direct DC (not an info or memory DC). /// /// Note: On palettized displays, if the system palette changes the /// UpdateDIBColorTable function should be called to maintain /// the identity palette mapping between the DIB and the display. /// </summary> /// <returns>A valid bitmap handle if successful, IntPtr.Zero otherwise.</returns> private IntPtr CreateCompatibleDIB(IntPtr hdc, IntPtr hpal, int ulWidth, int ulHeight, ref IntPtr ppvBits) { if (hdc == IntPtr.Zero) { throw new ArgumentNullException(nameof(hdc)); } IntPtr hbmRet = IntPtr.Zero; Interop.Gdi32.BITMAPINFO_FLAT pbmi = default; // Validate hdc. Interop.Gdi32.ObjectType objType = Interop.Gdi32.GetObjectType(hdc); switch (objType) { case Interop.Gdi32.ObjectType.OBJ_DC: case Interop.Gdi32.ObjectType.OBJ_METADC: case Interop.Gdi32.ObjectType.OBJ_MEMDC: case Interop.Gdi32.ObjectType.OBJ_ENHMETADC: break; default: throw new ArgumentException(SR.DCTypeInvalid); } if (FillBitmapInfo(hdc, hpal, ref pbmi)) { // Change bitmap size to match specified dimensions. pbmi.bmiHeader_biWidth = ulWidth; pbmi.bmiHeader_biHeight = ulHeight; if (pbmi.bmiHeader_biCompression == NativeMethods.BI_RGB) { pbmi.bmiHeader_biSizeImage = 0; } else { if (pbmi.bmiHeader_biBitCount == 16) { pbmi.bmiHeader_biSizeImage = ulWidth * ulHeight * 2; } else if (pbmi.bmiHeader_biBitCount == 32) { pbmi.bmiHeader_biSizeImage = ulWidth * ulHeight * 4; } else { pbmi.bmiHeader_biSizeImage = 0; } } pbmi.bmiHeader_biClrUsed = 0; pbmi.bmiHeader_biClrImportant = 0; // Create the DIB section. Let Win32 allocate the memory and return // a pointer to the bitmap surface. hbmRet = Interop.Gdi32.CreateDIBSection(new HandleRef(null, hdc), ref pbmi, NativeMethods.DIB_RGB_COLORS, ref ppvBits, IntPtr.Zero, 0); Win32Exception?ex = null; if (hbmRet == IntPtr.Zero) { ex = new Win32Exception(Marshal.GetLastWin32Error()); } if (ex != null) { throw ex; } } return(hbmRet); }