代码的功能是横向拼接多幅图片,24位色深Bitmap直接复制粘贴就能用,其它色深需要把每个像素占用数据长度对应改一下。
田字拼接,或者更复杂的多行拼接也可以照着修改,原理都一样,只要在外层按行进行循环,里层的操作其实还是横向拼接。
* 后附 Graphics.DrawImage()方法做对比。
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
namespace ConsoleApp1
{
class BitmapHelper
{
public static Bitmap Merge(Bitmap left, Bitmap right)
{ //横向拼接两张图片,只适用于24位色深Bitmap,一像素占24bit(3B),
//其它色深可以根据位数计算每个像素的长度
Bitmap bmpOut = new Bitmap(left.Width + right.Width, left.Height);
unsafe
{
BitmapData leftData =
left.LockBits(new Rectangle(0, 0, left.Width, left.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte* leftPtr = (byte*)(void*)leftData.Scan0;
BitmapData rightData =
right.LockBits(new Rectangle(0, 0, right.Width, left.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte* rightPtr = (byte*)(void*)rightData.Scan0;
BitmapData outData =
bmpOut.LockBits(new Rectangle(0, 0, bmpOut.Width, bmpOut.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte* outPtr = (byte*)(void*)outData.Scan0;
//Stride代表bitmap的一行数据,但并不等于图片width
int outStride = outData.Stride;
//求左图和右图各自的行数据偏移量
int offL = leftData.Stride - 3 * left.Width;
int offR = rightData.Stride - 3 * right.Width;
for (int y = 0; y < left.Height; ++y)
{//逐行拼接:新图的行数据 = 左图行数据 + 右图数据
for (int x = 0; x < leftData.Width * 3; ++x)
{
outPtr[outStride * y + x] = leftPtr[0];
++leftPtr;
}
leftPtr += offL;
//左图行数据复制结束后,跳过Offset才是左图的新行起始
for (int x = 0; x < rightData.Width * 3; ++x)
{
outPtr[outStride * y + 3 * left.Width + x] = rightPtr[0];
++rightPtr;
}
rightPtr += offR; //右图行数据跳过Offset,读取新行
}
left.UnlockBits(leftData);
right.UnlockBits(rightData);
bmpOut.UnlockBits(outData);
return bmpOut;
}
}
public static Bitmap Merge(Bitmap[] bitmaps)
{ //横向拼接多张图片,只适用于24位色深Bitmap,一像素占24bit(3B),
//其它色深可以根据位数计算每个像素的长度
int count = bitmaps.Length;
int height = bitmaps[0].Height;
int outWidth = bitmaps.Select(b => b.Width).Sum();
Bitmap bmpOut = new Bitmap(outWidth, bitmaps[0].Height);
unsafe
{
BitmapData[] imgData = new BitmapData[count];
byte*[] imgPtr = new byte*[count];
int[] imgOffset = new int[count];
int[] imgStart = new int[count];
imgStart[0] = 0;
for (int i = 0; i < count; i++)
{ //计算每张图片的行数据偏移量Offset
imgData[i] =
bitmaps[i].LockBits(new Rectangle(0, 0, bitmaps[i].Width, height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
imgPtr[i] = (byte*)imgData[i].Scan0;
imgOffset[i] = imgData[i].Stride - 3 * bitmaps[i].Width;
if (i > 0) imgStart[i] = imgStart[i - 1] + imgData[i - 1].Width * 3;
//求每张子图片的起始位置
}
BitmapData outData =
bmpOut.LockBits(new Rectangle(0, 0, bmpOut.Width, bmpOut.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte* outPtr = (byte*)(void*)outData.Scan0;
int outStride = outData.Stride;
for (int y = 0; y < height; ++y)
{
for (int idx = 0; idx < count; idx++)
{
for (int x = 0; x < imgData[idx].Width * 3; ++x)
{ //从第y行,第idx张图片起始位置开始,写入图片数据
outPtr[outStride * y + imgStart[idx] + x] = imgPtr[idx][0];
++imgPtr[idx];
}
imgPtr[idx] += imgOffset[idx];//跳过偏移量
}
}
for (int i = 0; i < count; i++)
{
bitmaps[i].UnlockBits(imgData[i]);
}
bmpOut.UnlockBits(outData);
return bmpOut;
}
}
public static Bitmap SlowMerge(Bitmap left, Bitmap right)
{
//效率非常低,借用Graphics.DrawImage()方法,
//直接将所有图片输出到bmpout
Bitmap bmpout = new Bitmap(left.Width + right.Width, left.Height);
using Graphics g = Graphics.FromImage(bmpout);
g.DrawImage(left, 0, 0);
g.DrawImage(right, left.Width, 0);
return bmpout;
}
}
}
效果图: