如何在 windows 10 uwp 中有效地使用 WriteableBitmap.setpixel()?
How to use WriteableBitmap.setpixel() efficiently in windows 10 uwp?
我有一个场景
- 选择图像文件,然后使用 BitmapDecoder 转换
source 到 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。
首先,我看不出你如何检查 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);
但我认为你必须改变整个想法。使用位图时,递归算法不是最好的主意。
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 所建议的那样)您应该研究非递归洪水填充算法,尤其是当您有大位图时。递归算法将溢出堆栈以进行大量填充。 Wikipedia 上有一些相当简单的选项:https://en.wikipedia.org/wiki/Flood_fill#Alternative_implementations 一个简单的选项是保持与此处基本相同的结构,但不是递归处理每个新像素,而是显式处理待编辑像素的堆栈。如果您知道您的目标是小区域,那么它本身可能就足够快了。要支持更大的,您可能需要进一步优化。
我有一个场景
- 选择图像文件,然后使用 BitmapDecoder 转换 source 到 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。
首先,我看不出你如何检查 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);
但我认为你必须改变整个想法。使用位图时,递归算法不是最好的主意。
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 所建议的那样)您应该研究非递归洪水填充算法,尤其是当您有大位图时。递归算法将溢出堆栈以进行大量填充。 Wikipedia 上有一些相当简单的选项:https://en.wikipedia.org/wiki/Flood_fill#Alternative_implementations 一个简单的选项是保持与此处基本相同的结构,但不是递归处理每个新像素,而是显式处理待编辑像素的堆栈。如果您知道您的目标是小区域,那么它本身可能就足够快了。要支持更大的,您可能需要进一步优化。