繁体   English   中英

如何从 C# 中的 Graphics 对象获取位图/图像?

[英]How to get the bitmap/image from a Graphics object in C#?

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

我不确定我是否理解你的要求,因为你的问题很不清楚。

如果您想知道如何将Graphics对象的内容保存到位图,那么答案是没有直接的方法可以这样做。 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");
}

这段代码对我有用,我正在转换图像 >> 位图 >> 字节 >> 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);
}

由于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、调色板等)。 如果你真的想支持一切,这需要做很多工作

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

这里不能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);

你可以得到他的 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;
}

你看过这篇 MSDN 文章吗? 它描述了 Bitmap 类,它是一个用于处理由像素数据定义的图像的对象。 System.Drawing.Image 为其提供了附加功能。 HTH

到目前为止,@dialer 在这个线程中有最好的答案。 作为一个额外的例子,这里是如何从图形或任何 HWND 中获取位到 C# 中的 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);
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM