【问题标题】:How to get the bitmap/image from a Graphics object in C#?如何从 C# 中的 Graphics 对象获取位图/图像?
【发布时间】:2011-07-15 13:37:39
【问题描述】:

我想知道 Graphics 对象正在绘制一些东西的缓冲区的中间状态。如何获取位图或它正在绘制的图像?

【问题讨论】:

  • 如果它适合你,不要忘记将其标记为已接受............
  • @PranayRana:没有一个答案能满足提问者的需求;相反,恰恰相反。

标签: c# .net graphics bitmap system.drawing


【解决方案1】:

我不确定我是否理解您的要求,因为您的问题非常不清楚。

如果您想知道如何将Graphics 对象的内容保存到位图,那么答案是没有直接的方法可以这样做。在Graphics 对象上绘图是一种单向操作。

更好的选择是创建一个新的Bitmap 对象,为该位图获取一个Graphics 对象,然后直接在其上绘图。以下代码是您可以如何执行此操作的示例:

// Create a new bitmap object
using (Bitmap bmp = new Bitmap(200, 300))
{
    // Obtain a Graphics object from that bitmap
    using (Graphics g = Graphics.FromImage(bmp))
    {
        // Draw onto the bitmap here
        // ....
        g.DrawRectangle(Pens.Red, 10, 10, 50, 50);
    }

    // Save the bitmap to a file on disk, or do whatever else with it
    // ...
    bmp.Save("C:\\MyImage.bmp");
}

【讨论】:

    【解决方案2】:

    此代码适用于我正在转换图像 >> 位图 >> 字节 >> Base64 字符串。

    System.Drawing.Image originalImage = //your image
    
    //Create empty bitmap image of original size
    
    Bitmap tempBmp = new Bitmap(originalImage.Width, originalImage.Height);
    
    Graphics g = Graphics.FromImage(tempBmp);
    
    //draw the original image on tempBmp
    
    g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);
    
    //dispose originalImage and Graphics so the file is now free
    
    g.Dispose();
    
    originalImage.Dispose();
    
    using (MemoryStream ms = new MemoryStream())
    {
        // Convert Image to byte[]
        tempBmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
        //dpgraphic.image.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
        byte[] imageBytes = ms.ToArray();
    
        // Convert byte[] to Base64 String
        string strImage = Convert.ToBase64String(imageBytes);
        sb.AppendFormat(strImage);
    }
    

    【讨论】:

    • 我不太确定您为什么将图像转换为 Base64 字符串?我承认这个问题不是特别清楚,但我没有看到其中提到 anywhere
    • 是的,当然我们可以使用 Graphics.FromImage() 方法从位图中获取 Graphics 对象。我正在寻找的是一些 API,它可以让我从位图中获得图像!
    【解决方案3】:

    由于 9 年后没有人回答实际问题......

    // System.Windows.Forms.Internal.IntUnsafeNativeMethods
    [DllImport("gdi32.dll", CharSet = CharSet.Auto, EntryPoint = "GetCurrentObject", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr IntGetCurrentObject(HandleRef hDC, int uObjectType);
    
    
    IntPtr hdc = graphics.GetHdc();
    // This is a HBITMAP, which is the actual buffer that is being drawn to by hdc.
    IntPtr hbitmap = IntGetCurrentObject(new HandleRef(null, hdc), 7 /*OBJ_BITMAP*/);
    // You can create a Gdiplus::Bitmap object from this, but it will copy all image data.
    //Bitmap bitmap = Image.FromHbitmap(hbitmap);
    // To manipulate the actual bitmap pixel data directly, see below.
    
    // Put these in finally:
    //bitmap.Dispose();
    // NOTE: You cannot use the graphics object before ReleaseHdc is called.
    graphics.ReleaseHdc(hdc);
    

    要了解实际的位图位,您必须首先了解 GDI 位图。有所谓的设备相关位图(DDB,或者通常只是API 中的“位图”)和设备无关位图 (DIB)。对差异的完整解释超出了此答案的范围。

    如果使用this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);,那么OnPaint中的图形对象将使用DIB,否则将使用DDB。

    如果您的HBITMAPDDB,则您无法直接读取/写入像素数据(即使技术上可行,Windows 也无法做到这一点)。您必须使用GetDIBits 以使用特定格式将它们复制到与设备无关的缓冲区,然后使用SetDIBits 将它们复制回来。

    如果您的HBITMAPDIB,那么您可以获得实际的像素位(作为指针),并使用GetObject 直接在内存中读/写它们(而不是与GetCurrentObject混淆):

    [DllImport("gdi32.dll")]
    static extern unsafe int GetObject(IntPtr hobj, int cb, void* data);
    
    [StructLayout(LayoutKind.Sequential)]
    unsafe struct BITMAP
    {
        int        bmType;
        int        bmWidth;
        int        bmHeight;
        int        bmWidthBytes;
        ushort     bmPlanes;
        ushort     bmBitsPixel;
        void*      bmBits;
    }
    
    BITMAP BitmapDesc = new BITMAP();
    GetObject(hbitmap, sizeof(BITMAP), &BitmapDesc);
    

    如果是 DDB,BITMAP.bmBits 将是 null,如果是 DIB,它将是有效的内存地址。如果只想复制这个数据,可以直接使用bmBits;总长度为bmHeight * bmWidthBytes

    如果你真的想操作内存中的像素数据,你需要知道 DIB 的确切像素格式才能正确操作它。像素格式有很多可能性(每像素的位数 1/4/8/16/24/32、RGB 与 BGR、调色板等)。 如果你真的想支持一切,这将是一项艰巨的工作

    要做到这一点,要知道当给定HBITMAP 时,GetObject 函数将接受BITMAP 结构(如上面的代码示例所示)或DIBSECTION 结构。注意DIBSECTIONBITMAP 开头,这使得这两个结构兼容。如果HBITMAP 是一个DIB,那么GetObject 将填写一个有效的(非空)bmBits 指针,它还将填写DIBSECTIONBITMAPINFOHEADER 结构,然后您可以使用它检查 DIB 的像素格式。 检查BITMAPINFOHEADER 会很痛苦。

    【讨论】:

      【解决方案4】:

      这里不是100%确定你想要什么,但是如果你想用图形类来绘制,然后保存到文件,你必须从一个Bitmap文件中获取Graphics对象,然后保存位图完毕。你可以这样做:

        Bitmap bitmap = new Bitmap(bWidth, bHeight);
        Graphics g = Graphics.FromImage(bitmap);
        //do all your operations on g here.
        bitmap.Save(fileName, imageFormat);
      

      【讨论】:

        【解决方案5】:

        你看过this MSDN article吗?它描述了 Bitmap 类,这是一个用于处理由像素数据定义的图像的对象。 System.Drawing.Image 为它提供了额外的功能。高温

        【讨论】:

          【解决方案6】:

          你可以得到他的hdc,它是一个指向表面缓冲区的指针,并最终将他的内容复制到另一个使用bitblt函数的hdc。这样您就可以在位图上创建绘图表面的副本。

          enum TernaryRasterOperations : uint
          {
              /// <summary>dest = source</summary>
              SRCCOPY = 0x00CC0020,
              /// <summary>dest = source OR dest</summary>
              SRCPAINT = 0x00EE0086,
              /// <summary>dest = source AND dest</summary>
              SRCAND = 0x008800C6,
              /// <summary>dest = source XOR dest</summary>
              SRCINVERT = 0x00660046,
              /// <summary>dest = source AND (NOT dest)</summary>
              SRCERASE = 0x00440328,
              /// <summary>dest = (NOT source)</summary>
              NOTSRCCOPY = 0x00330008,
              /// <summary>dest = (NOT src) AND (NOT dest)</summary>
              NOTSRCERASE = 0x001100A6,
              /// <summary>dest = (source AND pattern)</summary>
              MERGECOPY = 0x00C000CA,
              /// <summary>dest = (NOT source) OR dest</summary>
              MERGEPAINT = 0x00BB0226,
              /// <summary>dest = pattern</summary>
              PATCOPY = 0x00F00021,
              /// <summary>dest = DPSnoo</summary>
              PATPAINT = 0x00FB0A09,
              /// <summary>dest = pattern XOR dest</summary>
              PATINVERT = 0x005A0049,
              /// <summary>dest = (NOT dest)</summary>
              DSTINVERT = 0x00550009,
              /// <summary>dest = BLACK</summary>
              BLACKNESS = 0x00000042,
              /// <summary>dest = WHITE</summary>
              WHITENESS = 0x00FF0062,
              /// <summary>
              /// Capture window as seen on screen.  This includes layered windows 
              /// such as WPF windows with AllowsTransparency="true"
              /// </summary>
              CAPTUREBLT = 0x40000000
          }
          
          [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)]
          [return: MarshalAs(UnmanagedType.Bool)]
          static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
          
          public static Bitmap CopyGraphicsContent(Graphics source, Rectangle rect)
          {
              Bitmap bmp = new Bitmap(rect.Width, rect.Height);
          
              using (Graphics dest = Graphics.FromImage(bmp))
              {
                  IntPtr hdcSource = source.GetHdc();
                  IntPtr hdcDest = dest.GetHdc();
          
                  BitBlt(hdcDest, 0, 0, rect.Width, rect.Height, hdcSource, rect.X, rect.Y, TernaryRasterOperations.SRCCOPY);
          
                  source.ReleaseHdc(hdcSource);
                  dest.ReleaseHdc(hdcDest);
              }
          
              return bmp;
          }
          

          【讨论】:

            【解决方案7】:

            @dialer 迄今为止在此线程中有最好的答案。作为一个附加示例,这里是如何在 C# 中将位从 Graphics 或任何 HWND 获取到 Emgu.CV Mat 中。

                struct BITMAP
                {
                    public Int32 bmType;
                    public Int32 bmWidth;
                    public Int32 bmHeight;
                    public Int32 bmWidthBytes;
                    public Int16 bmPlanes;
                    public Int16 bmBitsPixel;
                    public IntPtr bmBits;
                }
            
                [StructLayout(LayoutKind.Sequential, Pack = 4)]
                struct BITMAPINFOHEADER
                {
                    public int biSize;
                    public int biWidth;
                    public int biHeight;
                    public Int16 biPlanes;
                    public Int16 biBitCount;
                    public int biCompression;
                    public int biSizeImage;
                    public int biXPelsPerMeter;
                    public int biYPelsPerMeter;
                    public int biClrUsed;
                    public int bitClrImportant;
                }
            
                [DllImport("user32.dll", SetLastError=true)]
                static extern IntPtr GetDC(IntPtr hWnd);
            
                // System.Windows.Forms.Internal.IntUnsafeNativeMethods
                [DllImport("gdi32.dll", CharSet = CharSet.Auto, EntryPoint = "GetCurrentObject", ExactSpelling = true, SetLastError = true)]
                static extern IntPtr IntGetCurrentObject(HandleRef hDC, int uObjectType);
                
                [DllImport("gdi32.dll", CharSet = CharSet.Auto, EntryPoint = "GetObject")]
                static extern int GetObjectBitmap(IntPtr hObject, int nCount, ref BITMAP lpObject);
            
                [DllImport("gdi32.dll", EntryPoint = "GetDIBits")]
                static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, int uStartScan, int cScanLines, 
                                            IntPtr lpvBits, ref BITMAPINFOHEADER lpbi, int uUsage);
            
                /// <summary>Gets GDI HDC as an Emgu.CV.Mat image as BGRA</summary>
                /// <param name="hdc">GDI HDC</param>
                /// <param name="destination">Destination Mat which will receive the window contents image</param>
                /// <param name="verticalFlip">If TRUE, pixel will be flipped vertically</param>
                /// <returns>TRUE if image was copied successfully</returns>
                public static bool GetHdcAsMat(IntPtr hdc, ref Mat destination, bool verticalFlip)
                {
                    try
                    {
                        // This is a HBITMAP, which is the actual buffer that is being drawn to by hdc.
                        IntPtr hbitmap = IntGetCurrentObject(new HandleRef(null, hdc), 7 /*OBJ_BITMAP*/);
            
                        // Get width, height and the address of the pixel data for the native HBitmap
                        BITMAP info = new BITMAP();
                        if (0 == GetObjectBitmap(hbitmap, Marshal.SizeOf(info), ref info))
                            return false;
            
                        // if the image is a DIB, we can copy the bits directly from bmBits
                        if (info.bmBits != IntPtr.Zero)
                        {
                            // data view of the DIB bits, no allocations
                            Mat view = new Mat(info.bmHeight, info.bmWidth, DepthType.Cv8U, 4, 
                                               info.bmBits, info.bmWidth * 4);
            
                            if (verticalFlip) // copy flipped:
                                CvInvoke.Flip(view, destination, FlipType.Vertical);
                            else // copy directly:
                                view.CopyTo(destination); // automatically resize destination
                            return true;
                        }
            
                        // otherwise, use GetDIBits to get the bitmap from the GPU
                        // a copy is always needed to get the data from GPU to system memory
            
                        if (destination.Width != info.bmWidth ||
                            destination.Height != info.bmHeight)
                        {
                            destination.Dispose();
                            destination = new Mat(info.bmHeight, info.bmWidth, DepthType.Cv8U, 4);
                        }
            
                        var desired = new BITMAPINFOHEADER();
                        desired.biSize = Marshal.SizeOf(desired);
                        desired.biWidth = info.bmWidth;
                        desired.biHeight = verticalFlip ? -info.bmHeight : info.bmHeight;
                        desired.biPlanes = 1;
                        desired.biBitCount = info.bmBitsPixel;
            
                        // Copy bits into destination
                        IntPtr dest = destination.DataPointer;
                        return 0 != GetDIBits(hdc, hbitmap, 0, destination.Height, dest, ref desired, 0);
                    }
                    catch
                    {
                        return false;
                    }
                }
                
                /// <summary>Gets window contents as an Emgu.CV.Mat image as BGRA</summary>
                /// <param name="hwnd">Handle to desired window</param>
                /// <param name="destination">Destination Mat which will receive the window contents image</param>
                /// <param name="verticalFlip">If TRUE, pixel will be flipped vertically</param>
                /// <returns>TRUE if image was copied successfully</returns>
                public static bool GetWindowAsMat(IntPtr hwnd, ref Mat destination, bool verticalFlip)
                {
                    IntPtr hdc = GetDC(hwnd); // private DC does not need to be released
                    return GetHdcAsMat(hdc, ref destination, verticalFlip);
                }
            
                /// <summary>Gets GDI Graphics contents as an Emgu.CV.Mat image as BGRA</summary>
                /// <param name="graphics">.NET GDI Graphics instance</param>
                /// <param name="destination">Destination Mat which will receive the window contents image</param>
                /// <param name="verticalFlip">If TRUE, pixel will be flipped vertically</param>
                /// <returns>TRUE if image was copied successfully</returns>
                public static bool GetGraphicsAsMat(Graphics graphics, ref Mat destination, bool verticalFlip)
                {
                    IntPtr hdc = graphics.GetHdc();
                    try
                    {
                        return GetHdcAsMat(hdc, ref destination, verticalFlip);
                    }
                    finally
                    {
                        // NOTE: You cannot use the graphics object before ReleaseHdc is called.
                        graphics.ReleaseHdc(hdc);
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-05-09
              • 2021-03-15
              • 2012-09-15
              • 1970-01-01
              • 2020-03-30
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多