I found this code on how to apply a color filter to a Bitmap object in C#. The problem with it is it uses unsafe code to accomplish this. Is there a managed, safe way to do this same thing? I know I could use a library like AForge.NET or something similar, but I'm hoping there is a simple way to just apply a color filter. All I need is simple color replacement, replacing white pixels with yellow. Any suggestions?
Applying a color filter to a Bitmap object
You could always use the safe GetPixel and SetPixel methods, but they are slow when used on many pixels, which is the reason you use unsafe method to use pointers to the bitmap's memory. Unsafe is the way to do it.
If your bitmap is very small and you don't care about performance all that much, use the GetPixel and SetPixel methods.
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);
}
Usage would be:
this.ReplaceColor(myBitmap, Color.White, Color.Yellow); // SLOW
OR
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
I was a little apprehensive about the speed of this method, but I tried it out and it actually was quite fast. At least for what I am doing! Thanks for this solution! –
Lily
Bitmap bitlocking is the common way to do it fast. Glad to help. –
Transpontine
Is this something I could use parallel extensions for in order to make it work twice as fast? –
Lily
@icemanind Only do if you know what you're doing. Wouldn't suggest it. In general, parallel is for big collections iterations and interpolations, plinqing... For other things you better of use threading or bg workers. In this case I would choose none. –
Transpontine
@VirtualBlackFox No need to call
.ToArgb()
there. –
Transpontine @Transpontine Please run this code gist.github.com/vbfox/c0b6a3a497512d5aa29f and tell me the color you see. Then add a call to
ToArgb()
. –
Calvano @Transpontine The overload compare more than just the colors, see the note on the corresponding MSDN page : msdn.microsoft.com/en-us/library/… the names are also compared for example making
Color.Red != Color.FromArgb(255, 0, 0)
return True
. The WCF equivalent class don't have the same problem but they didn't change this in WinForms (for compatiblity reasons I guess). –
Calvano For me this line did not work: var bpp = Image.GetPixelFormatSize(data.PixelFormat); It returns bits, when the rest of the algorithm expect bytes. So I had to split it by 8 –
Thrust
© 2022 - 2024 — McMap. All rights reserved.