winforms 使用指标直接写入表单的字节数组

wz8daaqr  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(153)

在winforms应用程序中,是否有任何方法可以直接绘制到窗口的像素缓冲区/字节数组?
我有一个字节数组,其中有一个ARGB位图的byte[] myimg = new byte[width x height x 4]的图像,现在我想以窗体显示它,我所知道的唯一方法是首先制作一个位图,然后使用lockbits将像素写入位图,然后我将一个picturebox.image设置为我的位图示例。但是我想跳过这一步,直接写入窗体,如果可能的话,甚至不需要picturebox,这可能吗?

更新

澄清一下,我特别想避免窗口处理缩放/拉伸的开销和缓慢。我只想以最有效的方式将我自己的像素放到窗体的画布区域上,我自己的字节数组由我自己预先准备并相应地缩放。

更新2

正如jtxkopt所示,较低的windows api确实提供了使用bitblt的方法,这几乎完全消除了我的开销

qjp7pelc

qjp7pelc1#

你不能直接在Form上绘制原始图像数据。没有像DrawImageData(byte[] data, int width, int height, PixelFormat format);那样的功能。我知道有两个选项。你可以尝试其中一个最适合你的需要。

选项1

您可以使用构造函数从图像数据创建Bitmap类,根据需要填充数据并将位图绘制到屏幕上。但是,正如其他人所述,这样做并不会获得太多的性能提升,只要您停留在托管代码中。
对于一个简单的实现,您可以查看示例代码。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CustomRendering
{
    public unsafe partial class MainForm : Form
    {
        public MainForm()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
            InitializeComponent();
        }

        private Bitmap m_surfaceBitmap;
        private byte* m_surfaceData;
        private int m_stride;
        private int m_width, m_height;
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics; 
            g.SmoothingMode = SmoothingMode.HighSpeed;
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            g.PixelOffsetMode = PixelOffsetMode.Half;
            g.DrawImage(m_surfaceBitmap, Point.Empty);
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            m_width = ClientSize.Width;
            m_height = ClientSize.Height;
            m_stride = (32 * m_width + 31) / 32 * 4; // Calculate the stride.
            m_surfaceData = (byte*)Marshal.AllocHGlobal(m_stride * m_height);
            m_surfaceBitmap = new Bitmap(m_width, m_height, m_stride, PixelFormat.Format32bppArgb, (IntPtr)m_surfaceData);
        }

        protected unsafe override void OnMouseMove(MouseEventArgs e)
        {
            Clear(Color.White);
            FillRectangle(e.X, e.Y, 100, 100, Color.Black);
            Invalidate();
            base.OnMouseMove(e);
        }
        private void Clear(Color color)
        {
            int argb = color.ToArgb();
            for (int i = 0; i < m_stride * m_height; i += 4) 
                *(int*)(m_surfaceData + i) = argb; 
        }
        private void FillRectangle(int x0, int y0, int width, int height, Color color)
        {
            int argb = color.ToArgb();
            for (int y = y0; y < y0 + height; y++) 
                for (int x = x0; x < x0 + width; x++) 
                    SetPixel(x, y, argb); 
        }

        private void SetPixel(int x, int y, int argb)
        {
            if (x >= m_width || x < 0 || y >= m_height || y < 0)
                return;
            m_surfaceData[y * m_stride + 4 * x + 0] = (byte)((argb >> 0) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 1] = (byte)((argb >> 8) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 2] = (byte)((argb >> 16) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 3] = (byte)((argb >> 24) & 0x000000FF);
        }
    }

}

