public Composition(int width, int height, ushort bitsPerPixel) { DeviceContext = Gdi32.CreateCompatibleDC(IntPtr.Zero).ThrowWithoutLastErrorAvailableIfInvalid(nameof(Gdi32.CreateCompatibleDC)); bitmap = Gdi32.CreateDIBSection(DeviceContext, new() { bmiHeader = { biSize = Marshal.SizeOf <Gdi32.BITMAPINFOHEADER>(), biWidth = width, biHeight = -height, biPlanes = 1, biBitCount = bitsPerPixel, }, }, Gdi32.DIB.RGB_COLORS, out var compositionPixelDataPointer, hSection: IntPtr.Zero, offset: 0).ThrowLastErrorIfInvalid(); Gdi32.SelectObject(DeviceContext, bitmap).ThrowWithoutLastErrorAvailableIfInvalid(nameof(Gdi32.SelectObject)); unsafe { Pixels = new( (byte *)compositionPixelDataPointer, (uint)width, stride : ((((uint)width * 3) + 3) / 4) * 4, (uint)height); } }
public void Overwrite( Gdi32.DeviceContextSafeHandle bitmapDC, Gdi32.DeviceContextSafeHandle windowDC, int windowClientLeft, int windowClientTop, int windowClientWidth, int windowClientHeight, uint windowDpi, uint zOrder) { if (windowClientWidth > 0 && windowClientHeight > 0) { if (bitmap is null || bitmapWidth < windowClientWidth || bitmapHeight < windowClientHeight) { if (bitmap is null) { // Most of the time, windows don't resize, so save some space by not rounding up. bitmapWidth = windowClientWidth; bitmapHeight = windowClientHeight; } else { // Round up to the nearest 256 pixels to minimize the number of times that bitmaps are // reallocated. bitmapWidth = ((Math.Max(bitmapWidth, windowClientWidth) + 255) / 256) * 256; bitmapHeight = ((Math.Max(bitmapHeight, windowClientHeight) + 255) / 256) * 256; bitmap.Dispose(); } bitmap = Gdi32.CreateDIBSection(windowDC, new() { bmiHeader = { biSize = Marshal.SizeOf <Gdi32.BITMAPINFOHEADER>(), biWidth = bitmapWidth, biHeight = -bitmapHeight, biPlanes = 1, biBitCount = BitsPerPixel, }, }, Gdi32.DIB.RGB_COLORS, ppvBits: out _, hSection: IntPtr.Zero, offset: 0).ThrowLastErrorIfInvalid(); } Gdi32.SelectObject(bitmapDC, bitmap).ThrowWithoutLastErrorAvailableIfInvalid(nameof(Gdi32.SelectObject)); if (!Gdi32.BitBlt(bitmapDC, 0, 0, windowClientWidth, windowClientHeight, windowDC, 0, 0, Gdi32.RasterOperation.SRCCOPY)) { throw new Win32Exception(); } } WindowClientLeft = windowClientLeft; WindowClientTop = windowClientTop; WindowClientWidth = windowClientWidth; WindowClientHeight = windowClientHeight; WindowDpi = windowDpi; ZOrder = zOrder; }
const int CBM_INIT = 0x04;// /* initialize bitmap */ public static IntPtr Create32BppBitmap(Image sourceImage) { BITMAPV5HEADER bi = new BITMAPV5HEADER(); bi.bV5Size = (uint)Marshal.SizeOf(bi); bi.bV5Width = sourceImage.Width; bi.bV5Height = sourceImage.Height; bi.bV5Planes = 1; bi.bV5BitCount = 32; bi.bV5Compression = BI_BITFIELDS; // The following mask specification specifies a supported 32 BPP // alpha format for Windows XP. bi.bV5RedMask = 0x00FF0000; bi.bV5GreenMask = 0x0000FF00; bi.bV5BlueMask = 0x000000FF; bi.bV5AlphaMask = 0xFF000000; IntPtr hdc = User32.GetDC(IntPtr.Zero); IntPtr bits = IntPtr.Zero; // Create the DIB section with an alpha channel. IntPtr hBitmap = Gdi32.CreateDIBSection(hdc, bi, (uint)DIB.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0); var hMemDC = Gdi32.CreateCompatibleDC(hdc); Gdi32.ReleaseDC(IntPtr.Zero, hdc); var sourceBits = ((Bitmap)sourceImage).LockBits( new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var stride = sourceImage.Width * 4; for (int y = 0; y < sourceImage.Height; y++) { IntPtr DstDib = (IntPtr)(bits.ToInt32() + (y * stride)); IntPtr SrcDib = (IntPtr)(sourceBits.Scan0.ToInt32() + ((sourceImage.Height - 1 - y) * stride)); for (int x = 0; x < sourceImage.Width; x++) { Marshal.WriteInt32(DstDib, Marshal.ReadInt32(SrcDib)); DstDib = (IntPtr)(DstDib.ToInt32() + 4); SrcDib = (IntPtr)(SrcDib.ToInt32() + 4); } } return(hBitmap); }
/// <summary> /// Creates a 32 bit HBITMAP of the specified size. /// </summary> /// <param name="hdc">The HDC.</param> /// <param name="size">The size.</param> /// <param name="bits">The bits.</param> /// <param name="hBitmap">The bitmap handle.</param> /// <returns>True if the bitmap was created successfully.</returns> private static bool Create32BitHBITMAP(IntPtr hdc, Size size, out IntPtr bits, out IntPtr hBitmap) { // Create a bitmap info setup for a 32 bit bitmap. var bi = new BITMAPINFO { bmiHeader = new BITMAPINFOHEADER { biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)), biPlanes = 1, biCompression = (uint)BI.BI_RGB, biWidth = size.Width, biHeight = size.Height, biBitCount = 32 } }; // Create the DIB section. hBitmap = Gdi32.CreateDIBSection(hdc, ref bi, (uint)DIB.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0); // Return success only if we have a handle and bitmap bits. return(hBitmap != IntPtr.Zero && bits != IntPtr.Zero); }
public void Other() { var hDc = User32.GetWindowDC(IntPtr.Zero); var hMemDc = Gdi32.CreateCompatibleDC(hDc); var bi = new BitmapInfoHeader(); bi.biSize = (uint)Marshal.SizeOf(bi); bi.biBitCount = 24; //Creating RGB bitmap. The following three members don't matter bi.biClrUsed = 0; bi.biClrImportant = 0; bi.biCompression = 0; bi.biHeight = Height; bi.biWidth = Width; bi.biPlanes = 1; var cb = (int)(bi.biHeight * bi.biWidth * bi.biBitCount / 8); //8 is bits per byte. bi.biSizeImage = (uint)(((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight); //bi.biXPelsPerMeter = XPelsPerMeter; //bi.biYPelsPerMeter = YPelsPerMeter; bi.biXPelsPerMeter = 96; bi.biYPelsPerMeter = 96; var pBits = IntPtr.Zero; //Allocate memory for bitmap bits var pBI = Kernel32.LocalAlloc((uint)LocalMemoryFlags.LPTR, new UIntPtr(bi.biSize)); // Not sure if this needed - simply trying to keep marshaller happy Marshal.StructureToPtr(bi, pBI, false); //This will return IntPtr to actual DIB bits in pBits var hBmp = Gdi32.CreateDIBSection(hDc, ref pBI, 0, out pBits, IntPtr.Zero, 0); //Marshall back - now we have BitmapInfoHeader correctly filled in Marshal.PtrToStructure(pBI, bi); var biNew = (BitmapInfoHeader)Marshal.PtrToStructure(pBI, typeof(BitmapInfoHeader)); //Usual stuff var hOldBitmap = Gdi32.SelectObject(hMemDc, hBmp); //Grab bitmap var nRet = Gdi32.BitBlt(hMemDc, 0, 0, bi.biWidth, bi.biHeight, hDc, Left, Top, CopyPixelOperations.SourceCopy | CopyPixelOperations.CaptureBlt); // Allocate memory for a copy of bitmap bits var realBits = new byte[cb]; // And grab bits from DIBSestion data Marshal.Copy(pBits, realBits, 0, cb); //This simply creates valid bitmap file header, so it can be saved to disk var bfh = new BitmapFileHeader(); bfh.bfSize = (uint)cb + 0x36; // Size of header + size of Native.BitmapInfoHeader size of bitmap bits bfh.bfType = 0x4d42; //BM bfh.bfOffBits = 0x36; // var hdrSize = 14; var header = new byte[hdrSize]; BitConverter.GetBytes(bfh.bfType).CopyTo(header, 0); BitConverter.GetBytes(bfh.bfSize).CopyTo(header, 2); BitConverter.GetBytes(bfh.bfOffBits).CopyTo(header, 10); //Allocate enough memory for complete bitmap file var data = new byte[cb + bfh.bfOffBits]; //BITMAPFILEHEADER header.CopyTo(data, 0); //BitmapInfoHeader header = new byte[Marshal.SizeOf(bi)]; var pHeader = Kernel32.LocalAlloc((uint)LocalMemoryFlags.LPTR, new UIntPtr((uint)Marshal.SizeOf(bi))); Marshal.StructureToPtr(biNew, pHeader, false); Marshal.Copy(pHeader, header, 0, Marshal.SizeOf(bi)); Kernel32.LocalFree(pHeader); header.CopyTo(data, hdrSize); //Bitmap bits realBits.CopyTo(data, (int)bfh.bfOffBits); //Native.SelectObject(_compatibleDeviceContext, _oldBitmap); //Native.DeleteObject(_compatibleBitmap); //Native.DeleteDC(_compatibleDeviceContext); //Native.ReleaseDC(_desktopWindow, _windowDeviceContext); Gdi32.SelectObject(hMemDc, hOldBitmap); Gdi32.DeleteObject(hBmp); Gdi32.DeleteDC(hMemDc); User32.ReleaseDC(IntPtr.Zero, hDc); }
/// <summary> /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, very fast. /// </summary> /// <param name="bitmap">A <see cref="Bitmap"/> with the output bitmap.</param> /// <param name="bitPerPixel">1 or 8, target bits per pixel.</param> /// <returns>A <see cref="Bitmap"/> with a converted copy of the bitmap.</returns> public static Bitmap CopyToBpp(Bitmap bitmap, int bitPerPixel) { if (bitPerPixel != 1 && bitPerPixel != 8) { throw new System.ArgumentException("1 or 8", "bpp"); } // Plan: built into Windows GDI is the ability to convert // bitmaps from one format to another. Most of the time, this // job is actually done by the graphics hardware accelerator card // and so is extremely fast. The rest of the time, the job is done by // very fast native code. // We will call into this GDI functionality from C#. Our plan: // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed) // (2) Create a GDI monochrome hbitmap // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above) // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed) int w = bitmap.Width, h = bitmap.Height; IntPtr hbm = bitmap.GetHbitmap(); // this is step (1) // Step (2): create the monochrome bitmap. // "BITMAPINFO" is an interop-struct which we define below. // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs Gdi32.BITMAPINFO bmi = new Gdi32.BITMAPINFO(); bmi.BiSize = 40; // the size of the BITMAPHEADERINFO struct bmi.BiWidth = w; bmi.BiHeight = h; bmi.BiPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info. bmi.BiBitCount = (short)bitPerPixel; // ie. 1bpp or 8bpp bmi.BiCompression = BIRGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes bmi.BiSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8); bmi.BiXPelsPerMeter = 1000000; // not really important bmi.BiYPelsPerMeter = 1000000; // not really important // Now for the colour table. uint ncols = (uint)1 << bitPerPixel; // 2 colours for 1bpp; 256 colours for 8bpp bmi.BiClrUsed = ncols; bmi.BiClrImportant = ncols; bmi.Cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours if (bitPerPixel == 1) { bmi.Cols[0] = MAKERGB(0, 0, 0); bmi.Cols[1] = MAKERGB(255, 255, 255); } else { for (int i = 0; i < 180; i++) { bmi.Cols[i] = MAKERGB(i, i, i); } } // For 8bpp we've created an palette with just grayscale colours. // You can set up any palette you want here. Here are some possibilities: // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i); // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255}; // for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]); // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization // // Now create the indexed bitmap "hbm0" IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. IntPtr hbm0 = Gdi32.CreateDIBSection( IntPtr.Zero, ref bmi, DIBRGBCOLORS, out bits0, IntPtr.Zero, 0); // Step (3): use GDI's BitBlt function to copy from output hbitmap into monocrhome bitmap // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC". IntPtr sdc = User32.GetDC(IntPtr.Zero); // First we obtain the DC for the screen // Next, create a DC for the output hbitmap IntPtr hdc = Gdi32.CreateCompatibleDC(sdc); Gdi32.SelectObject(hdc, hbm); // and create a DC for the monochrome hbitmap IntPtr hdc0 = Gdi32.CreateCompatibleDC(sdc); Gdi32.SelectObject(hdc0, hbm0); // Now we can do the BitBlt: Gdi32.BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, Gdi32.TernaryRasterOperations.SRCCOPY); // Step (4): convert this monochrome hbitmap back into a Bitmap: Bitmap b0 = Bitmap.FromHbitmap(hbm0); // Finally some cleanup. Gdi32.DeleteDC(hdc); Gdi32.DeleteDC(hdc0); User32.ReleaseDC(IntPtr.Zero, sdc); Gdi32.DeleteObject(hbm); Gdi32.DeleteObject(hbm0); return(b0); }