如何在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库
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);
但是我认为你必须改变整个想法。当使用位图时,递归算法并不是最好的方法。