I have a color of class Microsoft.Xna.Framework.Color. How can I change its hue (or get a new color with slightly different hue). Should I convert it to System.Drawing.Color, then somehow change it and convert back? I can't find any useful information on this anywhere.
EDIT
Example:
I have red color R:255, G:0, B:0. Now I want to get slightly more orange color. Then if I get this color and transform it again I'll get even more orange color, then after some transformations I'll go to yellow, green etc. I don't know exact values of ARGB of each color and I don't need them. I just need to change hue of a color by some factor (for example 10 degrees).
You should use the A R G B properties and change the values to get different nyansers.
For example:
Color color = new Color(0,0,0);
//Then you can change the argb properties:
color.A = 10;
color.R = 15;
color.G = 9;
color.B = 25;
If I understand you need something like this:
public static class Utilities
{
public static void Increase(this Color color, int value)
{
if(color.R >= color.G && color.R >= color.B)
color.R += value;
else if(color.G >= color.R && color.G >= color.B)
color.G += value;
else
color.B += value;
}
public static void Decrease(this Color color, int value)
{
if(color.R <= color.G && color.R <= color.B)
color.R -= value;
else if(color.G <= color.R && color.G <= color.B)
color.G -= value;
else
color.B -= value;
}
}
Then:
Color myColor = new Color(10,0,0);
myColor.Increase(10);
//or
myColor.Decrease(10);
according to this documentation, you can pass whatever RGB(A) values you want into the XNA color class constructor. You can also use the R, B, and G properties to change them afterwards
example:
Color myColor = new Color(150, 100, 100);
myColor.R = 200
that example will change a red to a slightly deeper red.
An example of making a color go from Red to orange to yellow to green would be
Color myColor = new Color(255, 0, 0);
for(int i=0; i<255; i++)
{
myColor.R--;
myColor.G++
}
Red and Green make yellow, so higher numbers of red will make it redder, higher numbers of green will make it greener. same of both numbers make it redder.
You can change color incrementally in other ways too, so long as you know how the primary colors of light work.
You're never ever going to find a function called Color.MakeRedder() or Color.MakeGreener() It will always focus on some sort of mathmatical representation of the color, (RBG is most common but there are other representations)
If you want to convert Hue to RBG Here is a guide on how to do it
What would probably be easiest is to keep track of a System.Drawing.Color class as your base color class, and modify your XNA Color Class based on your System.Drawing.Color class accordingly.
If you want to get really adventurous, you can see if it is possible to make a class that extends(inherits from) Microsoft.Xna.Framework.Color, override the R, G, and B properties, so that they are based off of an underlying System.Drawing.Color object
I did some research and found this post which has C++ code:
http://www.cs.rit.edu/~ncs/color/t_convert.html
I've modified the code to be C#, to have an IncreaseHueBy method, and to fix a few bugs:
public static void IncreaseHueBy(ref Color color, float value, out float hue)
{
float h, s, v;
RgbToHsv(color.R, color.G, color.B, out h, out s, out v);
h += value;
float r, g, b;
HsvToRgb(h, s, v, out r, out g, out b);
color.R = (byte)(r);
color.G = (byte)(g);
color.B = (byte)(b);
hue = h;
}
static void RgbToHsv(float r, float g, float b, out float h, out float s, out float v)
{
float min, max, delta;
min = System.Math.Min(System.Math.Min(r, g), b);
max = System.Math.Max(System.Math.Max(r, g), b);
v = max; // v
delta = max - min;
if (max != 0)
{
s = delta / max; // s
if (r == max)
h = (g - b) / delta; // between yellow & magenta
else if (g == max)
h = 2 + (b - r) / delta; // between cyan & yellow
else
h = 4 + (r - g) / delta; // between magenta & cyan
h *= 60; // degrees
if (h < 0)
h += 360;
}
else
{
// r = g = b = 0 // s = 0, v is undefined
s = 0;
h = -1;
}
}
static void HsvToRgb(float h, float s, float v, out float r, out float g, out float b)
{
// Keeps h from going over 360
h = h - ((int)(h / 360) * 360);
int i;
float f, p, q, t;
if (s == 0)
{
// achromatic (grey)
r = g = b = v;
return;
}
h /= 60; // sector 0 to 5
i = (int)h;
f = h - i; // factorial part of h
p = v * (1 - s);
q = v * (1 - s * f);
t = v * (1 - s * (1 - f));
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
break;
}
}
I tested it by using a value of 1 to increase the hue by 1 every frame and it worked fairly well. Notice there may be some rounding errors.
Related
Need to get middle color beetwen startColor and endColor.
If startColor is "red", endColor is "green", then need to get yellow.
If startColor is "red", endColor is "yellow", then need to get orange and so on.
i've started from default red(start) and green(end) colors.
i have found some formula, in most cases return right color but not always, so need to calculte right percent step.
As you can see from image additional colors yellow(start) and green(end) makes one more green...
public static string LerpHue(Color min, Color max)
{
float t = 0.7F // **percent step, need to calculate somehow...**
float minHue = min.GetHue() / 255;
float maxHue = max.GetHue() / 255;
float scale = maxHue - minHue;
float curHue = minHue + (scale * t);
double r, g, b;
HSVToRGB(curHue, 1.0, 1.0, out r, out g, out b);
Color color = Color.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
return $"#{color.R.ToString("X").PadLeft(2, '0')}{color.G.ToString("X").PadLeft(2, '0')}{color.B.ToString("X").PadLeft(2, '0')}";
}
public static void HSVToRGB(double H, double S, double V, out double R, out double G, out double B)
{
if (H == 1.0)
H = 0.0;
double step = 1.0 / 6.0;
double vh = H / step;
int i = (int)System.Math.Floor(vh);
double f = vh - i;
double p = V * (1.0 - S);
double q = V * (1.0 - (S * f));
double t = V * (1.0 - (S * (1.0 - f)));
switch (i)
{
case 1: { R = q; G = V; B = p; break; }
case 2: { R = p; G = V; B = t; break; }
case 3: { R = p; G = q; B = V; break; }
case 4: { R = t; G = p; B = V; break; }
case 5: { R = V; G = p; B = q; break; }
default:{ R = V; G = t; B = p; break; }
}
}
Help please)
protected void GetCalculatedColor()
{
var colorRangesCount = MeasureAppearance.GaugeRanges.Count;
int step = 1;
var startRange = MeasureAppearance.GaugeRanges.FirstOrDefault();
var endRange = MeasureAppearance.GaugeRanges.LastOrDefault();
Color startColor = Color.FromArgb(int.Parse(startRange?.Color.Replace("#", "") ?? "0", System.Globalization.NumberStyles.AllowHexSpecifier));
Color endColor = Color.FromArgb(int.Parse(endRange?.Color.Replace("#", "") ?? "0", System.Globalization.NumberStyles.AllowHexSpecifier));
foreach (var gaugeRange in MeasureAppearance.GaugeRanges)
{
if (gaugeRange == startRange || gaugeRange == endRange)
continue;
var calculatedColor = ColorService.LerpHueReal(startColor, endColor, (1f / (colorRangesCount - 1)) * step);
gaugeRange.Color = calculatedColor;
step++;
}
}
public static string LerpHueReal(Color start, Color end, float t)
{
var startHue = start.GetHue();
var endHue = end.GetHue();
var interpolatedHue = Math.Min(Math.Max((startHue + (endHue - startHue) * t), 0f), 360f);
var rgb = ColorService.ColorFromHSV(interpolatedHue, 1f, 1f);
return $"#{rgb.R:X2}{rgb.G:X2}{rgb.B:X2}";
}
private static Color ColorFromHSV(float hue, float saturation, float value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromArgb(255, v, t, p);
else if (hi == 1)
return Color.FromArgb(255, q, v, p);
else if (hi == 2)
return Color.FromArgb(255, p, v, t);
else if (hi == 3)
return Color.FromArgb(255, p, q, v);
else if (hi == 4)
return Color.FromArgb(255, t, p, v);
else
return Color.FromArgb(255, v, p, q);
}
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)
{
// ...
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Is there any way or workaround to combine/append two System.Drawing.Brush classes together?
e.g. Brush b1 = GetFromSomewhere();
Brush b2 = GetFromSomewhereElse();
(something like that...)
Brush b3 = b1 + b2;
Eventually my purpose is to do something like:
Graphics graphics = new Graphics;
graphics.FillRectangle(b3, rectangle);
Update:
I have a third party library (have no control over it) which gives me a predefined Brush instance (represents a fill pattern, e.g. ++++ or #####). I want to "overlay" that instance with my "own" brush pattern.
Update:
Since you have finally clarified what you want here is a solution to mix two TextureBrushes:
I assume you have the pattern for your TextureBrush in the Image img2. Use the simple method below to mix two images of the same size and create the new brush by using the combined patterns:
TextureBrush brush3 = new TextureBrush(
mixBitmaps( (Bitmap)(brush1.Image), (Bitmap) img2) );
Now you can paint or fill stuff with it..
Bitmap mixBitmaps(Bitmap bmp1, Bitmap bmp2)
{
using (Graphics G = Graphics.FromImage(bmp1) )
{
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
G.DrawImage(bmp2, Point.Empty);
}
return bmp1;
}
Here is an example:
I leave the old answer, since some folks were interested in it, too:
If you want to mix the Colors you can do it easily, maybe like this:
SolidBrush MixColor(SolidBrush b1, SolidBrush b2)
{
return new SolidBrush(Color.FromArgb(Math.Max(b1.Color.A, b2.Color.A),
(b1.Color.R + b2.Color.R) / 2, (b1.Color.G + b2.Color.G) / 2,
(b1.Color.B + b2.Color.B) / 2));
}
You may want to set the alpha channel to a fixed value of 255 instead.
However this is a simplistic average caculation, which will not work well if the colors are not close.
For a better mix you would mix the hues separatly from the saturation and the brightness values, that is you would have to convert to HSL or HSV, mix there and convert back to RGB..
Here is a version that should do just that:
SolidBrush MixBrushes(SolidBrush br1, SolidBrush br2)
{
return new SolidBrush ( MixColorHSV( br1.Color, br2.Color) );
}
Color MixColorHSV(Color c1 , Color c2 )
{
double h1 = c1.GetHue();
double h2 = c2.GetHue();
double d = (h2 - h1) / 2d;
double h = h1 + d;
if (d > 90) h -= 180; else if (d < -90) h += 180; // correction 1!
if (h < 0) h += 360; else if (h > 360) h -= 360; // correction 2!
int max1 = Math.Max(c1.R, Math.Max(c1.G, c1.B));
int min1 = Math.Min(c1.R, Math.Min(c1.G, c1.B));
double s1 = (max1 == 0) ? 0 : 1d - (1d * min1 / max1);
double v1 = max1 / 255d;
int max2 = Math.Max(c2.R, Math.Max(c2.G, c2.B));
int min2 = Math.Min(c2.R, Math.Min(c2.G, c2.B));
double s2 = (max2 == 0) ? 0 : 1d - (1d * min2 / max2);
double v2 = max2 / 255d;
double s = (s1 + s2) / 2d;
double v = (v1 + v2) / 2d;
return ColorFromHSV(h,s,v);
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0) return Color.FromArgb(255, v, t, p);
else if (hi == 1) return Color.FromArgb(255, q, v, p);
else if (hi == 2) return Color.FromArgb(255, p, v, t);
else if (hi == 3) return Color.FromArgb(255, p, q, v);
else if (hi == 4) return Color.FromArgb(255, t, p, v);
else return Color.FromArgb(255, v, p, q);
}
See this post for parts of the conversion!
Here is a color chart that compares the two mixes adding Red with colors each 30° farther away. Note how many of the simple mixes are murkier with less saturation:
I would like some advice on how to accomplish the following within C#. I would like to plot a "color", the exact color will be determined by a property value, for the sake of this example let's assume it's a percentage.
Ideally I'd like the user to specify five colors.
Negative MAX
Negative MIN
Even
Positive MIN
Positive MAX
The user is only specifying colors for each level, not the value which determines Min and Max.
Using this data, I would like to be able to calculate a color based on a percentage. i.e. 57% would result in a color hue in between Positive MIN and Positive MAX. Any help or advice for this is appreciated.
To interpolate between two colors you just interpolate each color channel
Color color1 = Color.Red;
Color color2 = Color.Green;
double fraction = 0.3;
Color color3 = Color.FromArgb(
(int)(color1.R * fraction + color2.R * (1 - fraction)),
(int)(color1.G * fraction + color2.G * (1 - fraction)),
(int)(color1.B * fraction + color2.B * (1 - fraction)));
To interpolate along your scale you need to decide what exact percentage values your "steps" have and project your target value that a fraction between two of those color steps.
Using the function copied from here, you can convert the hues back into RGB colors. This function assumes has all inputs and outputs in on a 0..1 scale, so you'll need to divide by color.GetHue() by 255. Below the function is some sample code using a set of percentages; implement DisplayColor in the UI of your choice to see the results.
public static void HSVToRGB(double H, double S, double V, out double R, out double G, out double B)
{
if (H == 1.0)
{
H = 0.0;
}
double step = 1.0/6.0;
double vh = H/step;
int i = (int) System.Math.Floor(vh);
double f = vh - i;
double p = V*(1.0 - S);
double q = V*(1.0 - (S*f));
double t = V*(1.0 - (S*(1.0 - f)));
switch (i)
{
case 0:
{
R = V;
G = t;
B = p;
break;
}
case 1:
{
R = q;
G = V;
B = p;
break;
}
case 2:
{
R = p;
G = V;
B = t;
break;
}
case 3:
{
R = p;
G = q;
B = V;
break;
}
case 4:
{
R = t;
G = p;
B = V;
break;
}
case 5:
{
R = V;
G = p;
B = q;
break;
}
default:
{
// not possible - if we get here it is an internal error
throw new ArgumentException();
}
}
}
Color min = Color.Blue;
Color max = Color.Green;
float minHue = min.GetHue() / 255;
float maxHue = max.GetHue() / 255;
float scale = maxHue - minHue;
float[] percents = new float[] { .05F, .15F, .40F, .70F, .85F, .95F };
foreach (var pct in percents) {
float curHue = minHue + (scale * pct);
double r, g, b;
HSVToRGB(curHue, 1.0, 1.0, out r, out g, out b);
Color curColor = Color.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
DisplayColor(curColor);
}
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.