代码的功能是横向拼接多幅图片,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;
        }
    }
}

效果图:

分类: articles