从位图数组创建大位图的快速方法?

时间:2013-08-06 14:42:44

标签: c# vb.net bitmap gdi+ unsafe

我有这段代码,

复制/粘贴一个新的winform应用程序,如果你运行它,它会在你的桌面上写一个文件:test123abcd.png

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim SquareSize = 5
    Dim GridX = 2500
    Dim GridY = 2500
    Dim SquareCount = GridX * GridY - 1
    Dim sw As New Stopwatch

    Dim Rect(4) As Rectangle
    Rect(0) = New Rectangle(0, 3, 3, 1)
    Rect(1) = New Rectangle(3, 0, 1, 3)
    Rect(2) = New Rectangle(3, 3, 3, 1)
    Rect(3) = New Rectangle(0, 0, 1, 3)

    Dim fullsw = Stopwatch.StartNew
    Using board = New Bitmap(SquareSize * (GridX + 1), SquareSize * (GridY + 1), Imaging.PixelFormat.Format32bppPArgb)
        Using graph = Graphics.FromImage(board)
            Using _board = New Bitmap(SquareSize, SquareSize, Imaging.PixelFormat.Format32bppPArgb)
                Using g As Graphics = Graphics.FromImage(_board)
                    For i = 0 To SquareCount
                        g.Clear(If((i And 1) = 1, Color.Red, Color.Blue))
                        g.FillRectangles(Brushes.White, Rect)
                        sw.Start()
                        graph.DrawImageUnscaled(_board, ((i Mod GridX) * SquareSize), ((i \ GridY) * SquareSize))
                        sw.Stop()
                    Next
                End Using
            End Using
        End Using
        fullsw.Stop()
        board.Save(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) & "\test123abcd.png", Imaging.ImageFormat.Png)
    End Using

    MessageBox.Show("Full SW: " & fullsw.ElapsedMilliseconds & Environment.NewLine &
                    "DrawImageUnscaled SW: " & sw.ElapsedMilliseconds)
End Sub

大约40%到45%的时间花费在DrawImageUnscaled上,在我当前的计算机上大约23秒,而整个过程大约需要50秒

有没有办法加快DrawImageUnscaled? (也许整件事?)

编辑 - 在vb.net中提问,在c#中回答

2 个答案:

答案 0 :(得分:1)

通过假设无法避免生成部分(g.FillRectangles(Brushes.White, Rect),非常耗时),您可以做的最好的事情是避免第二个图生成过程(也适用于board)和只是从_board复制信息。复制比新一代快得多(如下所示),但是你有一个问题,即源信息(_board)与目标格式(board不匹配,依赖于.SetPixel )因此你必须创建一个函数,根据提供的信息(当前矩形)确定当前像素(X / Y点)。

下面您可以看到一个简单的代码,显示两种方法之间的时间要求差异:

Dim SquareSize As Integer = 5
Dim _board As Bitmap = Bitmap.FromFile("in.png")
Dim board As Bitmap = New Bitmap(_board.Width * SquareSize, _board.Height * SquareSize)

For x As Integer = 0 To _board.Width - 1
    For y As Integer = 0 To _board.Height - 1
        board.SetPixel(x * SquareSize, y * SquareSize, _board.GetPixel(x, y))
    Next
Next

board.Save("out1.png", Imaging.ImageFormat.Png)


board = New Bitmap(_board.Width, _board.Height)

Using board
    Using graph = Graphics.FromImage(board)
        Using _board
            Using g As Graphics = Graphics.FromImage(_board)
                For x As Integer = 0 To _board.Width - 1
                    For y As Integer = 0 To _board.Height - 1
                        graph.DrawImageUnscaled(_board, x, y)
                    Next
                Next
            End Using
        End Using
    End Using
    board.Save("out2.png", Imaging.ImageFormat.Png)
End Using

请记住,它不是“正常工作的代码”。它的重点是展示如何在位图之间复制像素(乘以一个因子,只是为了获得与输入不同的输出);并将DrawImageUnscaled方法放在等效条件下(虽然输出图像在逻辑上是不同的),以便很好地理解两种方法之间时间要求的差异。

正如评论所说,这就是我在目前情况下可以做的一切。我希望这足以帮助您找到最佳解决方案。

答案 1 :(得分:1)

哇我喜欢不安全的代码,最后用c#解决了我的问题

这是代码,比我的问题中的代码快约70倍

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace BmpFile
{
    public class BmpTest
    {
        private const int PixelSize = 4;

        public static long Test(int GridX, int GridY, int SquareSize, Rectangle[][] Rect)
        {
            Bitmap bmp = new Bitmap(GridX * SquareSize, GridY * SquareSize, PixelFormat.Format32bppArgb);
            BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                                          System.Drawing.Imaging.ImageLockMode.ReadWrite,
                                          bmp.PixelFormat);

            int Stride = bmd.Stride;
            int Height = bmd.Height;
            int Width = bmd.Width;

            int RectFirst = Rect.GetUpperBound(0);
            int RectSecond;
            int Offset1, Offset2, Offset3;
            int i, j, k, l, w, h;
            int FullRow = SquareSize * Stride;
            int FullSquare = SquareSize * PixelSize;

            var sw = System.Diagnostics.Stopwatch.StartNew();
            unsafe
            {
                byte* row = (byte*)bmd.Scan0;

                //draw all rectangles
                for (i = 0; i <= RectFirst; ++i)
                {

                    Offset1 = ((i / GridX) * FullRow) + ((i % GridX) * FullSquare) + 3;
                    RectSecond = Rect[i].GetUpperBound(0);

                    for (j = 0; j <= RectSecond; ++j)
                    {
                        Offset2 = Rect[i][j].X * PixelSize + Rect[i][j].Y * Stride;
                        w=Rect[i][j].Width;
                        h=Rect[i][j].Height;
                        for (k = 0; k <= w; ++k)
                        {
                            Offset3 = k * PixelSize;
                            for (l = 0; l <= h; ++l)
                            {
                                row[Offset1 + Offset2 + Offset3 + (l * Stride)] = 255;
                            }
                        }
                    }
                }

                //invert color
                for (int y = 0; y < Height; y++)
                {
                    Offset1 = (y * Stride) + 3;

                    for (int x = 0; x < Width; x++)
                    {
                        if (row[Offset1 + x * PixelSize] == 255)
                        {
                            row[Offset1 + x * PixelSize] = 0;
                        }
                        else
                        {
                            row[Offset1 + x * PixelSize] = 255;
                        }
                    }
                }
            }
            sw.Stop();

            bmp.UnlockBits(bmd);

            bmp.Save(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\test.png", ImageFormat.Png);
            bmp.Dispose();

            return sw.ElapsedMilliseconds;
        }
    }
}