Adjust color based on contrast - c#

For a mod I'm working on, I'd like to incorporate the player's theme colors and use them to generate UI elements. However, I'm running into an issue where not all color themes have colors that provide a good contrast ratio as outlined in 1.4.3 Contrast (Minimum) of Web Content Accessibility Guidelines (WCAG) 2.1.
I can currently check the contrast with the following:
float RelativeLuminance(Color color)
{
float ColorPartValue(float part)
{
return part <= 0.03928f ? part / 12.92f : Mathf.Pow((part + 0.055f) / 1.055f, 2.4f);
}
var r = ColorPartValue(color.r);
var g = ColorPartValue(color.g);
var b = ColorPartValue(color.b);
var l = 0.2126f * r + 0.7152f * g + 0.0722f * b;
return l;
}
private float ColorContrast(Color a, Color b)
{
float result = 0f;
var La = RelativeLuminance(a) + 0.05f;
var Lb = RelativeLuminance(b) + 0.05f;
result = Mathf.Max(La, Lb) / Mathf.Min(La, Lb);
return result;
}
I use the found color contrast to determine whether or not the initial text color is good enough.
public Color GetContrastingColors(Color backgroundColor, Color textColor)
{
Color contrastColor;
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < 4.5f))
{
return textColor;
}
Color.RGBToHSV(textColor, out var textH, out var textS, out var textV);
Color.RGBToHSV(backgroundColor, out var bgH, out var bgS, out var bgV);
// Modify textV by some value to provide enough contrast.
contrastColor = Color.HSVToRGB(textH, textS, textV);
return contrastColor;
}
However, I'm unsure how to adjust the colors so that the text color just brightens (or dims) enough to get to that 4.5:1 contrast ratio. Originally, I was thinking of working the algebra for the luminosity and contrast equations to the point where the sRGB values are multiplied by some value X. I remembered HSV though, and adjusting the brightness of the color seems a lot simpler to me. The issue is, I'm unsure how to compare the contrasts of 2 HSV colors, let alone use their values to manipulate a color's brightness to reach a desired contrast.
My current thought process is to do something dumb like this:
float targetL;
bool brighter = false;
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
var ratio = 4.5f;
// Try to go in the direction of brightness originally.
if (textL > backL)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
if (targetL > 1f)
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
brighter = false;
}
}
else
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
if (targetL > 0f)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
}
}
Color adjustedColor = textColor;
while ((!brighter && textL > targetL) || (brighter && textL < targetL))
{
Color.RGBToHSV(adjustedColor, out var textH, out var textS, out var textV);
textV += brighter ? 0.01f : -0.01f;
adjustedColor = Color.HSVToRGB(textH, textS, textV);
textL = RelativeLuminance(adjustedColor);
}
contrastColor = adjustedColor;
But that's not really efficient, so how can I manipulate the text color so that it "remains the same" but provides enough contrast?
Edit:
To give more context to what I'm trying to do, imagine I have the following set of 4 colors as the player's theme.
In terms of HTML codes, that's:
#32263d
#3d1c70
#7347b6
#320d68
I want to incorporate 2 of those colors from their theme when creating a UI for them. However, not all of them are easily distinguishable, you can see the various contrasts in this case here:
Now each theme contains a darker and lighter color just like the center 2 rows in this example, but also like this example, their contrast may not always be accessible for the end user to read. Moving along with the example, in this case, we're going to be using #32263d and #7347b6 to build our UI.
While I could try to randomly create a shade of purple similar, I want to keep it as close to the original as possible and just brighten it. We can see how it'd look in the various levels of light, here:
If we set #7347b6 to the maximum brightness at #a163ff, we get the following pair now:
While better than before, this is only a contrast of 3.88 : 1 still. So now I want to scale down the brightness of #32263d. If we reduce it to #251B2D, we then end up with this:
The two new colors then have a color contrast of 4.51 : 1.
Now, I could go through each theme manually, but given the number of them, I'd prefer to write an algorithm that generates the updated colors on the fly.

