You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用SkiaSharp实现指定颜色区域的泛洪填充(Flood Fill)?

SkiaSharp实现指定颜色区域的颜色修改方案

一、全图替换指定标记色(高效版)

你的现有代码会修改全图颜色,是因为CreateTable滤镜会替换所有像素的RGB通道。要只修改标记色区域,推荐用SkiaSharp的颜色滤镜组合实现,利用GPU加速,性能远高于逐像素遍历。

代码实现

SKBitmap sourceBitmap = Image; // 你的原始图片
SKColor targetMarkerColor = SKColors.Magenta; // 预设的可修改区域标记色
SKColor newColor = color; // 用户选择的目标颜色

var paint = new SKPaint();

// 1. 创建蒙版滤镜:仅保留标记色区域,其余转为透明
var maskFilter = SKColorFilter.CreateColorThreshold(
    SKColorSpace.CreateSrgb(),
    targetMarkerColor.ToVector4(),
    SKColorSpace.CreateSrgb(),
    new SKVector4(20, 20, 20, 0), // RGB容差,根据需求调整
    SKColorFilterColorThresholdStyle.Replace,
    SKColors.Transparent.ToVector4(),
    SKColors.White.ToVector4()
);

// 2. 创建颜色替换滤镜:将所有像素转为目标颜色
var colorReplaceFilter = SKColorFilter.CreateTable(
    null,
    Enumerable.Repeat((byte)newColor.Red, 256).ToArray(),
    Enumerable.Repeat((byte)newColor.Green, 256).ToArray(),
    Enumerable.Repeat((byte)newColor.Blue, 256).ToArray()
);

// 3. 组合滤镜:先提取标记色蒙版,再替换颜色
paint.ColorFilter = SKColorFilter.CreateCompose(colorReplaceFilter, maskFilter);

// 先绘制原图,再叠加滤镜后的图层,仅标记色区域会被替换
canvas.DrawBitmap(sourceBitmap, new SKPoint(drawX, drawY));
canvas.DrawBitmap(sourceBitmap, new SKPoint(drawX, drawY), paint);

二、逐像素遍历替换标记色(简单直接版)

如果不需要高性能,或者需要更灵活的颜色判断逻辑,逐像素遍历是最直观的方案。

代码实现

SKBitmap sourceBitmap = Image;
SKColor targetMarkerColor = SKColors.Magenta;
SKColor newColor = color;

sourceBitmap.LockPixels(); // 锁定像素缓冲区,提升读写性能
try
{
    for (int y = 0; y < sourceBitmap.Height; y++)
    {
        for (int x = 0; x < sourceBitmap.Width; x++)
        {
            SKColor currentColor = sourceBitmap.GetPixel(x, y);
            // 精准匹配标记色,若需要容差可计算颜色差异
            if (currentColor == targetMarkerColor)
            {
                sourceBitmap.SetPixel(x, y, newColor);
            }
        }
    }
}
finally
{
    sourceBitmap.UnlockPixels(); // 必须解锁,否则位图可能损坏
}

canvas.DrawBitmap(sourceBitmap, new SKPoint(drawX, drawY), paint);

带容差的颜色判断

如果标记色存在轻微色差,可通过计算颜色距离调整判断逻辑:

// 计算RGB通道差异的平方和(避免开根号,提升性能)
int colorDiff = (currentColor.Red - targetMarkerColor.Red) * (currentColor.Red - targetMarkerColor.Red) +
                (currentColor.Green - targetMarkerColor.Green) * (currentColor.Green - targetMarkerColor.Green) +
                (currentColor.Blue - targetMarkerColor.Blue) * (currentColor.Blue - targetMarkerColor.Blue);

// 设定容差阈值(示例为2500,对应RGB差值各50左右)
if (colorDiff < 2500)
{
    sourceBitmap.SetPixel(x, y, newColor);
}

三、泛洪填充(仅替换连通的标记色区域)

如果需要实现点击某点后,填充连通的标记色区域(类似画图工具的油漆桶),可以用队列式泛洪算法:

代码实现

void FloodFill(SKBitmap bitmap, int startX, int startY, SKColor targetColor, SKColor newColor)
{
    // 边界判断
    if (startX < 0 || startX >= bitmap.Width || startY < 0 || startY >= bitmap.Height)
        return;

    SKColor originalColor = bitmap.GetPixel(startX, startY);
    // 若当前颜色不是目标标记色,或已替换为新颜色,直接返回
    if (originalColor != targetColor || originalColor == newColor)
        return;

    Queue<SKPoint> pixelQueue = new Queue<SKPoint>();
    pixelQueue.Enqueue(new SKPoint(startX, startY));
    bitmap.SetPixel(startX, startY, newColor);

    while (pixelQueue.Count > 0)
    {
        SKPoint point = pixelQueue.Dequeue();
        int x = (int)point.X;
        int y = (int)point.Y;

        // 检查并填充上方像素
        if (y > 0 && bitmap.GetPixel(x, y - 1) == targetColor)
        {
            bitmap.SetPixel(x, y - 1, newColor);
            pixelQueue.Enqueue(new SKPoint(x, y - 1));
        }
        // 下方
        if (y < bitmap.Height - 1 && bitmap.GetPixel(x, y + 1) == targetColor)
        {
            bitmap.SetPixel(x, y + 1, newColor);
            pixelQueue.Enqueue(new SKPoint(x, y + 1));
        }
        // 左方
        if (x > 0 && bitmap.GetPixel(x - 1, y) == targetColor)
        {
            bitmap.SetPixel(x - 1, y, newColor);
            pixelQueue.Enqueue(new SKPoint(x - 1, y));
        }
        // 右方
        if (x < bitmap.Width - 1 && bitmap.GetPixel(x + 1, y) == targetColor)
        {
            bitmap.SetPixel(x + 1, y, newColor);
            pixelQueue.Enqueue(new SKPoint(x + 1, y));
        }
    }
}

// 使用方式
SKBitmap sourceBitmap = Image;
int clickX = 100; // 用户点击的X坐标
int clickY = 100; // 用户点击的Y坐标

sourceBitmap.LockPixels();
try
{
    FloodFill(sourceBitmap, clickX, clickY, targetMarkerColor, newColor);
}
finally
{
    sourceBitmap.UnlockPixels();
}

canvas.DrawBitmap(sourceBitmap, new SKPoint(drawX, drawY), paint);

方案选择建议

  • 全图替换标记色:优先用颜色滤镜组合方案,GPU加速性能优异,适合大图片;
  • 简单场景或需要自定义颜色判断:用逐像素遍历方案;
  • 连通区域填充:用泛洪填充算法,若追求更高性能可优化为扫描线泛洪算法。

内容的提问来源于stack exchange,提问作者Justin Greywolf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 10:54:14