winforms 如何垂直合并两个位图(使用LockBits和BitmapData)

xzlaal3s  于 2023-08-07  发布在  其他
关注(0)|答案(1)|浏览(128)

我需要一个函数在C#中,返回合并位图垂直。我希望得到的图像包含两个位图设置一个以上的其他。我对在不安全模式下处理图像没有太多经验。我有一个方法,合并图像垂直使用GDI+,但有失真的像素。
请帮帮我

public static Bitmap merge_Two_Bitmaps32bppArgb_Vertically(Bitmap imageSrc1, Bitmap imageSrc2) {
  Bitmap returnMapOut = null;
  int maxW = (
    new List < int > () {
      imageSrc1.Width,
        imageSrc2.Width
    }
  ).Max();

  returnMapOut = new Bitmap(
    maxW,
    (imageSrc1.Height + imageSrc2.Height), //sum !!!
    PixelFormat.Format32bppArgb
  );

  //src 1:
  BitmapData bitmapDataSrc1 = imageSrc1.LockBits(
    new Rectangle(0, 0, imageSrc1.Width, imageSrc1.Height),
    ImageLockMode.ReadOnly,
    PixelFormat.Format32bppArgb
  );

  //output:
  BitmapData bitmapDataOut = returnMapOut.LockBits(
    new Rectangle(0, 0, returnMapOut.Width, returnMapOut.Height),
    ImageLockMode.ReadOnly,
    PixelFormat.Format32bppArgb
  );

  //TODO:

}

字符串

wtzytmuj

wtzytmuj1#

由于您没有指定目标平台,因此我假设(为简单起见)平台是Windows(因为GDI+System.Drawing API仅在该平台上完全可用)。此外,我假设您的目标是.NET的最新版本之一,例如.NET 6.0 LTS。
我为您提供了一些示例代码,避免使用处理Stride指针和原始图像数据的功能。我在示例中使用System.Drawing.Common包。如果您正在使用旧版本的.NET Framework,则添加对System.Drawing的引用也应该有效。
下面的方法接受Image对象数组。它采用第一个图像的宽度和像素格式,并通过将所有图像的高度相加来计算结果图像的高度。它使所有的图像未缩放,所以你需要调整它,以您的需要;也许你想指定结果图像的宽度作为参数,并相应地缩放所有图像。

private static Image MergeBitmaps(params Image[] bitmaps)
{
    Image first = bitmaps.FirstOrDefault() ?? throw new InvalidOperationException();
    int width = first.Width;
    int resultImageHeight = bitmaps.Sum(bitmap => bitmap.Height);

    var offset = 0;
    var resultImage = new Bitmap(width, resultImageHeight, first.PixelFormat);
    using Graphics g = Graphics.FromImage(resultImage);
    foreach (Image bitmap in bitmaps)
    {
        g.DrawImageUnscaled(bitmap, 0, offset, bitmap.Width, bitmap.Height);
        offset += bitmap.Height;
    }

    return resultImage;
}

字符串
另外,我会使用Image类型而不是Bitmap。您可以使用提供的示例方法,如下所示:

using Image bitmap1 = Image.FromStream(...);
using Image bitmap2 = Image.FromStream(...);
using Image mergedImage = MergeBitmaps(bitmap1, bitmap2);
mergedImage.Save(Path.Combine(path, "merged.bmp"));


正如我的评论中提到的,如果您了解了缓冲区中的像素信息是如何布局的,那么您应该只处理原始位图数据。您的实现可能需要计算和像素转换,以支持不同像素格式的图像。如果性能不是问题,那么使用Graphics对象呈现目标图像的代码(如我的示例中所提供的)可能不那么复杂,也更容易使用。

更新

如果要通过将原始位图缓冲区复制到结果图像中来合并图像,请确保所有输入图像具有相同的宽度和像素格式,并且像素格式也与结果图像的格式匹配(在这种情况下为32bppArgb)。然后你可以尝试这样的东西(这需要适当的测试和额外的检查):

private static Image MergeBitmaps(params Bitmap[] bitmaps)
{
    int width = bitmaps.Max(bitmap => bitmap.Width);
    int resultImageHeight = bitmaps.Sum(bitmap => bitmap.Height);

    var offset = 0;
    var pixelFormat = PixelFormat.Format32bppArgb;
    var resultImage = new Bitmap(width, resultImageHeight, pixelFormat);

    foreach (Bitmap bitmap in bitmaps)
    {
        int bitmapHeight = bitmap.Height;
        var rectangle = new Rectangle(0, offset, width, bitmapHeight);

        CopyBitmap(resultImage, rectangle, bitmap);

        offset += bitmapHeight;
    }

    return resultImage;
}

private static void CopyBitmap(
    Bitmap destBitmap, 
    Rectangle destinationRectange, 
    Bitmap sourceBitmap)
{
    BitmapData resultBitmapData = destBitmap.LockBits(destinationRectange, 
        ImageLockMode.WriteOnly, 
        destBitmap.PixelFormat);
    try
    {
        var bytes = new byte[resultBitmapData.Stride * resultBitmapData.Height];
        var rect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
        BitmapData bitmapData = sourceBitmap.LockBits(rect, 
            ImageLockMode.ReadOnly, 
            sourceBitmap.PixelFormat);
        try
        {
            var array = new byte[bitmapData.Stride * bitmapData.Height];
            Marshal.Copy(bitmapData.Scan0, array, 0, array.Length);
            Array.Copy(array, bytes, array.Length);
        }
        finally
        {
            sourceBitmap.UnlockBits(bitmapData);
        }

        Marshal.Copy(bytes, 0, resultBitmapData.Scan0, bytes.Length);
    }
    finally
    {
        destBitmap.UnlockBits(resultBitmapData);
    }
}

相关问题