Bitmap合并超大图片,采坑

项目中有一个需求,就是要导出一个达到几百兆超大图片文件。

因为Bitmap类是有大小限制的,而且内存也不允许。

所以去探索了一下bitmap存储的原理,想通过自己构造一个bmp来实现超大文件的导出。

其实Bitmap就是一个位图,前面54个字节存储文件的基本信息。后面的字节就是存储颜色信息。

实现的思路就是:

1.创建一个空的test.bmp文件,并写入54个头文件字节

2.事先计算好最终图片的高度和宽度

3.根据最终图片的高度和宽度计算出来 最终的字节数,例如1000个字节,则直接new byte[1000],写入test.bmp

4.获取每一个小bitmap的颜色字节,填充到第3步的字节中。

头文件信息说明:

在实现之前。先理解一下Bitmap的存储(以下针对RBG24),你可以把它看做是一个矩形,展示出来的时候就是拿取颜色信息,按照位置放到矩形中去。

它是一个位映射的,所以如果多个bitmap写入到一个总bitmap中的时候,存储的byte位置是不连续的

    附上代码:

FileStream fs=new FileStream("G:\\bytesFile\\12.GDSS", FileMode.Open, FileAccess.ReadWrite);
            byte[] bytes = new byte[100 * 1440*3];
            fs.Read(bytes, 0, bytes.Length);
            int totalImage = (int)(fs.Length / (1440 * 3)); //总张数

            int startCount = (int)(totalImage/1000);
            int forCount = totalImage % 1000 == 0 ? startCount : startCount + 1; //循环次数

            //随便先拿一个bitmap的头文件信息,先写入流,后面修改
            Bitmap bitmap = Helper.GetBitmapByBytes(bytes, 1440 * 3, false);
            FileStream write = new FileStream("G:\\bytesFile\\lyy12.bmp", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Bmp);
            byte[] soure=ms.ToArray();
            write.Write(soure, 0, 54);

            List<int> lstWidth = new List<int>();
            List<long> lstByteCount = new List<long>();
            long allLength = 0;
            long perRowLength = 0;
            //空白填充
            for (int j = 0; j < 1440; j++)
            {                
                for (int i = 0; i < forCount; i++)
                {

                    int c = (totalImage - (i * 1000) >= 1000) ? 1000 : totalImage - (i * 1000);
                    lstWidth.Add(c);
                    byte[] fill;
                    if (c % 4 == 0)
                    {
                        fill = new byte[c * 3];
                    }
                    else
                    {
                        fill = new byte[(c * 3) + (4 - (c * 3 % 4))];
                    }
                    if (i == 0) write.Seek(54+j* perRowLength, SeekOrigin.Begin);
                    else write.Seek(j* perRowLength+54 + lstWidth[i - 1], SeekOrigin.Begin);
                    allLength += fill.Length;

                    if (j == 0)
                    {
                        lstByteCount.Add(fill.Length);
                        perRowLength += fill.Length;
                    }

                    write.Write(fill, 0, fill.Length);

                }
            }

            //修改文件大小
            write.Seek(2, SeekOrigin.Begin);
            long len = 54 + allLength;
            write.Write(BitConverter.GetBytes(len));
            //修改文件宽度
            write.Seek(18, SeekOrigin.Begin);
            write.Write(BitConverter.GetBytes(totalImage));
            //设置文件高度
            write.Seek(22, SeekOrigin.Begin);
            write.Write(BitConverter.GetBytes(1440));

            FileStream txt = new FileStream("G:\\log.txt",FileMode.OpenOrCreate,FileAccess.ReadWrite);
            StreamWriter sw = new StreamWriter(txt);
            int w = 0;
            //颜色填充
            for (int i = 1; i <= forCount; i++)
            {
                int c = (totalImage - ((i - 1) * 1000) >= 1000) ? 1000 : totalImage - ((i - 1) * 1000);
                long pos = (i - 1) * 1000 * 1440 * 3;
                fs.Seek(pos, SeekOrigin.Begin);
                byte[] result = new byte[c * 1440 * 3];
                fs.Read(result, 0, result.Length);
                using (Bitmap resultBitmap = Helper.GetBitmapByBytes(result, 1440 * 3, false))
                {
                    //resultBitmap.Save($"G:\\images\\{i}.bmp", ImageFormat.Bmp);
                    w += resultBitmap.Width;
                    using (MemoryStream msResult = new MemoryStream())
                    {
                        msResult.Flush();
                        resultBitmap.Save(msResult, ImageFormat.Bmp);
                        byte[] temp = msResult.ToArray();

                        for (int h = 0; h < 1440; h++)
                        {
                            write.Flush();
                            byte[] rowByte = new byte[lstByteCount[i - 1]];
                            Array.Copy(temp, 54 + h * lstByteCount[i - 1], rowByte, 0, rowByte.Length);
                            long kk = 54 + (h * perRowLength) + lstByteCount.Take(i - 1).Sum();

                            sw.WriteLine($"第{i}张---->{h}---->{kk}");
                            write.Seek(kk, SeekOrigin.Begin);
                            write.Write(rowByte, 0, rowByte.Length);
                            rowByte = null;
                        }
                        temp = null;
                    }
                }
              
            }
            txt.Close();
           // sw.Dispose();
            fs.Close();
            write.Close();
            fs.Dispose();
            write.Dispose();
            MessageBox.Show("ok");

Helper.cs

 public static Bitmap GetBitmapByBytes(byte[] bytes, int stride, bool mirror)
        {
            if (bytes.Length == 0) return null;
            int height = bytes.Length / stride;
            int width = 1440;
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);

            //得到一个指向Bitmap的buffer指针
            IntPtr ptrBmp = bmpData.Scan0;
            //int nImageStride = 1440 * 3;
            //图像宽能够被4整除直接copy
            for (int i = 0; i < bitmap.Height; ++i)
            {
                Marshal.Copy(bytes, i * stride, new IntPtr(ptrBmp.ToInt64() + i * bmpData.Stride), 1440 * 3);
            }

            //BitmapData解锁
            bitmap.UnlockBits(bmpData);

            bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
            return bitmap;//  BitmapHelper.CompressImage(bitmap);
        }

花了我两天时间研究,总算是可以出来图片了。但是!!!!当文件大小超过300M,会出现前后重复,中间空白的情况,如下图,小于300M的就正常。不清楚什么原因。这个问题还未解决,有知道的可告知一下,感谢~~~

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值