Check out my answer for Adapt given color pairs to adhere to W3C Accessibility standard for ePubs
You can skip the part where I talk about the contrast ratio formula since you have that already but I talk about how to adjust the colors to get better contrast.
If I were to actually code my recommendation from the previous answer, I would be more efficient and rather than adding or subtracting 1 from each RGB component and recomputing the luminance, I would probably add/subtract 10 and recompute. If not sufficient contrast, do 10 again. Once I get enough contrast, I could then re-adjust the values, perhaps by 2 each time, in the opposite direction until I got as close to 4.5 without going under.

I ended up using a loop after all for my code. While slugolicious's answer was close to what I wanted, I found that adjust all the RGB components by the same amount was not what I wanted as is actually affected the hue, so I ended up using HSV instead.
public Color[] GetContrastingColors(Color backgroundColor, Color textColor, float ratio)
{
Color[] colors = new Color[2];
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
if (textL > backL)
{
colors[0] = textColor;
colors[1] = backgroundColor;
}
else
{
colors[1] = textColor;
colors[0] = backgroundColor;
}
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < ratio))
{
return colors;
}
Color.RGBToHSV(colors[0], out var lightH, out var lightS, out var lightV);
Color.RGBToHSV(colors[1], out var darkH, out var darkS, out var darkV);
// If the darkest color can be darkened enough to have enough contrast after brightening the color.
if (ColorContrast(Color.HSVToRGB(darkH, darkS, 0f), Color.HSVToRGB(lightH, lightS, 1f)) >= ratio)
{
var lightDiff = 1f - lightV;
var darkDiff = darkV;
var steps = new float[] { 0.12f, 0.1f, 0.08f, 0.05f, 0.04f, 0.03f, 0.02f, 0.01f, 0.005f };
var step = 0;
var lightRatio = (lightDiff / (lightDiff + darkDiff));
var darkRatio = (darkDiff / (lightDiff + darkDiff));
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV), Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV + lightRatio * steps[step]), Color.HSVToRGB(darkH, darkS, darkV - darkRatio * steps[step])) > ratio && step < steps.Length - 1)
{
step++;
}
lightV += lightRatio * steps[step];
darkV -= darkRatio * steps[step];
}
colors[0] = Color.HSVToRGB(lightH, lightS, lightV);
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
// Fall back to using white.
else
{
colors[0] = Color.white;
while (ColorContrast(Color.white, Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
darkV -= 0.01f;
}
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
return colors;
}

Related

Fade out images in order

Is basically a mathematical question, would like to know what would be a good solution.
Problem: I have 25 images placed in one line. I want the images to fade out in the order. That is the first image should be completely opaque and last image should be completely transparent.
I have placed all these images in an order inside one parent.
My solution: I am just providing a fixed number that iterates itself for the alpha.
What I am looking for: a formula so that this "fixed" number can be dynamically changed by number of images present.
void Start () {
int color = 10; //my fixed number
foreach (Transform child in transform) {
child.gameObject.GetComponent<Image>().color = new Color32(255, 255, 255, (byte) (255 - color));
color += 10; //iterating for the next child
}
}
What about simply calculating the step:
void Start ()
{
if(transform.childCount <= 1)
{
Debug.LogWarning("Requires at least 2 children!");
return;
}
var alphaStep = 1f / (transform.childCount - 1);
var alpha = 1f;
foreach (Transform child in transform)
{
child.GetComponent<Image>().color = new Color(1f, 1f, 1f, alpha);
alpha -= alphaStep;
}
}
Or if you want full control over the maximum and minimum alpha you could use e.g.
public float minAlpha = 0f;
public float maxAlpha = 1f;
and then
var alphaStep = 1f / (transform.childCount - 1);
for (var i = 0; i < transform.childCount; i++)
{
var factor = i / (transform.childCount - 1);
transform.GetChild(i).GetComponent<Image>().color = new Color(1f, 1f, 1f, Mathf.Lerp(maxAlpha, minAlpha, factor));
}
I would recomend use an Array to iterate through your elements more freely.
With that you could go something like... (coding in SO, not tested)
Image[] images; //this should reference the array constructed elsewhere where you load the images.
private void Start() {
for (int i = 0; i < images.Length; i++) {
int alpha = 255 - (Mathf.CeilToInt(255 / images.Length) * i + 1);
images[i].color = Color32(255,255,255,alpha);}
}
That will probably do what you want.
By the way, not shure why you using Color32 but working with "float" RGBA will rid you from that ceil and give you more precission.

Cut faraway objects based on depth map

I would like to do grabcut which uses a depth map that cuts away far objects, that is used in mixed reality application. So I would like to show just the front of what I see and the background as virtual reality scene.
The problem right now I tried to adapt so code and what I get is front which is cut but in black color, the mask actually.
I don't know where is the problem settle.
The input is a depth map from zed camera.
here is a picture of the behaviour:
My trial:
private void convertToGrayScaleValues(Mat mask)
{
int width = mask.rows();
int height = mask.cols();
byte[] buffer = new byte[width * height];
mask.get(0, 0, buffer);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int value = buffer[y * width + x];
if (value == Imgproc.GC_BGD)
{
buffer[y * width + x] = 0; // for sure background
}
else if (value == Imgproc.GC_PR_BGD)
{
buffer[y * width + x] = 85; // probably background
}
else if (value == Imgproc.GC_PR_FGD)
{
buffer[y * width + x] = (byte)170; // probably foreground
}
else
{
buffer[y * width + x] = (byte)255; // for sure foreground
}
}
}
mask.put(0, 0, buffer);
}
For Each depth frame from Camera:
Mat erodeElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(4, 4));
Mat dilateElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(7, 7));
depth.copyTo(maskFar);
Core.normalize(maskFar, maskFar, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);
Imgproc.cvtColor(maskFar, maskFar, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(maskFar, maskFar, 180, 255, Imgproc.THRESH_BINARY);
Imgproc.dilate(maskFar, maskFar, erodeElement);
Imgproc.erode(maskFar, maskFar, dilateElement);
Mat bgModel = new Mat();
Mat fgModel = new Mat();
Imgproc.grabCut(image, maskFar, new OpenCVForUnity.CoreModule.Rect(), bgModel, fgModel, 1, Imgproc.GC_INIT_WITH_MASK);
convertToGrayScaleValues(maskFar); // back to grayscale values
Imgproc.threshold(maskFar, maskFar, 180, 255, Imgproc.THRESH_TOZERO);
Mat foreground = new Mat(image.size(), CvType.CV_8UC4, new Scalar(0, 0, 0));
image.copyTo(foreground, maskFar);
Utils.fastMatToTexture2D(foreground, texture);
In this case, the graph cut on the depth image might not be the correct method to solve all of your issue.
If you insist the processing should be done in the depth image. To find everything that is not on the table and filter out the table part. You may first apply the disparity based approach for finding the object that's is not on the ground. Reference: https://github.com/windowsub0406/StereoVision
Then based on the V disparity output image, find the locally connected component that is grouped together. You may follow this link how to do this disparity map in OpenCV which is asking the similar way to find the objects that's not on the ground
If you are ok with RGB based approaches, then use any deep learning-based method to recognize the monitor should be the correct approaches. It can directly detect the mointer bounding box. By apply this bounding box to the depth image, you may have what you want. For deep learning based approaches, there are many available package such as Yolo series. You may find one that is suitable for you. reference: https://medium.com/#dvshah13/project-image-recognition-1d316d04cb4c

Low pass filter for accelerometer

I'm creating an app for windows phone using c# that uses the accelerator but its not smooth when displayed on the screen. I only need to move the Y-axis. I have seen this formula on microsofts website but I'm not sure how i should use it
O = O-1 + α(I – O-1)
where O is the output, α is the coefficient and I is the input(raw value)
how do i implement this into my code which is
private void UpdateUI(AccelerometerReading accelerometerReading)
{
statusTextBlock.Text = "getting data";
Vector3 acceleration = accelerometerReading.Acceleration;
// Show the numeric values on screen.
yTextBlock.Text = "Y: " + acceleration.Y.ToString("0.00");
//low pass filter
//????
//move ball on screen
var TopMar = (278.5*acceleration.Y)+278.5;
var BotMar = 557 - TopMar;
yDot.Margin = new Thickness(203, BotMar, 203, TopMar);
}
If Vector3 has overloaded the operators, this should do:
private Vector3 MeanAcceleration = null;
private void UpdateUI(AccelerometerReading accelerometerReading)
{
const double alpha = 0.05;
statusTextBlock.Text = "getting data";
Vector3 acceleration = accelerometerReading.Acceleration;
// Show the numeric values on screen.
yTextBlock.Text = "Y: " + acceleration.Y.ToString("0.00");
//low pass filter
if (MeanAcceleration == null)
MeanAcceleration = acceleration;
else
MeanAcceleration = (1 - alpha) * MeanAcceleration + alpha * acceleration;
//move ball on screen
var TopMar = (278.5 * MeanAcceleration.Y) + 278.5;
var BotMar = 557 - TopMar;
yDot.Margin = new Thickness(203, BotMar, 203, TopMar);
}
You need a field (or something of similar scope) and assign it the mean value. Every timestep, you update this mean value.
Alpha must be between 0 and, 1, to effectively low-pass the signal it should be 0.1 or below. Decrease this if the output is too wiggly and increase alpha if the output is too slow. If both is the case, you probably need a more sophisticated digital filter.
If the beginning is not important, you can initialize the mean with something like
private Vector3 MeanAcceleration = new Vector3(0, 0, 0);
but I'm not sure about the constructor, because I don't know exactly which Vector3 that is.

How do I detect irregular borders in an image?

Given the image below, what algorithm might I use to detect whether regions one and two (identified by color) have a border?
http://img823.imageshack.us/img823/4477/borders.png
If there's a C# example out there, that would be awesome, but I'm really just looking for any example code.
Edit: Using Jaro's advice, I came up with the following...
public class Shape
{
private const int MAX_BORDER_DISTANCE = 15;
public List<Point> Pixels { get; set; }
public Shape()
{
Pixels = new List<Point>();
}
public bool SharesBorder(Shape other)
{
var shape1 = this;
var shape2 = other;
foreach (var pixel1 in shape1.Pixels)
{
foreach (var pixel2 in shape2.Pixels)
{
var xDistance = Math.Abs(pixel1.X - pixel2.X);
var yDistance = Math.Abs(pixel1.Y - pixel2.Y);
if (xDistance > 1 && yDistance > 1)
{
if (xDistance * yDistance < MAX_BORDER_DISTANCE)
return true;
}
else
{
if (xDistance < Math.Sqrt(MAX_BORDER_DISTANCE) &&
yDistance < Math.Sqrt(MAX_BORDER_DISTANCE))
return true;
}
}
}
return false;
}
// ...
}
Clicking on two shapes that do share a border returns fairly quickly, but very distance shapes or shapes with a large number of pixels take 3+ seconds at times. What options do I have for optimizing this?
2 regions having border means that within a certain small area there should be 3 colors present: red, black and green.
So a very ineffective solution presents itself:
using Color pixelColor = myBitmap.GetPixel(x, y); you could scan an area for those 3 colors. The area must be larger than the width of the border of course.
There is of course plenty room for optimizations (like going in 50 pixels steps and decreasing the precision continually).
Since black is the least used color, you would search around black areas first.
This should explain what I have written in various comments in this topic:
namespace Phobos.Graphics
{
public class BorderDetector
{
private Color region1Color = Color.FromArgb(222, 22, 46);
private Color region2Color = Color.FromArgb(11, 189, 63);
private Color borderColor = Color.FromArgb(11, 189, 63);
private List<Point> region1Points = new List<Point>();
private List<Point> region2Points = new List<Point>();
private List<Point> borderPoints = new List<Point>();
private Bitmap b;
private const int precision = 10;
private const int distanceTreshold = 25;
public long Miliseconds1 { get; set; }
public long Miliseconds2 { get; set; }
public BorderDetector(Bitmap b)
{
if (b == null) throw new ArgumentNullException("b");
this.b = b;
}
private void ScanBitmap()
{
Color c;
for (int x = precision; x < this.b.Width; x += BorderDetector.precision)
{
for (int y = precision; y < this.b.Height; y += BorderDetector.precision)
{
c = this.b.GetPixel(x, y);
if (c == region1Color) region1Points.Add(new Point(x, y));
else if (c == region2Color) region2Points.Add(new Point(x, y));
else if (c == borderColor) borderPoints.Add(new Point(x, y));
}
}
}
/// <summary>Returns a distance of two points (inaccurate but very fast).</summary>
private int GetDistance(Point p1, Point p2)
{
return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y);
}
/// <summary>Finds the closests 2 points among the points in the 2 sets.</summary>
private int FindClosestPoints(List<Point> r1Points, List<Point> r2Points, out Point foundR1, out Point foundR2)
{
int minDistance = Int32.MaxValue;
int distance = 0;
foundR1 = Point.Empty;
foundR2 = Point.Empty;
foreach (Point r1 in r1Points)
foreach (Point r2 in r2Points)
{
distance = this.GetDistance(r1, r2);
if (distance < minDistance)
{
foundR1 = r1;
foundR2 = r2;
minDistance = distance;
}
}
return minDistance;
}
public bool FindBorder()
{
Point r1;
Point r2;
Stopwatch watch = new Stopwatch();
watch.Start();
this.ScanBitmap();
watch.Stop();
this.Miliseconds1 = watch.ElapsedMilliseconds;
watch.Start();
int distance = this.FindClosestPoints(this.region1Points, this.region2Points, out r1, out r2);
watch.Stop();
this.Miliseconds2 = watch.ElapsedMilliseconds;
this.b.SetPixel(r1.X, r1.Y, Color.Green);
this.b.SetPixel(r2.X, r2.Y, Color.Red);
return (distance <= BorderDetector.distanceTreshold);
}
}
}
It is very simple. Searching this way only takes about 2 + 4 ms (scanning and finding the closest points).
You could also do the search recursively: first with precision = 1000, then precision = 100 and finally precision = 10 for large images.
FindClosestPoints will practically give you an estimated rectangual area where the border should be situated (usually borders are like that).
Then you could use the vector approach I have described in other comments.
I read your question as asking whether the two points exist in different regions. Is this correct? If so, I would probably use a variation of Flood Fill. It's not super difficult to implement (don't implement it recursively, you will almost certainly run out of stack space) and it will be able to look at complex situations like a U-shaped region that has a border between two points, but are not actually different regions. Basically run flood fill, and return true when your coordinate matches the target coordinate (or perhaps when it's close enough for your satisfaction, depending on your use case)
[Edit] Here is an example of flood fill that I wrote for a project of mine. The project is CPAL-licensed, but the implementation is pretty specific to what I use it for anyway, so don't worry about copying parts of it. And it doesn't use recursion, so it should be able to scale to pixel data.
[Edit2] I misunderstood the task. I don't have any example code that does exactly what you're looking for, but I can say that comparing pixel-per-pixel the entire two regions is not something you want to do. You can reduce the complexity by partitioning each region into a larger grid (maybe 25x25 pixels), and comparing those sectors first, and if any of those are close enough, do a pixel-per-pixel comparison just within those two sectors.
[Edit2.5] [Quadtree]3 might be able to help you too. I don't have a lot of experience with it, but I know it's popular in 2D collision detection, which is similar to what you're doing here. Might be worth researching.

C#: Create a lighter/darker color based on a system color [duplicate]

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

Categories