如何摆脱使用 SkiaSharp 创建的 ImageSource 中的伪影
How can I get rid of artifacts in ImageSource created with SkiaSharp
我创建了一个应用程序,我想在其中在 google 地图上显示文本。我选择使用自定义标记,但它们只能是图像,所以我决定使用 SkiaSharp 从我的文本创建图像。
private static ImageSource CreateImageSource(string text)
{
int numberSize = 20;
int margin = 5;
SKBitmap bitmap = new SKBitmap(30, numberSize + margin * 2, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
SKCanvas canvas = new SKCanvas(bitmap);
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
TextSize = numberSize,
Color = SKColors.Red,
StrokeWidth = 1,
};
canvas.DrawText(text.ToString(), 0, numberSize, paint);
SKImage skImage = SKImage.FromBitmap(bitmap);
SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100);
return ImageSource.FromStream(data.AsStream);
}
然而,我创建的图像在结果图像的顶部有丑陋的伪影,我的感觉是,如果我创建多个图像,它们会变得更糟。
我构建了一个示例应用程序,它显示了我用来绘制文本的工件和代码。在这里能找到它:
https://github.com/hot33331/SkiaSharpExample
我怎样才能摆脱那些文物。我是不是用错了skia?
我以前看到过,那是因为传递给 SkiaSharp 的内存没有清零。不过,作为一种优化,Skia 假设传递给它的内存块是预先置零的。结果,如果您的第一个操作是 clear,它会忽略该操作,因为它认为状态已经是 clean。要解决此问题,您可以手动将传递给 SkiaSharp 的内存归零。
public static SKSurface CreateSurface(int width, int height)
{
// create a block of unmanaged native memory for use as the Skia bitmap buffer.
// unfortunately, this may not be zeroed in some circumstances.
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(width * height * 4);
byte[] empty = new byte[width * height * 4];
// copy in zeroed memory.
// maybe there's a more sanctioned way to do this.
System.Runtime.InteropServices.Marshal.Copy(empty, 0, buff, width * height * 4);
// create the actual SkiaSharp surface.
var colorSpace = CGColorSpace.CreateDeviceRGB();
var bContext = new CGBitmapContext(buff, width, height, 8, width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo);
var surface = SKSurface.Create(width, height, SKColorType.Rgba8888, SKAlphaType.Premul, bitmap.Data, width * 4);
return surface;
}
编辑:顺便说一句,我认为这是 SkiaSharp 中的错误。为您创建缓冲区的 samples/apis 可能应该将其清零。根据平台的不同,可能很难重现,因为内存分配的行为不同。或多或少可能会为您提供未触及的记忆。
我从 Matthew Leibowitz 在 SkiaSharp GitHub 上得到了以下答案:
很可能你没有先清除 canvas/bitmap。
您可以选择 bitmap.Erase(SKColors.Transparent) 或 canvas.Clear(SKColors.Transparent)(您可以使用任何颜色)。
原因在于性能。创建新位图时,计算机无法知道您想要什么背景颜色。所以,如果它是透明的并且你想要白色,那么将有两个绘制操作来清除像素(这对于大图像来说可能非常昂贵)。
位图分配时,提供了内存,但实际数据未动。如果之前有任何东西(将会有),此数据将显示为彩色像素。
我创建了一个应用程序,我想在其中在 google 地图上显示文本。我选择使用自定义标记,但它们只能是图像,所以我决定使用 SkiaSharp 从我的文本创建图像。
private static ImageSource CreateImageSource(string text)
{
int numberSize = 20;
int margin = 5;
SKBitmap bitmap = new SKBitmap(30, numberSize + margin * 2, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
SKCanvas canvas = new SKCanvas(bitmap);
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
TextSize = numberSize,
Color = SKColors.Red,
StrokeWidth = 1,
};
canvas.DrawText(text.ToString(), 0, numberSize, paint);
SKImage skImage = SKImage.FromBitmap(bitmap);
SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100);
return ImageSource.FromStream(data.AsStream);
}
然而,我创建的图像在结果图像的顶部有丑陋的伪影,我的感觉是,如果我创建多个图像,它们会变得更糟。
我怎样才能摆脱那些文物。我是不是用错了skia?
我以前看到过,那是因为传递给 SkiaSharp 的内存没有清零。不过,作为一种优化,Skia 假设传递给它的内存块是预先置零的。结果,如果您的第一个操作是 clear,它会忽略该操作,因为它认为状态已经是 clean。要解决此问题,您可以手动将传递给 SkiaSharp 的内存归零。
public static SKSurface CreateSurface(int width, int height)
{
// create a block of unmanaged native memory for use as the Skia bitmap buffer.
// unfortunately, this may not be zeroed in some circumstances.
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(width * height * 4);
byte[] empty = new byte[width * height * 4];
// copy in zeroed memory.
// maybe there's a more sanctioned way to do this.
System.Runtime.InteropServices.Marshal.Copy(empty, 0, buff, width * height * 4);
// create the actual SkiaSharp surface.
var colorSpace = CGColorSpace.CreateDeviceRGB();
var bContext = new CGBitmapContext(buff, width, height, 8, width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo);
var surface = SKSurface.Create(width, height, SKColorType.Rgba8888, SKAlphaType.Premul, bitmap.Data, width * 4);
return surface;
}
编辑:顺便说一句,我认为这是 SkiaSharp 中的错误。为您创建缓冲区的 samples/apis 可能应该将其清零。根据平台的不同,可能很难重现,因为内存分配的行为不同。或多或少可能会为您提供未触及的记忆。
我从 Matthew Leibowitz 在 SkiaSharp GitHub 上得到了以下答案:
很可能你没有先清除 canvas/bitmap。
您可以选择 bitmap.Erase(SKColors.Transparent) 或 canvas.Clear(SKColors.Transparent)(您可以使用任何颜色)。
原因在于性能。创建新位图时,计算机无法知道您想要什么背景颜色。所以,如果它是透明的并且你想要白色,那么将有两个绘制操作来清除像素(这对于大图像来说可能非常昂贵)。
位图分配时,提供了内存,但实际数据未动。如果之前有任何东西(将会有),此数据将显示为彩色像素。