在 SkiaSharp 中重新映射任意灰度颜色

Remap arbitrary grey scale colors in SkiaSharp

我正在构建一个程序来生成热图。我正在利用为 GDI+ 编写的代码,但是为 various reasons I've decided to move it to SkiaSharp 编写的代码。

我目前有一个灰度图块,其中白色代表最大值,纯黑色是透明的。在 GDI+/C# 中,我可以快速使用 ColorMapSetRemapTable。我看过 ColorFilter,但它使用矩阵变换,而不是我有点随意的重新映射。

SkiaSharp 有类似的东西吗?或者我应该手动遍历每个像素并重新着色吗?

使用 GDI+ 的原始代码:

ImageAttributes imageAttributes = new ImageAttributes();
ColorMap[] remapTable = new ColorMap[256];
Color[] scale = IncandescentHeatScale();

for (int i = 0; i < 256; i++)
{
    remapTable[i] = new ColorMap()
    {
        OldColor = Color.FromArgb(i, i, i),
        NewColor = scale[i]
    };
}

imageAttributes.SetRemapTable(remapTable);

var outTile = new Bitmap(TileSize, TileSize);
using (var g = Graphics.FromImage(outTile))
{
    g.DrawImage(tile, new Rectangle(0, 0, TileSize, TileSize), padding, padding, TileSize, TileSize, GraphicsUnit.Pixel, imageAttributes);
}

相关函数,供参考:

static Color[] GetHeatScale(float[] points, Color[] colors)
{
    var bm = new Bitmap(256, 1);
    using (Graphics g = Graphics.FromImage(bm))
    {
        LinearGradientBrush brush = new LinearGradientBrush(new Point(0, 0), new Point(256, 0), colors[0], colors[colors.Length - 1]);
        var cb = new ColorBlend();
        cb.Colors = colors;
        cb.Positions = points;
        brush.InterpolationColors = cb;
        g.FillRectangle(brush, 0, 0, 256, 1);
    }
    return Enumerable.Range(0, 256).Select(x => bm.GetPixel(x, 0)).ToArray();
}


static Color[] IncandescentHeatScale()
{
    float[] points = new float[] { 0.0f, 0.333f, 0.6666f, 1f };
    Color[] colors = new Color[] { Color.Black, Color.DarkRed, Color.Yellow, Color.White };
    return GetHeatScale(points, colors);
}

是的Skia/SkiaSharp会让你变换颜色。除了使用矩阵变换,您还可以使用颜色重映射表。

使用 SKColorFilter.CreateTable(alphaRemapTable, redRemapTable, blueRemapTable, greeenRemapTable)

创建 SKColorFilter

这些表是字节数组,256 个元素长。重新映射的工作原理是将起始颜色的分量用作索引来查找它应该重新映射到的值,例如。 10 red 转换为红色数组中索引 10 处的值。

将 null 传递给任何参数将使该颜色分量保持不变。

进一步阅读:https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/effects/color-filters

这是我为所有实际上只想快速复制一些代码的人提供的解决方案。我知道这个问题已经有两年了,但这是您在这个主题上唯一能找到的东西。此代码将 SKBitmap 转换为彩色位图。

public Bitmap Colorize(SKBitmap bitmap, SKImageInfo info)
    {
        using (SKSurface surface = SKSurface.Create(info))
        {
            SKCanvas canvas = surface.Canvas;

            using (SKPaint paint = new SKPaint())
            {
                paint.ColorFilter = CreatePaletteIndex_skia(255);

                canvas.DrawBitmap(bitmap, info.Rect, paint);
            }

            using (SKImage image = surface.Snapshot())
            using (SKData data = image.Encode(SKEncodedImageFormat.Png, 100))
            using (MemoryStream memoryStream = new MemoryStream(data.ToArray()))
            {
                Bitmap bm = new Bitmap(memoryStream, false);
                return bm;
            }

        }
     }

    private static SKColorFilter CreatePaletteIndex_skia(byte Alpha)
    {
        byte[] R = new byte[256];
        byte[] G = new byte[256];
        byte[] B = new byte[256];
        byte[] A = new byte[256];

        string dirExe = System.AppDomain.CurrentDomain.BaseDirectory;

        string colorImg = "ColorPalette.png";
        string pathColorPalette = dirExe + "Content\" + colorImg;

        Bitmap Palette = (Bitmap)Bitmap.FromFile(pathColorPalette);
        // Loop through each pixel and create a new color mapping
        for (int X = 0; X <= 255; X++)
        {
            R[X] = Palette.GetPixel(X, 0).R;
            G[X] = Palette.GetPixel(X, 0).G;
            B[X] = Palette.GetPixel(X, 0).B;
            A[X] = Alpha;
        }

        SKColorFilter colorFilter = SKColorFilter.CreateTable(A, R, G, B);

        return colorFilter;
    }

您只需将一张 256 像素宽的渐变图片(gradient) 放入您的项目的文件夹中,然后通过变量 pathColorPalette link 它。

代码基于这个项目,关于如何在没有 skia 的情况下做到这一点:http://dylanvester.com/2015/10/creating-heat-maps-with-net-20-c-sharp/