Search code examples
skiasharp

Ignore transparent pixels for non-separable blend modes


I'm using non-separable blend modes like this

SKColorFilter.CreateBlendMode(SKColors.Red, SKBlendMode.Color)

to replace "colored" pixel for an image (e.g. eye color).

Unfortunately in this sample this will also transform transparent pixels to red. Is there are way to ignore transparent pixel for non-separable blend modes?

EDIT

This is the workaround I ended up with:

public static SKImage Colorize(this SKImage image, SKColor color)
{
    using SKBitmap bitmap = SKBitmap.FromImage(image);
    using SKCanvas canvas = new(bitmap);

    using SKPaint paint = new()
    {
        ImageFilter = SKImageFilter.CreateColorFilter(SKColorFilter.CreateBlendMode(color, SKBlendMode.Color)),
        BlendMode = SKBlendMode.SrcIn,
    };

    canvas.DrawImage(image, SKRect.Create(image.Width, image.Height), paint);

    return SKImage.FromBitmap(bitmap);
}

Solution

  • If I understand you correctly, then when initializing SKPaint with your color filter you'd set Blend mode to SrcIn, the transparency of canvas would work as mask for what you are drawing:

        new SKPaint
        {
            ...
            ColorFilter = SKColorFilter.CreateBlendMode(SKColors.Red, SKBlendMode.Color),
            BlendMode = SKBlendMode.SrcIn
        }
    

    Edit:

    As per comment, solution using just the ApplyImageFilter method might look like this:

    public static SKImage Colorize(SKImage image, SKColor color)
    {
        var filter = SKImageFilter.CreateBlendMode(SKBlendMode.DstIn,
            SKImageFilter.CreateColorFilter(SKColorFilter.CreateBlendMode(color, SKBlendMode.Color))
        );
        return image.ApplyImageFilter(filter, image.Info.Rect, image.Info.Rect, out _, out SKPointI _);
    }
    

    Note the SKBlendMode is DstIn here as now the image itself is the background for the filter.