// ported form VB6 (Ctls\PortUtil\StdCtl.cpp:6176)
            private unsafe bool DICopy(IntPtr hdcDest, IntPtr hdcSrc, RECT rect, bool bStretch)
            {
                long i;

                // Get the bitmap from the DC by selecting in a 1x1 pixel temp bitmap
                IntPtr hNullBitmap = SafeNativeMethods.CreateBitmap(1, 1, 1, 1, IntPtr.Zero);

                if (hNullBitmap == IntPtr.Zero)
                {
                    return(false);
                }

                try
                {
                    IntPtr hBitmap = Gdi32.SelectObject(hdcSrc, hNullBitmap);
                    if (hBitmap == IntPtr.Zero)
                    {
                        return(false);
                    }

                    // Restore original bitmap
                    Gdi32.SelectObject(hdcSrc, hBitmap);

                    if (!Gdi32.GetObjectW(hBitmap, out Gdi32.BITMAP bmp))
                    {
                        return(false);
                    }

                    var lpbmi = new Gdi32.BITMAPINFO
                    {
                        bmiHeader = new Gdi32.BITMAPINFOHEADER
                        {
                            biSize        = (uint)sizeof(Gdi32.BITMAPINFOHEADER),
                            biWidth       = bmp.bmWidth,
                            biHeight      = bmp.bmHeight,
                            biPlanes      = 1,
                            biBitCount    = bmp.bmBitsPixel,
                            biCompression = Gdi32.BI.RGB
                        },
                        bmiColors = new byte[Gdi32.BITMAPINFO.MaxColorSize * 4]
                    };

                    // Include the palette for 256 color bitmaps
                    long iColors = 1 << (bmp.bmBitsPixel * bmp.bmPlanes);
                    if (iColors <= 256)
                    {
                        byte[] aj = new byte[sizeof(Gdi32.PALETTEENTRY) * 256];
                        SafeNativeMethods.GetSystemPaletteEntries(hdcSrc, 0, (int)iColors, aj);

                        fixed(byte *pcolors = lpbmi.bmiColors)
                        {
                            fixed(byte *ppal = aj)
                            {
                                Gdi32.RGBQUAD *     prgb = (Gdi32.RGBQUAD *)pcolors;
                                Gdi32.PALETTEENTRY *lppe = (Gdi32.PALETTEENTRY *)ppal;

                                // Convert the palette entries to RGB quad entries
                                for (i = 0; i < (int)iColors; i++)
                                {
                                    prgb[i].rgbRed   = lppe[i].peRed;
                                    prgb[i].rgbBlue  = lppe[i].peBlue;
                                    prgb[i].rgbGreen = lppe[i].peGreen;
                                }
                            }
                        }
                    }

                    // Allocate memory to hold the bitmap bits
                    long   bitsPerScanLine  = bmp.bmBitsPixel * (long)bmp.bmWidth;
                    long   bytesPerScanLine = (bitsPerScanLine + 7) / 8;
                    long   totalBytesReqd   = bytesPerScanLine * bmp.bmHeight;
                    byte[] lpBits           = new byte[totalBytesReqd];

                    // Get the bitmap bits
                    int diRet = Gdi32.GetDIBits(
                        hdcSrc,
                        hBitmap,
                        0,
                        (uint)bmp.bmHeight,
                        lpBits,
                        ref lpbmi,
                        Gdi32.DIB.RGB_COLORS);
                    if (diRet == 0)
                    {
                        return(false);
                    }

                    // Set the destination coordiates depending on whether stretch-to-fit was chosen
                    int xDest, yDest, cxDest, cyDest;
                    if (bStretch)
                    {
                        xDest  = rect.left;
                        yDest  = rect.top;
                        cxDest = rect.right - rect.left;
                        cyDest = rect.bottom - rect.top;
                    }
                    else
                    {
                        xDest  = rect.left;
                        yDest  = rect.top;
                        cxDest = bmp.bmWidth;
                        cyDest = bmp.bmHeight;
                    }

                    // Paint the bitmap
                    int iRet = Gdi32.StretchDIBits(
                        hdcDest,
                        xDest,
                        yDest,
                        cxDest,
                        cyDest,
                        0,
                        0,
                        bmp.bmWidth,
                        bmp.bmHeight,
                        lpBits,
                        ref lpbmi,
                        Gdi32.DIB.RGB_COLORS,
                        Gdi32.ROP.SRCCOPY);
                    if (iRet == NativeMethods.GDI_ERROR)
                    {
                        return(false);
                    }
                }
                finally
                {
                    Gdi32.DeleteObject(hNullBitmap);
                }

                return(true);
            }
Beispiel #2
0
        /// <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);
        }