选项2
这是一个使用Win32 API的低级解决方案,但我认为这个解决方案比上一个解决方案更快,因为它处理WM_PAINT消息本身,并使用bitblt函数显示DIB(设备独立位图)而不是绘制Gdiplus Bitmap。不要解释如何创建一个DIB,以及其他与Win32相关的代码是如何工作的。下面是示例代码。比较它们并选择其中之一。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CustomRendering
{ 
    public unsafe partial class MainForm : Form
    {
        public MainForm()
        {
             InitializeComponent();
        }
         
        private byte* m_surfaceData;
        private int m_stride;
        private int m_width, m_height;
        private IntPtr m_hdcWindow;
        private IntPtr m_surfaceDC;
        private IntPtr m_surfaceBitmap;
        private IntPtr m_oldObject;
        private Point m_mouseLocation;
         
        protected override void WndProc(ref Message m)
        {
            // Process WM_PAINT ourself.
            if(m.Msg == 0x000F)
            {
                Clear(Color.White);
                FillRectangle(m_mouseLocation.X, m_mouseLocation.Y, 100, 100, Color.Black);
                PAINTSTRUCT ps;
                IntPtr hdc = BeginPaint(Handle, out ps);
                
                BitBlt(m_hdcWindow, 0, 0, m_width, m_height, m_surfaceDC, 0, 0, RasterOperations.SRCCOPY);
                EndPaint(Handle, ref ps);base.WndProc(ref m);
            }
            // Process WM_ERASEBKGND to prevent flickering.
            else if(m.Msg == 0x0014) 
                m.Result = (IntPtr)1; 
            else
                base.WndProc(ref m);
        }
        protected override void OnHandleCreated(EventArgs e)
        { 
            m_width = Screen.PrimaryScreen.WorkingArea.Width;
            m_height = Screen.PrimaryScreen.WorkingArea.Height;
            m_stride = (32 * m_width + 31) / 32 * 4; // Calculate the stride.
            CreateSurface(m_width, m_height);
         }

        protected unsafe override void OnMouseMove(MouseEventArgs e)
        {
            m_mouseLocation = e.Location;
            Invalidate(ClientRectangle); // Invalidate the only visible area.
        }
        private void CreateSurface(int width, int height)
        {
            BITMAPINFO bi = new BITMAPINFO();

            m_hdcWindow = GetDC(Handle);
            m_surfaceDC = CreateCompatibleDC(m_hdcWindow);

            bi.bmiHeader.biSize = (uint)Marshal.SizeOf<BITMAPINFOHEADER>();
            bi.bmiHeader.biWidth = width;
            bi.bmiHeader.biHeight = -height;
            bi.bmiHeader.biPlanes = 1;
            bi.bmiHeader.biBitCount = 32;
            bi.bmiHeader.biCompression = BitmapCompressionMode.BI_RGB; // No compression
            bi.bmiHeader.biSizeImage = (uint)(width * height * 4);
            bi.bmiHeader.biXPelsPerMeter = 0;
            bi.bmiHeader.biYPelsPerMeter = 0;
            bi.bmiHeader.biClrUsed = 0;
            bi.bmiHeader.biClrImportant = 0; 

            IntPtr ppvBits;
            m_surfaceBitmap = CreateDIBSection(m_surfaceDC, ref bi, DIB_RGB_COLORS, out ppvBits, IntPtr.Zero, 0);

            m_surfaceData = (byte*)ppvBits.ToPointer();

            m_oldObject = SelectObject(m_surfaceDC, m_surfaceBitmap);
        }
        private void Clear(Color color)
        {
            int argb = color.ToArgb();
            for (int i = 0; i < m_stride * m_height; i += 4)
                *(int*)(m_surfaceData + i) = argb;
        }
        private void FillRectangle(int x0, int y0, int width, int height, Color color)
        {
            int argb = color.ToArgb();
            for (int y = y0; y < y0 + height; y++)
                for (int x = x0; x < x0 + width; x++)
                    SetPixel(x, y, argb);
        }

        private void SetPixel(int x, int y, int argb)
        { 
            m_surfaceData[y * m_stride + 4 * x + 0] = (byte)((argb >> 0) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 1] = (byte)((argb >> 8) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 2] = (byte)((argb >> 16) & 0x000000FF);
            m_surfaceData[y * m_stride + 4 * x + 3] = (byte)((argb >> 24) & 0x000000FF);
        }
        private enum RasterOperations : uint
        {
            SRCCOPY = 0x00CC0020,
            SRCPAINT = 0x00EE0086,
            SRCAND = 0x008800C6,
            SRCINVERT = 0x00660046,
            SRCERASE = 0x00440328,
            NOTSRCCOPY = 0x00330008,
            NOTSRCERASE = 0x001100A6,
            MERGECOPY = 0x00C000CA,
            MERGEPAINT = 0x00BB0226,
            PATCOPY = 0x00F00021,
            PATPAINT = 0x00FB0A09,
            PATINVERT = 0x005A0049,
            DSTINVERT = 0x00550009,
            BLACKNESS = 0x00000042,
            WHITENESS = 0x00FF0062,
            CAPTUREBLT = 0x40000000
        }
        private enum BitmapCompressionMode : uint
        {
            BI_RGB = 0,
            BI_RLE8 = 1,
            BI_RLE4 = 2,
            BI_BITFIELDS = 3,
            BI_JPEG = 4,
            BI_PNG = 5
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompressionMode biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant; 
        }
        [StructLayoutAttribute(LayoutKind.Sequential)]
        private struct BITMAPINFO
        { 
            public BITMAPINFOHEADER bmiHeader; 
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.Struct)]
            public int[] bmiColors;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left, Top, Right, Bottom; 
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct PAINTSTRUCT
        {
            public IntPtr hdc;
            public bool fErase;
            public RECT rcPaint;
            public bool fRestore;
            public bool fIncUpdate;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] rgbReserved;
        }
        private const int DIB_RGB_COLORS = 0;
        private const int DIB_PAL_COLORS = 1;

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO pbmi, uint usage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

        [DllImport("gdi32.dll")]
        private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, RasterOperations dwRop);

        [DllImport("gdi32.dll")]
        private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hNewObj);

        [DllImport("user32.dll")]
        static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
         
        [DllImport("user32.dll")]
        static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);
    }

}

相关问题