【问题标题】:WPF or WinForms in Paint Program? (GPU acceleration)绘图程序中的 WPF 或 WinForms? (GPU加速)
【发布时间】:2013-03-05 20:11:14
【问题描述】:

我在 WinForms 中制作了一个类似 MsPaint 的程序,我正在考虑将它移到 WPF 中。

我看过很多文章,我很清楚,当涉及到使控件无效时,这是值得做的,在这种情况下这种情况经常发生(例如线工具)。

我的一个朋友使用 Java Swing 编写了相同的程序,我们遇到了有趣的事情。 我们使用相同的算法“Flood Fill”来填充封闭空间(桶工具)。

他的程序要快得多。在这个算法中,只是对BitMap进行了复杂的计算,最后只有一次重绘

我的问题是它可能是由 GPU 加速 引起的,它存在于 Java Swing 中而 WinForms 没有使用它?换句话说,Wpf 能否(GPU 加速)使 Bitmap(即不显示)上的操作比 WinForms 更快?

下面是 Mine 12 sec on complex BMP 1000x1000 的代码:

        public override void MyMouseDownSubscriber(object sender, System.Windows.Forms.MouseEventArgs e, System.Drawing.Pen pen)
    {
        // public void floodFill(BufferedImage image, Point node, Color targetColor, Color replacementColor) {
        PictureBox canvas =  (PictureBox)sender;
        Bitmap image = (Bitmap) canvas.Image;
        int width = image.Width;
        int height = image.Height;            
        Color replacementColor = pen.Color;
        Point node = e.Location;
        Color targetColor = image.GetPixel(node.X, node.Y);
        int target = targetColor.ToArgb();
        if (targetColor != replacementColor)
        {
            Queue<Point> queue = new Queue<Point> ();
            bool noMorePixelsLeft = false;
            do
            {
                int x = node.X;
                int y = node.Y;
                while (x > 0 && image.GetPixel(x - 1, y).ToArgb() == target)
                {
                    x--;
                }
                bool spanUp = false;
                bool spanDown = false;
                while (x < width && image.GetPixel(x, y).ToArgb() == target)
                {
                    image.SetPixel(x, y, replacementColor);
                    if (!spanUp && y > 0 && image.GetPixel(x, y - 1).ToArgb() == target)
                    {
                        queue.Enqueue(new Point(x, y - 1));
                        spanUp = true;
                    }
                    else if (spanUp && y > 0 && image.GetPixel(x, y - 1).ToArgb() != target)
                    {
                        spanUp = false;
                    }
                    if (!spanDown && y < height - 1 && image.GetPixel(x, y + 1).ToArgb() == target)
                    {
                        queue.Enqueue(new Point(x, y + 1));
                        spanDown = true;
                    }
                    else if (spanDown && y < height - 1 && image.GetPixel(x, y + 1).ToArgb() != target)
                    {
                        spanDown = false;
                    }
                    x++;
                }
                noMorePixelsLeft = false;
                if (queue.Count > 0)
                {
                    node = queue.Dequeue();
                    noMorePixelsLeft = true;
                }
                else noMorePixelsLeft = false;
            } while (noMorePixelsLeft);
            canvas.Invalidate();
            canvas.Update();
        } 
    }

JavaSwing 中的那个:在复杂的 BMP 1000x1000 上 0.5 秒

private void floodFill(BufferedImage image, Point startPoint, Color targetColor, Color replacementColor) {
   int width = image.getWidth();
   int height = image.getHeight();
   int target = targetColor.getRGB();
   int replacement = replacementColor.getRGB();

   if (target != replacement) {
     Deque<Point> queue = new LinkedList<>();
     do {
       int x = startPoint.x;
       int y = startPoint.y;
       while (x > 0 && image.getRGB(x - 1, y) == target) x--;
       boolean spanUp = false;
       boolean spanDown = false;
       while (x < width && image.getRGB(x, y) == target) {
         image.setRGB(x, y, replacement);
         if (!spanUp && y > 0 && image.getRGB(x, y - 1) == target) {
           queue.add(new Point(x, y - 1));
           spanUp = true;
         } else if (spanUp && y > 0 && image.getRGB(x, y - 1) != target) spanUp = false;

         if (!spanDown && y < height - 1 && image.getRGB(x, y + 1) == target) {
           queue.add(new Point(x, y + 1));
           spanDown = true;
         } else if (spanDown && y < height - 1 && image.getRGB(x, y + 1) != target) spanDown = false;
         x++;
       }
     } while ((startPoint = queue.pollFirst()) != null);
   }

 }

【问题讨论】:

  • 这不太可能,这些类库都没有专用的 FloodFill() 方法。您的朋友可能只是复制了更好的代码。不知道为什么我们应该猜测您的代码是什么样的,这对我们没有帮助。
  • 我们不使用专用的 FloodFill() 方法。我们刚刚实现了相同的填充算法——Smith-Waterman 算法。当然,我们的实现可能会有一些差异。(不同的集合等)我想我只能粘贴我的代码,但我想知道的是,如果 gpu 加速技术(如 WPF、Swing)可以做到这一点计算速度更快?
  • 通常他们可以,但在这种特殊情况下我不会打赌这样的猜测。正如 Hans Passant 指出的那样,没有专用的 FloodFill()。因此,它不依赖于 WPF/Swing/等,因为他们不这样做。 除非您在这些框架中调用其他一些基于 GPU 的方法,这取决于您实现 GPU 程序以利用其功能。但你做了什么,我们不知道。
  • 首先:如果您获取/设置的不仅仅是几个像素,请不要使用 image.Get/SetPixel()。将图像字节锁定到原始数​​组中并对其执行操作。

标签: c# wpf winforms gpu


【解决方案1】:

我找到了问题的答案。

差异的原因:

Java 和 C# 算法执行时间的差异不是由 qpu 加速引起的,而是由 GetPixel/SetPixel 方法最后的时间引起的。 在 C# 中,此方法持续很长时间。

解决方案

Bitmap 类有一个 LockBits 方法,它只返回像素颜色的纯数据。 Marshal.Copy 允许您将元素复制到 int[][] 矩阵并从矩阵复制回位图。 实现的方法在这里: http://pastebin.com/SVX3w3tF

【讨论】:

    【解决方案2】:

    对于严格的基于位图的应用程序,我会选择 Winforms。

    WPF 更适用于矢量和声明性图形:像 MSPaint 这样的程序会以混合方式使用缓冲区来填充您自己的应用程序。也就是说,利用 GDI+ API(Winforms)。

    我不知道如何使用位图方式让您的应用程序更快。

    【讨论】:

      猜你喜欢
      • 2015-06-06
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-17
      • 2011-01-16
      • 1970-01-01
      • 2010-12-07
      相关资源
      最近更新 更多