如何在windows 10 uwp中有效地使用WriteableBitmap.setpixel()?

时间:2015-10-02 09:03:42

标签: c# winrt-xaml windows-10 uwp writablebitmap

我有一个场景

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

我使用的代码是

 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。

2 个答案:

答案 0 :(得分:4)

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

WriteableBitmapEx将在多个调用之间共享BitmapContext(如果先获取它并保留实时引用)。只需一次读出PixelBuffer,进行所有更改,然后只将其写回一次,就会快得多。

为此,请使用WriteableBitmapEx的BitmapContext对象(可通过GetBitmapContext扩展方法访问)提取PixelBuffer,然后根据需要在位图上下文中调用Get和SetPixel。完成后,将Dispose 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一个简单的选项是保持与此处基本相同的结构,但不是递归处理每个新像素,而是显式处理待编辑像素的堆栈。如果你知道你的目标是小区域,那么它本身可能足够快。为了支持更大的产品,您可能希望进一步优化。

答案 1 :(得分:0)

首先,我无法看到你如何检查xCord或yCord是否超出了位图边界并且超出了你想要填充的区域。您是否知道要提前填写的水龙头区域的形状?例如,如果它具有椭圆形状,是否更容易调用FillEllipse?或者如果是矩形 - FillRect?

第二,我相信你的递归算法是低效的。当然它会降低已经处理过的像素,并且不会进行无用的SetPixel调用,但它会进行许多错误检查,因为它仍然会获得像素,对其进行分析并产生可靠的调用。

尝试将其可视化。如果你有一个位图10x10,你甚至在设置第一个像素之前点击中间(5; 5)(它是10; 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);

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