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

如何使用LockBits和BitmapData垂直合并两张位图(C#)

32bppArgb格式位图垂直合并的不安全模式实现

需求说明

需要实现一个C#函数,将两张32bppArgb格式的位图垂直合并(上下排列),返回合并后的新位图。此前使用GDI+的方法存在像素失真问题,因此采用不安全模式直接操作像素数据来解决。

完善后的完整代码

using System;
using System.Drawing;
using System.Drawing.Imaging;

public static unsafe Bitmap merge_Two_Bitmaps32bppArgb_Vertically(Bitmap imageSrc1, Bitmap imageSrc2)
{
    if (imageSrc1 == null || imageSrc2 == null)
        throw new ArgumentNullException("输入位图不能为空");

    // 校验输入位图格式,仅支持32bppArgb
    if (imageSrc1.PixelFormat != PixelFormat.Format32bppArgb ||
        imageSrc2.PixelFormat != PixelFormat.Format32bppArgb)
    {
        throw new ArgumentException("仅支持32bppArgb格式的位图");
    }

    int maxWidth = Math.Max(imageSrc1.Width, imageSrc2.Width);
    int totalHeight = imageSrc1.Height + imageSrc2.Height;

    Bitmap mergedBitmap = new Bitmap(maxWidth, totalHeight, PixelFormat.Format32bppArgb);

    BitmapData src1Data = null;
    BitmapData src2Data = null;
    BitmapData destData = null;

    try
    {
        // 锁定源位图1,只读模式
        src1Data = imageSrc1.LockBits(
            new Rectangle(0, 0, imageSrc1.Width, imageSrc1.Height),
            ImageLockMode.ReadOnly,
            PixelFormat.Format32bppArgb);

        // 锁定源位图2,只读模式
        src2Data = imageSrc2.LockBits(
            new Rectangle(0, 0, imageSrc2.Width, imageSrc2.Height),
            ImageLockMode.ReadOnly,
            PixelFormat.Format32bppArgb);

        // 锁定目标位图,写入模式(原代码用ReadOnly无法写入数据,是核心错误)
        destData = mergedBitmap.LockBits(
            new Rectangle(0, 0, maxWidth, totalHeight),
            ImageLockMode.WriteOnly,
            PixelFormat.Format32bppArgb);

        // 计算每行的字节步长(32位像素占4字节)
        int src1Stride = src1Data.Stride;
        int src2Stride = src2Data.Stride;
        int destStride = destData.Stride;

        // 获取像素数据起始指针
        byte* src1Ptr = (byte*)src1Data.Scan0.ToPointer();
        byte* src2Ptr = (byte*)src2Data.Scan0.ToPointer();
        byte* destPtr = (byte*)destData.Scan0.ToPointer();

        // 复制第一张图到目标位图上半部分
        for (int y = 0; y < imageSrc1.Height; y++)
        {
            Buffer.MemoryCopy(
                src1Ptr + y * src1Stride,
                destPtr + y * destStride,
                destStride,
                imageSrc1.Width * 4);
        }

        // 复制第二张图到目标位图下半部分
        int destYOffset = imageSrc1.Height;
        for (int y = 0; y < imageSrc2.Height; y++)
        {
            Buffer.MemoryCopy(
                src2Ptr + y * src2Stride,
                destPtr + (destYOffset + y) * destStride,
                destStride,
                imageSrc2.Width * 4);
        }
    }
    finally
    {
        // 必须解锁所有位图,防止资源泄漏
        if (src1Data != null)
            imageSrc1.UnlockBits(src1Data);
        if (src2Data != null)
            imageSrc2.UnlockBits(src2Data);
        if (destData != null)
            mergedBitmap.UnlockBits(destData);
    }

    return mergedBitmap;
}

关键改进点说明

  • 修正锁定模式:将目标位图的ImageLockMode改为WriteOnly,原代码使用ReadOnly无法写入数据,是导致功能失效的核心问题。
  • 格式校验:增加输入位图格式检查,确保仅处理32bppArgb格式,避免格式不兼容引发的像素失真。
  • 资源安全:使用try-finally块确保所有BitmapData被正确解锁,防止内存泄漏。
  • 高效像素复制:采用Buffer.MemoryCopy进行字节级批量复制,比逐像素操作更高效,同时避免手动操作像素可能出现的失真。
  • 边界处理:自动处理两张图宽度不一致的情况,宽度不足的区域保持默认透明(32bppArgb位图初始化时Alpha通道为0)。

内容的提问来源于stack exchange,提问作者pl.lepko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:34:58