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

本文关键字:WriteableBitmap setpixel 有效地 windows uwp | 更新日期: 2023-09-27 18:15:52

我有一个场景

  • 选择一个图像文件,然后使用BitmapDecoder转换源代码到WriteableBitmap和设置图像。来源: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库

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

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

WriteableBitmapEx将在多个调用之间共享BitmapContext,如果你先得到它并保持一个实时引用。只读取一次PixelBuffer,进行所有更改,然后只写一次,这将大大加快速度。

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

有样例代码,将给出WriteableBitmapEx GitHub的总体思路https://github.com/teichgraf/WriteableBitmapEx

类似:

 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建议的)你应该研究非递归的洪水填充算法,特别是如果你有大的位图。对于大的填充,递归算法将溢出堆栈。Wikipedia上有一些相当简单的选项:https://en.wikipedia.org/wiki/Flood_fill#Alternative_implementations一个简单的选项是保持基本相同的结构,但不是递归地处理每个新像素,而是显式地处理待编辑像素的堆栈。如果你知道你的目标是小区域,那么它本身可能就足够快了。

首先,我看不出你是如何检查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);

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