1

我有一个场景

  • 选择一个图像文件,然后使用 BitmapDecoder 将源转换为 WriteableBitmap 并将 image.source 设置为 WriteableBitmap。
  • 现在,当用户点击图像时,我会得到坐标,然后想用特定颜色为该像素周围的整个区域着色。(如绘画中的填充选项)。

我使用的代码是

 private void setPixelColors(int xCord, int yCord, int newColor)
 {
    Color color = bit.GetPixel(xCord, yCord);
    if (color.R <= 5 && color.G <= 5 && color.B <= 5 || newColor == ConvertColorToInt(color))
    {
        //Debug.WriteLine("The color was black or same returning");
        return;
    }
    setPixelColors(xCord + 1, yCord, newColor);
    setPixelColors(xCord, yCord + 1, newColor);
    setPixelColors(xCord - 1, yCord, newColor);
    setPixelColors(xCord, yCord - 1, newColor);
    //Debug.WriteLine("Setting the color here");
    bit.SetPixel(xCord, yCord, newColor);
 }

这有效,但非常低效。我想知道有没有更好的方法来做到这一点。

编辑:使用库 WriteableBitmapEx。

4

2 回答 2

5

GetPixel 和 SetPixel 扩展方法对于多次迭代更改非常昂贵,因为它们提取 BitmapContext(WriteableBitmap 的 PixelBuffer),进行更改,然后在使用 BitmapContext 完成调用时写回更新的 PixelBuffer。

如果您首先获得它并保持实时引用,WriteableBitmapEx 将在多个调用之间共享 BitmapContext。这将显着加快读取 PixelBuffer 一次,进行所有更改,然后只将其写回一次。

为此,请使用 WriteableBitmapEx 的BitmapContext对象(可通过 GetBitmapContext 扩展方法访问)提取 PixelBuffer,然后根据需要在位图上下文中调用 Get 和 SetPixel。完成后,处置 BitmapContext 以将其保存回 WriteableBitmap 的 PixelBuffer(通常通过 using 语句自动处置 BitmapContext 会更容易)。

在https://github.com/teichgraf/WriteableBitmapEx上的 WriteableBitmapEx GitHub 上有示例代码可以给出大致的想法

就像是:

 private void setPixelColors(int xCord, int yCord, int newColor)
 {
     using (bit.GetBitmapContext())
     {
         _setPixelColors(xCord, yCord, newColor);
     }
 }
 private void _setPixelColors(int xCord, int yCord, int newColor)
 {
    Color color = bit.GetPixel(xCord, yCord);
    if (color.R <= 5 && color.G <= 5 && color.B <= 5 || newColor == ConvertColorToInt(color))
    {
        //Debug.WriteLine("The color was black or same returning");
        return;
    }
    setPixelColors(xCord + 1, yCord, newColor);
    setPixelColors(xCord, yCord + 1, newColor);
    setPixelColors(xCord - 1, yCord, newColor);
    setPixelColors(xCord, yCord - 1, newColor);
    //Debug.WriteLine("Setting the color here");
    bit.SetPixel(xCord, yCord, newColor);
 }

这应该使整体速度合理,但是(正如 Alex 建议的那样)您应该研究非递归洪水填充算法,特别是如果您有大位图。递归算法将溢出堆栈以进行大量填充。维基百科上有一些相当简单的选项:https ://en.wikipedia.org/wiki/Flood_fill#Alternative_implementations一个简单的方法是保持与此处基本相同的结构,但不是递归处理每个新像素,而是显式处理待编辑像素的堆栈。如果您知道您的目标是小区域,那么这本身可能就足够快了。要支持更大的,您可能需要进一步优化。

于 2015-10-02T23:28:14.377 回答
0

首先,我看不到您如何检查 xCord 或 yCord 是否超出位图边界和您要填充的区域。你知道你想预先填充的水龙头区域的形状吗?例如,如果它有一个椭圆形状,那么调用 FillEllipse 不是更容易吗?或者如果矩形 - FillRect?

其次,我相信你的递归算法效率低下。当然,它会拒绝已经处理过的像素,并且不会进行无用的 SetPixel 调用,但它会进行许多错误检查,因为它仍然会获取像素,对其进行分析并产生递归调用。

试着形象化它。如果您有一个 10x10 的位图,并且在设置第一个像素之前(它将是 10;5 处的像素)点击中间 (5; 5),您将有 5 个递归调用,每个调用都会产生另外4个电话等等。每次调用都将访问位图,获取像素并花费处理器时间。

作为一个小小的改进,尝试在递归调用之前调用 SetPixel :

bit.SetPixel(xCord, yCord, newColor);
setPixelColors(xCord + 1, yCord, newColor);
setPixelColors(xCord, yCord + 1, newColor);
setPixelColors(xCord - 1, yCord, newColor);
setPixelColors(xCord, yCord - 1, newColor);

但我认为你必须改变整个想法。使用位图时,递归算法不是最好的主意。

于 2015-10-02T12:07:42.400 回答