将颜色滤镜应用到位图对象

5
我发现了一段关于如何在C#中对Bitmap对象应用颜色滤镜的代码。问题在于它使用了不安全的代码来完成这个任务。有没有一种受控安全的方法来完成相同的事情?我知道我可以使用像AForge.NET或类似的库,但我希望有一种简单的方法来应用颜色滤镜。我只需要进行简单的颜色替换,将白色像素替换为黄色。有什么建议吗?
1个回答

4

您可以始终使用安全的GetPixel和SetPixel方法,但是当在许多像素上使用时它们会很慢,这就是您使用不安全方法使用指向位图内存的指针的原因。 不安全才是做到这一点的方法。

如果您的位图非常小,并且您不太关心性能,请使用GetPixel和SetPixel方法。

    private void ReplaceColor(Bitmap bitmap, Color originalColor, Color replacementColor)
    {
        for (var y = 0; y < bitmap.Height; y++)
        {
            for (var x = 0; x < bitmap.Width; x++)
            {
                if (bitmap.GetPixel(x, y) == originalColor)
                {
                    bitmap.SetPixel(x, y, replacementColor);
                }
            }
        }
    }

    private unsafe void ReplaceColorUnsafe(Bitmap bitmap, byte[] originalColor, byte[] replacementColor)
    {
        if (originalColor.Length != replacementColor.Length)
        {
            throw new ArgumentException("Original and Replacement arguments are in different pixel formats.");
        }

        if (originalColor.SequenceEqual(replacementColor))
        {
            return;
        }

        var data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size),
                                   ImageLockMode.ReadWrite,
                                   bitmap.PixelFormat);

        var bpp = Image.GetPixelFormatSize(data.PixelFormat);

        if (originalColor.Length != bpp)
        {
            throw new ArgumentException("Original and Replacement arguments and the bitmap are in different pixel format.");
        }

        var start = (byte*)data.Scan0;
        var end = start + data.Stride;

        for (var px = start; px < end; px += bpp)
        {
            var match = true;

            for (var bit = 0; bit < bpp; bit++)
            {
                if (px[bit] != originalColor[bit])
                {
                    match = false;
                    break;
                }
            }

            if (!match)
            {
                continue;
            }

            for (var bit = 0; bit < bpp; bit++)
            {
                px[bit] = replacementColor[bit];
            }
        }

        bitmap.UnlockBits(data);
    }

使用方法如下:

        this.ReplaceColor(myBitmap, Color.White, Color.Yellow); // SLOW

或者

        var orgRGB = new byte[] { 255, 255, 255 }; // White (in RGB format)
        var repRGB = new byte[] { 255, 255, 0 }; // Yellow (in RGB format)

        var orgARGB = new byte[] { 255, 255, 255, 255 }; // White (in ARGB format)
        var repARGB = new byte[] { 255, 255, 255, 0 }; // Yellow (in ARGB format)

        var orgRGBA = new byte[] { 255, 255, 255, 255 }; // White (in RGBA format)
        var repRGBA = new byte[] { 255, 255, 0, 255 }; // Yellow (in RGBA format)

        var orgBytes = orgRGB; // or ARGB or RGBA, depending on bitmap's pixel format
        var repBytes = repRGB; // or ARGB or RGBA, depending on bitmap's pixel format

        this.ReplaceColorUnsafe(myBitmap, orgBytes, repBytes); // FAST

我对这种方法的速度有些担心,但我尝试了一下,实际上它相当快。至少对于我所做的事情来说是这样!感谢这个解决方案! - Icemanind
位图位锁定是快速处理的常见方式。很高兴能帮助。 - SimpleVar
@VirtualBlackFox 那里不需要调用 .ToArgb() - SimpleVar
@YoryeNathan 请运行此代码 https://gist.github.com/vbfox/c0b6a3a497512d5aa29f 并告诉我你看到的颜色。然后添加一个 ToArgb() 的调用。 - Julien Roncaglia
@YoryeNathan 重载比较不仅比较颜色,还请参阅相应的MSDN页面上的注释:http://msdn.microsoft.com/en-us/library/system.drawing.color.op_equality.aspx。例如,名称也会进行比较,使得`Color.Red != Color.FromArgb(255, 0, 0)返回True`。WCF等效类没有同样的问题,但是他们没有在WinForms中更改(我猜是为了兼容性)。 - Julien Roncaglia
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接