Introduction
I remember how I used to love MS PhotoEditor that used to be included in MS Office 2000, for it's a simple and efficient tool when you want to make an image with transparent parts by eliminating a specific color from the entire image. Well, this article will show you how this is made possible.
Background
The idea behind this process lies in reading the image pixel by pixel, and for each pixel, a further break down is made for the three basic components of each color (ARGB): Alpha, Red, Green, Blue, respectively. We don't care much about reading alpha in this example, but will be when it comes to writing back the image pixels. After reading the pixel and getting its RGB components, we now set the upper and lower limits for each color by adding the tolerance value to composite the upper limit and subtracting it from the original value to come up with the lower color limit. Eventually, we decide whether or not a color lies between the boundaries of the range we set earlier, and if so, the new color is applicable to that pixel. In the case of transparency, the three color components don't matter anymore, so whatever they are, we just raise the Alpha
factor to its maximum value, i.e., 255 DEC or FF HEX.
Using the Code
According to the tolerance value, we first define the limits of each color of the original color passed to our function.
iR_Min = Math.Max((int)_colorOld.R - _tolerance, 0);
iR_Max = Math.Min((int)_colorOld.R + _tolerance, 255);
iG_Min = Math.Max((int)_colorOld.G - _tolerance, 0);
iG_Max = Math.Min((int)_colorOld.G + _tolerance, 255);
iB_Min = Math.Max((int)_colorOld.B - _tolerance, 0);
iB_Max = Math.Min((int)_colorOld.B + _tolerance, 255);
Then, we loop through the image pixels on a row, column basis:
for (int x = 0; x < bmap.Width; x++)
{
for (int y = 0; y < bmap.Height; y++)
{
c = bmap.GetPixel(x, y);
...
Once a pixel is read, we check whether or not it falls within the range of our original color values defined earlier:
if(
(c.R >= iR_Min && c.R <= iR_Max) &&
(c.G >= iG_Min && c.G <= iG_Max) &&
(c.B >= iB_Min && c.B <= iB_Max)
)
If it is a match, then we make another check, but this time, for writing the pixel, and the check is now about whether the replacing color is transparent or not.
if(_colorNew == Color.Transparent)
bmap.SetPixel(x, y, Color.FromArgb(0,
_colorNew.R,
_colorNew.G,
_colorNew.B));
else
bmap.SetPixel(x, y, Color.FromArgb(c.A,
_colorNew.R,
_colorNew.G,
_colorNew.B));
As you can see, if the new color is transparent, we simply decrease the alpha to 0
; otherwise, we use the alpha value fetched from the original image, c.A
.
Points of Interest
When trying to pick a color from the PictureBox
, the MouseEventArgs
coordinates are not very accurate and are still not fixed in this article, and I'm hoping that together, we can work something out. Here's how it is being managed right now...
private void pbPreview_MouseMove(object sender, MouseEventArgs e)
{
if (bPicking)
{
Bitmap bmTemp = (Bitmap)((PictureBox)sender).Image;
pnlOldColor.BackColor = bmTemp.GetPixel(e.X,e.Y);
}
}