创建新图像时,如何实现更快的位图数据复制?
图像操作类速度优化:替代
g.DrawImage()的逐行像素复制方案 问题背景
- 开发图像操作类时,
g.DrawImage()调用耗时超出预期,无法达到处理速度要求 - 此前使用LeadTools SDK时,通过逐行复制像素字节的方法获得了明显更快的处理效果
- 已通过可靠的
LogDebug()完成基准测试:- 当
headerHeight = 0(不为原图顶部添加空白):处理3页图像总耗时约1.9秒 - 当
headerHeight = 100(添加100像素顶部空白):总耗时约3.6秒
- 当
优化方案:直接操作位图像素缓冲区
跳过g.DrawImage()的GDI+封装开销,直接通过锁定位图内存、逐行复制像素数据实现,和LeadTools的高效思路一致。
核心逻辑
- 锁定源位图与目标位图的像素缓冲区,获取直接内存访问权限
- 根据图像像素格式(如32位ARGB、24位RGB)计算每行字节数
- 若需添加顶部空白,从目标位图的
headerHeight行开始,逐行复制源位图的像素数据 - 操作完成后解锁缓冲区,避免资源泄漏
代码示例
public Bitmap AddHeaderAndCopy(Bitmap sourceBmp, int headerHeight) { // 创建目标位图,高度为原图高度加顶部空白高度 Bitmap targetBmp = new Bitmap(sourceBmp.Width, sourceBmp.Height + headerHeight, sourceBmp.PixelFormat); // 定义源图和目标图的操作区域 Rectangle sourceRect = new Rectangle(0, 0, sourceBmp.Width, sourceBmp.Height); Rectangle targetRect = new Rectangle(0, headerHeight, sourceBmp.Width, sourceBmp.Height); // 锁定位图像素数据,获取内存指针 BitmapData sourceData = sourceBmp.LockBits(sourceRect, ImageLockMode.ReadOnly, sourceBmp.PixelFormat); BitmapData targetData = targetBmp.LockBits(targetRect, ImageLockMode.WriteOnly, targetBmp.PixelFormat); try { int rowByteCount = sourceData.Stride; IntPtr sourceRowPtr = sourceData.Scan0; IntPtr targetRowPtr = targetData.Scan0; // 逐行复制像素数据,规避内存对齐问题 for (int y = 0; y < sourceBmp.Height; y++) { byte[] rowBuffer = new byte[rowByteCount]; Marshal.Copy(sourceRowPtr, rowBuffer, 0, rowByteCount); Marshal.Copy(rowBuffer, 0, targetRowPtr, rowByteCount); // 移动指针到下一行 sourceRowPtr = IntPtr.Add(sourceRowPtr, sourceData.Stride); targetRowPtr = IntPtr.Add(targetRowPtr, targetData.Stride); } } finally { // 必须解锁位图,否则会导致资源占用 sourceBmp.UnlockBits(sourceData); targetBmp.UnlockBits(targetData); } return targetBmp; }
性能优势说明
- 直接操作内存避免了
g.DrawImage()内置的坐标转换、格式兼容检查等额外开销 - 逐行复制模式更适配CPU缓存机制,减少内存访问的延迟损耗
- 添加顶部空白时仅需调整目标位图的起始复制行,无额外绘制逻辑,耗时增长远低于
DrawImage()方案
内容的提问来源于stack exchange,提问作者Arvo Bowen
相关产品推荐
相关产品推荐

