Bitmap merges super large pictures, mining pits

There is a requirement in the project, which is to export a very large picture file reaching hundreds of megabytes.

Because the Bitmap class has a size limit, and memory is not allowed.

So I explored the principle of bitmap storage, and wanted to construct a bmp by myself to realize the export of very large files.

In fact, Bitmap is a bitmap, and the first 54 bytes store the basic information of the file. The following bytes store color information.

The idea of ​​realization is:

1. Create an empty test.bmp file and write 54 header bytes

2. Calculate the height and width of the final image in advance

3. Calculate the final number of bytes according to the height and width of the final image, for example, if 1000 bytes, then directly write new byte[1000] into test.bmp

4. Obtain the color bytes of each small bitmap and fill them into the bytes in step 3.

Header file information description:

before implementation. First understand the storage of Bitmap (the following is for RBG24), you can think of it as a rectangle, when it is displayed, it takes the color information and puts it in the rectangle according to the position.

It is a bitmap, so if multiple bitmaps are written into a total bitmap, the stored byte positions are not continuous

    Attach the code:

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);
        }

It took me two days to research, and I finally got a picture. but! ! ! ! When the file size exceeds 300M, there will be repetitions and blank spaces in the middle, as shown in the figure below, and those smaller than 300M are normal. It is not clear why. This problem has not been solved yet, please let me know if you know, thank you~~~

 

Guess you like

Origin blog.csdn.net/liyayou/article/details/127284094