You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForms应用中能否直接写入窗体的像素缓冲区/字节数组?

WinForms中直接写入窗口像素缓冲区的方法

问题描述

我有一个格式为byte[] myimg = new byte[width x height x 4]的ARGB位图字节数组,目前的显示流程是先创建Bitmap,通过LockBits写入像素,再设置PictureBox.Image展示。但想跳过这些步骤,直接写入窗体(甚至不用PictureBox),同时要避免Windows自动缩放/拉伸带来的开销和延迟,只需要高效绘制预先处理好的像素。

解决方案:使用Windows API的BitBlt实现高效绘制

可以通过调用底层Windows GDI API直接操作窗体的设备上下文(DC),结合CreateDIBSection创建与内存字节数组关联的DIB位图,再用BitBlt快速将内存位图复制到窗体DC,完全跳过中间Bitmap对象的创建和PictureBox的使用,消除额外开销。

实现步骤与代码示例

  1. 导入必要的Windows API函数并实现绘制逻辑:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public partial class MainForm : Form
{
    // Windows API声明
    [DllImport("gdi32.dll")]
    private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

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

    [DllImport("gdi32.dll")]
    private static extern IntPtr CreateCompatibleDC(IntPtr hdc);

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

    [DllImport("gdi32.dll")]
    private static extern bool DeleteDC(IntPtr hdc);

    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);

    // 位图信息结构体
    [StructLayout(LayoutKind.Sequential)]
    private struct BITMAPINFOHEADER
    {
        public uint biSize;
        public int biWidth;
        public int biHeight;
        public ushort biPlanes;
        public ushort biBitCount;
        public uint biCompression;
        public uint biSizeImage;
        public int biXPelsPerMeter;
        public int biYPelsPerMeter;
        public uint biClrUsed;
        public uint biClrImportant;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct BITMAPINFO
    {
        public BITMAPINFOHEADER bmiHeader;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public uint[] bmiColors;
    }

    private const uint DIB_RGB_COLORS = 0x00000000;
    private const uint SRCCOPY = 0x00CC0020;

    // 你的ARGB字节数组
    private byte[] myimg;
    private int width;
    private int height;

    public MainForm()
    {
        InitializeComponent();
        // 初始化示例数据(实际替换为你的像素数组)
        width = 640;
        height = 480;
        myimg = new byte[width * height * 4];
        // 填充测试像素(比如红色)
        for (int i = 0; i < myimg.Length; i += 4)
        {
            myimg[i] = 255;     // B
            myimg[i + 1] = 0;   // G
            myimg[i + 2] = 0;   // R
            myimg[i + 3] = 255; // A
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        DrawDirectlyToForm(e.Graphics);
    }

    private void DrawDirectlyToForm(Graphics g)
    {
        IntPtr hdc = g.GetHdc();
        IntPtr memDC = CreateCompatibleDC(hdc);

        try
        {
            BITMAPINFO bmi = new BITMAPINFO();
            bmi.bmiHeader.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
            bmi.bmiHeader.biWidth = width;
            bmi.bmiHeader.biHeight = -height; // 负数值表示顶部为原点(符合GDI+坐标系)
            bmi.bmiHeader.biPlanes = 1;
            bmi.bmiHeader.biBitCount = 32; // ARGB32
            bmi.bmiHeader.biCompression = 0; // BI_RGB
            bmi.bmiHeader.biSizeImage = (uint)(width * height * 4);

            // 创建DIBSection,直接关联我们的字节数组
            IntPtr pBits;
            IntPtr hBitmap = CreateDIBSection(hdc, ref bmi, DIB_RGB_COLORS, out pBits, IntPtr.Zero, 0);

            if (hBitmap != IntPtr.Zero)
            {
                // 将字节数组复制到DIB的内存区域
                Marshal.Copy(myimg, 0, pBits, myimg.Length);

                // 将DIB选入内存DC
                IntPtr oldBitmap = SelectObject(memDC, hBitmap);

                // BitBlt到窗体DC
                BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);

                // 恢复原来的位图并清理
                SelectObject(memDC, oldBitmap);
                DeleteObject(hBitmap);
            }
        }
        finally
        {
            DeleteDC(memDC);
            g.ReleaseHdc(hdc);
        }
    }

    // 如果需要实时更新,调用Invalidate()触发重绘
    public void UpdatePixels(byte[] newPixels)
    {
        myimg = newPixels;
        Invalidate();
    }
}
  1. 关键说明:
  • CreateDIBSection创建的DIB位图直接与你的字节数组内存关联,无需额外的LockBits操作,减少内存拷贝。
  • BitBlt是硬件加速的位图复制操作,性能远高于GDI+的常规绘制方法,完全避免Windows自动缩放(因为你已经预先处理好像素尺寸)。
  • 重写窗体的OnPaint方法,在每次重绘时直接调用DrawDirectlyToForm,确保像素正确显示。

内容的提问来源于stack exchange,提问作者Lasse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:40:31