I have an image and i want to modify the hue of that particular image to specific value.
I know rgb to hsl and hsl to rgb conversion mathematical formula but I am not being able to implement this thing into c#.
Following is the pseudo:
for(x=0;x<image_width;x++)
{
for(y=0;y<image_height;y++)
{
Color oldColor=GetColorFromPixel(x,y);
Color newColor=ModifyHue(oldColor);
SetColorPixel(x,y,newColor);
}
}
Thanks
Due to the fact, that the Color structure already has a GetHue(), GetSaturation() and GetBrightness() it would be nice to also construct a color from these values. So i found the following code somewhere on the net a while ago (currently can't find it again, but it was from a Microsoft guy blog and he also had a test that steps through all KnownColor).
/// <summary>
/// Creates a Color from alpha, hue, saturation and brightness.
/// </summary>
/// <param name="alpha">The alpha channel value.</param>
/// <param name="hue">The hue value.</param>
/// <param name="saturation">The saturation value.</param>
/// <param name="brightness">The brightness value.</param>
/// <returns>A Color with the given values.</returns>
public static Color FromAhsb(int alpha, float hue, float saturation, float brightness)
{
if (0 > alpha || 255 < alpha)
{
throw new ArgumentOutOfRangeException("alpha", alpha,
"Value must be within a range of 0 - 255.");
}
if (0f > hue || 360f < hue)
{
throw new ArgumentOutOfRangeException("hue", hue,
"Value must be within a range of 0 - 360.");
}
if (0f > saturation || 1f < saturation)
{
throw new ArgumentOutOfRangeException("saturation", saturation,
"Value must be within a range of 0 - 1.");
}
if (0f > brightness || 1f < brightness)
{
throw new ArgumentOutOfRangeException("brightness", brightness,
"Value must be within a range of 0 - 1.");
}
if (0 == saturation)
{
return Color.FromArgb(alpha, Convert.ToInt32(brightness * 255),
Convert.ToInt32(brightness * 255), Convert.ToInt32(brightness * 255));
}
float fMax, fMid, fMin;
int iSextant, iMax, iMid, iMin;
if (0.5 < brightness)
{
fMax = brightness - (brightness * saturation) + saturation;
fMin = brightness + (brightness * saturation) - saturation;
}
else
{
fMax = brightness + (brightness * saturation);
fMin = brightness - (brightness * saturation);
}
iSextant = (int)Math.Floor(hue / 60f);
if (300f <= hue)
{
hue -= 360f;
}
hue /= 60f;
hue -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
if (0 == iSextant % 2)
{
fMid = hue * (fMax - fMin) + fMin;
}
else
{
fMid = fMin - hue * (fMax - fMin);
}
iMax = Convert.ToInt32(fMax * 255);
iMid = Convert.ToInt32(fMid * 255);
iMin = Convert.ToInt32(fMin * 255);
switch (iSextant)
{
case 1:
return Color.FromArgb(alpha, iMid, iMax, iMin);
case 2:
return Color.FromArgb(alpha, iMin, iMax, iMid);
case 3:
return Color.FromArgb(alpha, iMin, iMid, iMax);
case 4:
return Color.FromArgb(alpha, iMid, iMin, iMax);
case 5:
return Color.FromArgb(alpha, iMax, iMin, iMid);
default:
return Color.FromArgb(alpha, iMax, iMid, iMin);
}
}
With this function you are able to work within the HSB (or HSV) color presentation not within the HSL presentation. For further informations about their differences take a look at this wikipedia article.
If you just want to try out or performance is not critical, the easist way is to use the Bitmap.GetPixel and Bitmap.SetPixel methods:
var bm = new Bitmap(filename);
for(int x=0;x<bm.Width;x++)
{
for(y=0;y<bm.Height;y++)
{
Color oldColor=bm.GetPixel(x,y);
Color newColor=ModifyHue(oldColor);
bm.SetPixel(x,y,newColor);
}
}
If you want more performant operation, you should look at the Bitmap.LockBits method which fixes the entire bitmap on a memory location and lets you modify this memory directly.
Related
I am trying to code a Red Pass filer in C#. I might not be using the correct name for this, but I want to convert all the non-red pixel to black (0,0,0).
So, like how a high pass filter sets all frequencies below a giving setpoint to 0. I want to set all the non-red pixel to (0,0,0) aka Black.
My problem is that I am using images from a camera, so the red pixels are not (255,0,0) plus because of Light and shades on the object I am taking a picture of some of the red pixels are darker than others. Meaning this [Red Color] (https://i.stack.imgur.com/Hc42q.png) and this Red Color are on the same object in the image.
This is what I have Tried so for:
public bool IsRed(RGB color)
{
// The pixel is a shade of red if the red value is greater than the green and blue values
return color.Red > color.Green && color.Red > color.Blue;
}
public bool IsRed2(RGB color)
{
// The pixel is a shade of red if the red value is within the range of 128 to 255.
return color.Red >= 128 && color.Red <= 255;
}
public bool IsRed3(RGB color)
{
// Convert the RGB values to HSL.
double hue, saturation, lightness;
RGBToHSL(color.Red, color.Green, color.Blue, out hue, out saturation, out lightness);
// The pixel is a shade of red if the hue is within the range of 0 to 30 degrees or 330 to 360 degrees.
return (hue >= 0 && hue <= 30) || (hue >= 330 && hue <= 360);
}
// Converts the given RGB values to HSL.
private void RGBToHSL(byte red, byte green, byte blue, out double hue, out double saturation, out double lightness)
{
// Convert the RGB values to the range 0 to 1.
double r = red / 255.0;
double g = green / 255.0;
double b = blue / 255.0;
// Calculate the maximum and minimum RGB values.
double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));
// Calculate the lightness.
lightness = (max + min) / 2;
// Calculate the saturation.
if (max == min)
{
// The color is a shade of gray.
hue = 0;
saturation = 0;
}
else
{
// The color is not a shade of gray.
double d = max - min;
saturation = lightness > 0.5 ? d / (2 - max - min) : d / (max + min);
// Calculate the hue.
if (max == r)
{
hue = (g - b) / d + (g < b ? 6 : 0);
}
else if (max == g)
{
hue = (b - r) / d + 2;
}
else
{
hue = (r - g) / d + 4;
}
hue /= 6;
}
}
Update :
This is how I am getting the RGB values from the image:
private RGB[,] ImageToRGBByteArray(Bitmap _image, int height, int width)
{
Bitmap b = new Bitmap(_image);
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
/* GetBitsPerPixel just does a switch on the PixelFormat and returns the number */
BitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*the size of the image in bytes */
int size = bData.Stride * bData.Height;
Stride = bData.Stride;
/*Allocate buffer for image*/
byte[] data = new byte[size];
RGB[,] imagematrix = new RGB[height, width];
int[,] imageData = new int[height, width];
/*This overload copies data of /size/ into /data/ from location specified (/Scan0/)*/
Marshal.Copy(bData.Scan0, data, 0, size);
int i = 0;
ModifiedHistogram = null;
ImageData = null;
ImageRGBData = null;
Dictionary<RGB, int> modifiedHistogram = new Dictionary<RGB, int>();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
RGB tempValue = new RGB(data[i], data[i + 1], data[i + 2]);
imagematrix[y, x] = tempValue;
imageData[y, x] = (int)((data[i] + data[i + 1] + data[i + 2]) / 3D);
if (!modifiedHistogram.ContainsKey(tempValue))
modifiedHistogram.Add(tempValue, 1);
else
modifiedHistogram[tempValue]++;
i += BitsPerPixel / 8;
}
}
int maxValue = 0;
string color = string.Empty;
foreach (var kvp in modifiedHistogram)
{
if (kvp.Value > maxValue)
{
maxValue = kvp.Value;
color = kvp.Key.ColorName;
}
}
ImageData = imageData;
ImageRGBData = imagematrix;
ModifiedHistogram = modifiedHistogram;
return imagematrix;
}
So
data[i] = Red
data[i+2] = Green
data[i+3] = Blue
This is my Color class
public class RGB
{
public byte Red { get; set; }
public byte Green { get; set; }
public byte Blue { get; set; }
public RGB(byte red, byte green, byte blue)
{
Red = red;
Green = green;
Blue = blue;
SetColorName();
}
public override bool Equals(object obj)
{
RGB other = obj as RGB;
return other != null && other.ColorName == this.ColorName;
}
}
C# has a very convenient getHue method, but I can't find a setHue method. Is there one?
If not, I think the best way to define a color after changing the hue would be to convert the HSL value to RGB, and then set the RGB value. I know there are formulas on the internet for doing this, but how would I best go about performing this conversion from HSL to RGB using C#?
Thank You
To set the Hue you create a new Color, maybe from a given one by using GetHue and GetSaturation. See below for the getBrightness function!
I'm using this:
Color SetHue(Color oldColor)
{
var temp = new HSV();
temp.h = oldColor.GetHue();
temp.s = oldColor.GetSaturation();
temp.v = getBrightness(oldColor);
return ColorFromHSL(temp);
}
// A common triple float struct for both HSL & HSV
// Actually this should be immutable and have a nice constructor!!
public struct HSV { public float h; public float s; public float v;}
// the Color Converter
static public Color ColorFromHSL(HSV hsl)
{
if (hsl.s == 0)
{ int L = (int)hsl.v; return Color.FromArgb(255, L, L, L); }
double min, max, h;
h = hsl.h / 360d;
max = hsl.v < 0.5d ? hsl.v * (1 + hsl.s) : (hsl.v + hsl.s) - (hsl.v * hsl.s);
min = (hsl.v * 2d) - max;
Color c = Color.FromArgb(255, (int)(255 * RGBChannelFromHue(min, max,h + 1 / 3d)),
(int)(255 * RGBChannelFromHue(min, max,h)),
(int)(255 * RGBChannelFromHue(min, max,h - 1 / 3d)));
return c;
}
static double RGBChannelFromHue(double m1, double m2, double h)
{
h = (h + 1d) % 1d;
if (h < 0) h += 1;
if (h * 6 < 1) return m1 + (m2 - m1) * 6 * h;
else if (h * 2 < 1) return m2;
else if (h * 3 < 2) return m1 + (m2 - m1) * 6 * (2d / 3d - h);
else return m1;
}
Do not use the built-in GetBrightness method! It returns the same value (0.5f) for red, magenta, cyan, blue and yellow (!). This is better:
// color brightness as perceived:
float getBrightness(Color c)
{ return (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) / 256f; }
System.Drawing.Color is a value type, which are almost always made immutable, especially in frameworks. That's why you can't setHue on it, you can only construct a new value type with fields you need.
So, if you have a function that will give you RGB values for your HSB values, you can do it like this
Color oldColor = ...;
int red, green, blue;
FromHSB(oldColor.GetHue(), oldColor.GetSaturation(), oldColor.GetBrightness(), out red, out green out blue);
Color newColor = Color.FromArgb(oldColor.A, red, green, blue);
where FromHSB looks like this
void FromHSB(float hue, float saturation, float brightness, out int red, out int green, out int blue)
{
// ...
}
I am trying to convert a HSB Color to RGB. The way I am doing that is
System.Windows.Media.Color winColor = value;
System.Drawing.Color drawColor = System.Drawing.Color.FromArgb(winColor.R, winColor.G, winColor.B);
Hue = (byte)(drawColor.GetHue()*255);
Saturation = (byte)(drawColor.GetSaturation()*255);
Luminosity = (byte)(drawColor.GetBrightness()*255);
I find that when I have FF0000, it will be converted to H = 0, S = 255, L = 127 which converts to RGB FF0E0E. I think Luminosity should be 120? Or am I getting the whole HSB thing wrong? When I look at the color picker in Photoshop, Hue is 0-360 degrees, Saturation, Luminosity is 0-100%. I am having HSB values ranging from 0 - 255, am I doing it wrong?
Maybe you already look into this wikipedia article, but to make it clear.
There is a difference between HSL and HSB (aka HSV).
So you can't take the (B)rightness from the color class and use it like a (L)uminosity.
To get back from the Color class provided values GetHue(), GetSaturation() and GetBrightness() to a normal color you should give this extension method a chance.
/// <summary>
/// Creates a Color from alpha, hue, saturation and brightness.
/// </summary>
/// <param name="alpha">The alpha channel value.</param>
/// <param name="hue">The hue value.</param>
/// <param name="saturation">The saturation value.</param>
/// <param name="brightness">The brightness value.</param>
/// <returns>A Color with the given values.</returns>
public static Color FromAhsb(int alpha, float hue, float saturation, float brightness)
{
if (0 > alpha
|| 255 < alpha)
{
throw new ArgumentOutOfRangeException(
"alpha",
alpha,
"Value must be within a range of 0 - 255.");
}
if (0f > hue
|| 360f < hue)
{
throw new ArgumentOutOfRangeException(
"hue",
hue,
"Value must be within a range of 0 - 360.");
}
if (0f > saturation
|| 1f < saturation)
{
throw new ArgumentOutOfRangeException(
"saturation",
saturation,
"Value must be within a range of 0 - 1.");
}
if (0f > brightness
|| 1f < brightness)
{
throw new ArgumentOutOfRangeException(
"brightness",
brightness,
"Value must be within a range of 0 - 1.");
}
if (0 == saturation)
{
return Color.FromArgb(
alpha,
Convert.ToInt32(brightness * 255),
Convert.ToInt32(brightness * 255),
Convert.ToInt32(brightness * 255));
}
float fMax, fMid, fMin;
int iSextant, iMax, iMid, iMin;
if (0.5 < brightness)
{
fMax = brightness - (brightness * saturation) + saturation;
fMin = brightness + (brightness * saturation) - saturation;
}
else
{
fMax = brightness + (brightness * saturation);
fMin = brightness - (brightness * saturation);
}
iSextant = (int)Math.Floor(hue / 60f);
if (300f <= hue)
{
hue -= 360f;
}
hue /= 60f;
hue -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
if (0 == iSextant % 2)
{
fMid = (hue * (fMax - fMin)) + fMin;
}
else
{
fMid = fMin - (hue * (fMax - fMin));
}
iMax = Convert.ToInt32(fMax * 255);
iMid = Convert.ToInt32(fMid * 255);
iMin = Convert.ToInt32(fMin * 255);
switch (iSextant)
{
case 1:
return Color.FromArgb(alpha, iMid, iMax, iMin);
case 2:
return Color.FromArgb(alpha, iMin, iMax, iMid);
case 3:
return Color.FromArgb(alpha, iMin, iMid, iMax);
case 4:
return Color.FromArgb(alpha, iMid, iMin, iMax);
case 5:
return Color.FromArgb(alpha, iMax, iMin, iMid);
default:
return Color.FromArgb(alpha, iMax, iMid, iMin);
}
}
Update
So just to make things clear. My code above and the three methods within the Color class mentioned above are using the HSB (aka HSV) color model, but Photoshop uses the HSL color model.
In your comment you wrote that the parameters Hue = 0, Saturation = 1 and Brightness = 1 give you with the code above a red color and white in Photoshop. When you take a closer look at the differences of these modes this makes absolutely sense:
The HSL cylinder
(source: wikimedia.org)
In both models the hue acts as the same by using color Red as start and end point (zero and 360 degree).
By just looking at the hue you've got a red color.
The saturation defines how opaque the color is or how much the portion of a whitescale is.
So by setting it to one you say you want a full shiny red color.
The lighting now defines how much is the black and white part within your color. By setting it to zero you got black, one means white and 0.5 means perfect weighting.
So by setting it to one you say you want it as bright as possible which result in a white color.
The HSB cylinder
(source: wikimedia.org)
In both models the hue acts as the same by using color Red as start and end point (zero and 360 degree).
By just looking at the hue you've got a red color.
The saturation defines how opaque the color is or how much the portion of a whitescale is.
So by setting it to one you say you want a full shiny red color.
The brightness (or value) now defines how much is the black part within the color (not the white part).
So by setting it to one you say you want it full colored leading to a full shiny red color.
As you can see, Photoshop and the .Net framework (including my extension function) are using different coloring models. So you should check if you find somewhere an implementation of the other coloring model, a transformation or something else that gives you the results you need.
This works...modified from Java source code. The other answer is still HSL, This really is HSB to RGB (actually it's HSB/HSV to System.Windows.Media.Color as my return type)
public static Color HSBtoRGB(float hue, float saturation, float brightness)
{
int r = 0, g = 0, b = 0;
if (saturation == 0)
{
r = g = b = (int)(brightness * 255.0f + 0.5f);
}
else
{
float h = (hue - (float)Math.Floor(hue)) * 6.0f;
float f = h - (float)Math.Floor(h);
float p = brightness * (1.0f - saturation);
float q = brightness * (1.0f - saturation * f);
float t = brightness * (1.0f - (saturation * (1.0f - f)));
switch ((int)h)
{
case 0:
r = (int)(brightness * 255.0f + 0.5f);
g = (int)(t * 255.0f + 0.5f);
b = (int)(p * 255.0f + 0.5f);
break;
case 1:
r = (int)(q * 255.0f + 0.5f);
g = (int)(brightness * 255.0f + 0.5f);
b = (int)(p * 255.0f + 0.5f);
break;
case 2:
r = (int)(p * 255.0f + 0.5f);
g = (int)(brightness * 255.0f + 0.5f);
b = (int)(t * 255.0f + 0.5f);
break;
case 3:
r = (int)(p * 255.0f + 0.5f);
g = (int)(q * 255.0f + 0.5f);
b = (int)(brightness * 255.0f + 0.5f);
break;
case 4:
r = (int)(t * 255.0f + 0.5f);
g = (int)(p * 255.0f + 0.5f);
b = (int)(brightness * 255.0f + 0.5f);
break;
case 5:
r = (int)(brightness * 255.0f + 0.5f);
g = (int)(p * 255.0f + 0.5f);
b = (int)(q * 255.0f + 0.5f);
break;
}
}
return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b));
}
Since the other answers don't seem to work for me (and I didn't have the patience to see what's going on), I'm sharing my code for HSV->RGB conversion. It's based on the section "Alternate HSV conversion" on Wikipedia and is quite compact.
public static Color FromAhsv(byte alpha, float hue, float saturation, float value)
{
if (hue < 0f || hue > 360f)
throw new ArgumentOutOfRangeException(nameof(hue), hue, "Hue must be in the range [0,360]");
if (saturation < 0f || saturation > 1f)
throw new ArgumentOutOfRangeException(nameof(saturation), saturation, "Saturation must be in the range [0,1]");
if (value < 0f || value > 1f)
throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be in the range [0,1]");
int Component(int n)
{
var k = (n + hue / 60f) % 6;
var c = value - value * saturation * Math.Max(Math.Min(Math.Min(k, 4 - k), 1), 0);
var b = (int)Math.Round(c * 255);
return b < 0 ? 0 : b > 255 ? 255 : b;
}
return Color.FromArgb(alpha, Component(5), Component(3), Component(1));
}
You can use ColorHelper library for this:
using ColorHelper;
RGB rgb = new RGB(20, 20, 20);
HSV hsv = ColorConverter.RgbToHsv(rgb);
Links:
Github
Nuget
If you have a System.Drawing.Bitmap instance that contains a greyscale image, is there a built in way to "colourize" it with the influence of another colour?
For example, if you had a black and white (greyscale) picture of a coffee mug and you wanted to create separate images of red, green and purple versions programmatically.
I don't have a code example to give but here's a way to do this. Convert each pixel from RGB to HSV and change the Hue and Saturation component on each pixel. The Hue controls the Color. The Value should stay the same. The result will be a Bitmap with the same lightness and darkness but with a different color.
Edit: here's an example. Notice the Hue and Saturation update.
public static Color ColorFromAhsb(int a, float h, float s, float b)
{
if (0 > a || 255 < a)
{
throw new Exception("a");
}
if (0f > h || 360f < h)
{
throw new Exception("h");
}
if (0f > s || 1f < s)
{
throw new Exception("s");
}
if (0f > b || 1f < b)
{
throw new Exception("b");
}
if (0 == s)
{
return Color.FromArgb(a, Convert.ToInt32(b * 255),
Convert.ToInt32(b * 255), Convert.ToInt32(b * 255));
}
float fMax, fMid, fMin;
int iSextant, iMax, iMid, iMin;
if (0.5 < b)
{
fMax = b - (b * s) + s;
fMin = b + (b * s) - s;
}
else
{
fMax = b + (b * s);
fMin = b - (b * s);
}
iSextant = (int)Math.Floor(h / 60f);
if (300f <= h)
{
h -= 360f;
}
h /= 60f;
h -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
if (0 == iSextant % 2)
{
fMid = h * (fMax - fMin) + fMin;
}
else
{
fMid = fMin - h * (fMax - fMin);
}
iMax = Convert.ToInt32(fMax * 255);
iMid = Convert.ToInt32(fMid * 255);
iMin = Convert.ToInt32(fMin * 255);
switch (iSextant)
{
case 1:
return Color.FromArgb(a, iMid, iMax, iMin);
case 2:
return Color.FromArgb(a, iMin, iMax, iMid);
case 3:
return Color.FromArgb(a, iMin, iMid, iMax);
case 4:
return Color.FromArgb(a, iMid, iMin, iMax);
case 5:
return Color.FromArgb(a, iMax, iMin, iMid);
default:
return Color.FromArgb(a, iMax, iMid, iMin);
}
}
private void Form1_Load(object sender, EventArgs e)
{
var bmp = new Bitmap("c:\\bw.bmp");
foreach (int y in Enumerable.Range(0, bmp.Height))
{
foreach (int x in Enumerable.Range(0,bmp.Width))
{
var p = bmp.GetPixel(x, y);
var h = p.GetHue();
var c = ColorFromAhsb(p.A, p.GetHue() + 200, p.GetSaturation() + 0.5f, p.GetBrightness());
bmp.SetPixel(x, y, c);
}
}
pictureBox1.Image = bmp;
//bmp.Dispose();
}
If it's an 8 bit image, you can just use a different the palette (Image.Palette). That's essentially a lookup table that assigns a Color value to each possible pixel byte value. Much faster than changing all pixels in a loop.
See here
I have used this in the past. You are wanting to look specifically at ToSepia.
You may need to deconstruct this a bit but it has worked for me.
I'd create a copy of the original image and them put a separate semi transparent image of the desired color of the top.
Update: see example at http://www.codeproject.com/KB/cs/Merge_Images_in_C_.aspx
I am unsure of a built in way but, if you represent each colour as a float rather than a byte (255 becomes 1 - full intensity), multiplying the each channel with your desired colour should give the effect you are talking about.
(1,1,1) "white" * (1,0,0) "red" = (1,0,0) "red"
(0.5,0.5, 0.5) "grey" * (0,1,0) "green" = (0,0.5,0) "dark green"
You do need to apply this per pixel though.
This question already has answers here:
How do I adjust the brightness of a color?
(7 answers)
Closed 1 year ago.
Duplicate
How do I adjust the brightness of a color?
How do I determine darker or lighter color variant of a given color?
Programmatically Lighten a Color
Say I have
var c = Color.Red;
Now I want to create a new Color that is lighter or darker than that color. How can I do that without too much hassle?
ControlPaint.Light .Dark .DarkDark, etc.
Color lightRed = ControlPaint.Light( Color.Red );
I recently blogged about this. The main idea is to apply a given correction factor to each of the color components. The following static method modifies the brightness of a given color with a specified correction factor and produces a darker or a lighter variant of that color:
/// <summary>
/// Creates color with corrected brightness.
/// </summary>
/// <param name="color">Color to correct.</param>
/// <param name="correctionFactor">The brightness correction factor. Must be between -1 and 1.
/// Negative values produce darker colors.</param>
/// <returns>
/// Corrected <see cref="Color"/> structure.
/// </returns>
public static Color ChangeColorBrightness(Color color, float correctionFactor)
{
float red = (float)color.R;
float green = (float)color.G;
float blue = (float)color.B;
if (correctionFactor < 0)
{
correctionFactor = 1 + correctionFactor;
red *= correctionFactor;
green *= correctionFactor;
blue *= correctionFactor;
}
else
{
red = (255 - red) * correctionFactor + red;
green = (255 - green) * correctionFactor + green;
blue = (255 - blue) * correctionFactor + blue;
}
return Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
}
You can also do this using a Lerp function. There's one in XNA, but it's easy to write yourself.
See my answer to this similar question for a C# implementation.
The function lets you do this:
// make red 50% lighter:
Color.Red.Lerp( Color.White, 0.5 );
// make red 75% darker:
Color.Red.Lerp( Color.Black, 0.75 );
// make white 10% bluer:
Color.White.Lerp( Color.Blue, 0.1 );
Most of these methods do darken the color but they adjust the hue way to much so the result doesn't look very good. The best answer is to use Rich Newman's HSLColor class and adjust the luminosity.
public Color Darken(Color color, double darkenAmount) {
HSLColor hslColor = new HSLColor(color);
hslColor.Luminosity *= darkenAmount; // 0 to 1
return hslColor;
}
Taking the core method of #Pavel's answer I prepared the following two little extension methods for a more intuitive (at least for me) signature.
public static Color LightenBy(this Color color, int percent)
{
return ChangeColorBrightness(color, percent/100.0);
}
public static Color DarkenBy(this Color color, int percent)
{
return ChangeColorBrightness(color, -1 * percent / 100.0);
}
Here's some javascript code I use for lightening/darkening a given colour. You could use it as a base for an equivalent C# function
It works by calculating a distance from pure white of each of the RGB components and then adjusts this distance by the provided factor. The new distance is used to calculate the new colour. A factor of between 0 and 1 darkens, a factor higher than 1 lightens
function Darken( hexColor, factor )
{
if ( factor < 0 ) factor = 0;
var c = hexColor;
if ( c.substr(0,1) == "#" )
{
c = c.substring(1);
}
if ( c.length == 3 || c.length == 6 )
{
var i = c.length / 3;
var f; // the relative distance from white
var r = parseInt( c.substr(0, i ), 16 );
f = ( factor * r / (256-r) );
r = Math.floor((256 * f) / (f+1));
r = r.toString(16);
if ( r.length == 1 ) r = "0" + r;
var g = parseInt( c.substr(i, i), 16);
f = ( factor * g / (256-g) );
g = Math.floor((256 * f) / (f+1));
g = g.toString(16);
if ( g.length == 1 ) g = "0" + g;
var b = parseInt( c.substr( 2*i, i),16 );
f = ( factor * b / (256-b) );
b = Math.floor((256 * f) / (f+1));
b = b.toString(16);
if ( b.length == 1 ) b = "0" + b;
c = r+g+b;
}
return "#" + c;
}
You can also simply work on the RGB percentage to get it lighter or darker as you want, Here is an example for how to make a color darker x% than it is:
//_correctionfactory in percentage, e.g 50 = make it darker 50%
private Color DarkerColor(Color color, float correctionfactory = 50f)
{
const float hundredpercent = 100f;
return Color.FromArgb((int)(((float)color.R / hundredpercent) * correctionfactory),
(int)(((float)color.G / hundredpercent) * correctionfactory), (int)(((float)color.B / hundredpercent) * correctionfactory));
}
One more thing we can also reverse the process to be lighter instead, Only we getting the result of 255 - RGB and then multiply it by the percentage we want like the following example:
private Color LighterColor(Color color, float correctionfactory = 50f)
{
correctionfactory = correctionfactory / 100f;
const float rgb255 = 255f;
return Color.FromArgb((int)((float)color.R + ((rgb255 - (float)color.R) * correctionfactory)), (int)((float)color.G + ((rgb255 - (float)color.G) * correctionfactory)), (int)((float)color.B + ((rgb255 - (float)color.B) * correctionfactory))
);
}
Hope that helps.
I made a site that does this colorglower.com You can check it out to see a demo.
Here's the javascript code i used.
function lighten(color) {
// convert to decimal and change luminosity
var luminosity = 0.01
var computedColors = new Array();
var newColor = "#",
c, i, n, black = 0,
white = 255;
for (n = 0; n < 10; n++) {
for (i = 0; i < 3; i++) {
c = parseInt(color.substr(i * 2, 2), 16);
c = Math.round(Math.min(Math.max(black, c + (luminosity * white)), white)).toString(16);
newColor += ("00" + c).substr(c.length);
}
computedColors[n] = newColor;
var arrayUnique = checkIfArrayIsUnique(computedColors);
if (arrayUnique == false) {
computedColors.pop();
break;
}
computedColors[n] = newColor;
newColor = "#";
luminosity += calcPercentage();
}
return computedColors;
}
What this code does is it receives a hex color and then it outputs 10 lightest color versions of it and puts in in the array. You can change the luminosity to whatever you like to adjust the shade percentage. To darken the colors you just need to change:
luminosity -= calcPercentage();
Using HSI converter library(search google). And then, adjust I channel for lighter/darker color.
I changed Pavel Vladov function to modify even RGB component, to get shades on any combination of R/G/B directions:
Public Function ChangeColorShades(color As Color, correctionFactor As Single, bR As Boolean, bG As Boolean, bB As Boolean) As Color
Dim red As Single = CSng(color.R)
Dim green As Single = CSng(color.G)
Dim blue As Single = CSng(color.B)
If (correctionFactor < 0) Then
correctionFactor = 1 + correctionFactor
If bR Then
red *= correctionFactor
End If
If bG Then
green *= correctionFactor
End If
If bB Then
blue *= correctionFactor
End If
Else
If bR Then
red = (255 - red) * correctionFactor + red
End If
If bG Then
green = (255 - green) * correctionFactor + green
End If
If bB Then
blue = (255 - blue) * correctionFactor + blue
End If
End If
Return color.FromArgb(color.A, CInt(red), CInt(green), CInt(blue))
End Function