Programmatically Lighten or Darken a hex color in dart - dart

I am trying to convert this hash color code #159424 (GREEN-COLOR) to more darken and lighten programmatically. How to do this please help?
make green color darker
toDarkColor(String hashColor){
// how to convert that hash string to make green color darker?
}
make green color lighter
toLightColor(String hashColor){
// how to convert that hash string to make green color lighter?
}

For people who want to darken or lighten Color instead of hex string
// ranges from 0.0 to 1.0
Color darken(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
Color lighten(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslLight.toColor();
}
// usage
final lightRed = lighten(Colors.red);
final darkBlue = darken(Colors.blue, .3);
Live Demo

Color accurate solution with no plugin
The accepted answer changes the tint of colors when darkening (the tint is more saturated). Also its lightening function produces pure white with an amount of 0.3 for some colors although white should only be reached with an amount of 1.
The two following methods produce shades of the base color that seem 'darker' or 'lighter' without changing the tint.
import 'package:flutter/material.dart';
/// Darken a color by [percent] amount (100 = black)
// ........................................................
Color darken(Color c, [int percent = 10]) {
assert(1 <= percent && percent <= 100);
var f = 1 - percent / 100;
return Color.fromARGB(
c.alpha,
(c.red * f).round(),
(c.green * f).round(),
(c.blue * f).round()
);
}
/// Lighten a color by [percent] amount (100 = white)
// ........................................................
Color lighten(Color c, [int percent = 10]) {
assert(1 <= percent && percent <= 100);
var p = percent / 100;
return Color.fromARGB(
c.alpha,
c.red + ((255 - c.red) * p).round(),
c.green + ((255 - c.green) * p).round(),
c.blue + ((255 - c.blue) * p).round()
);
}
Example: darken a color by 15%.
final Color darkerGreen = darken(Color(0xFF159424), 15);
If starting from a Hex String value as OP asked, use J.M. Taylor' solution:
Color hexToColor(String code) {
return Color(int.parse(code.substring(0, 6), radix: 16) + 0xFF000000);
}
final Color darkerGreen = darken(hexToColor('#159424'));
Note: it's for Flutter projects as it uses the material's Color class.

My solution based on https://stackoverflow.com/a/58604669/7479173
extension ColorBrightness on Color {
Color darken([double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(this);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
Color lighten([double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(this);
final hslLight =
hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslLight.toColor();
}
}
with this one can simply:
Colors.red.darken()
Colors.red.lighten()
Colors.red.lighten(0.1)
this works on any colors as long as you import the extension.

You can use tinycolor package:
TinyColor.fromString("#159424").darken(10).color
Edit:
You can convert Color back to hex string like this:
String toHex(Color color) {
return "#${color.red.toRadixString(16).padLeft(2, "0")}"
"${color.green.toRadixString(16).padLeft(2, "0")}"
"${color.blue.toRadixString(16).padLeft(2, "0")}";
}
or if you want opacity/alpha:
String toHex(Color color) {
return "#${color.alpha.toRadixString(16).padLeft(2, "0")}"
"${color.red.toRadixString(16).padLeft(2, "0")}"
"${color.green.toRadixString(16).padLeft(2, "0")}"
"${color.blue.toRadixString(16).padLeft(2, "0")}";
}

I used withLightness method of HSLColor to lighten the color.
HSLColor.fromColor(Colors.red).withLightness(0.95).toColor()

Since some parts of TinyColor seem broken, and I only really needed lighten and darken, NearHuscarl's answer was perfect for me.
However, it was missing one part that was necessary to completely answer the original question, which was converting hash color code (declared as a String) to Color.
To do that, you can use this:
Color hexToColor(String code) {
return Color(int.parse(code.substring(0, 6), radix: 16) + 0xFF000000);
}
The above is not my code, but something I learned from a tutorial here.
Then just combine that with NearHuscarl's code to get the desired effect:
final Color darkerGreen = darken(hexToColor('#159424'));

One liner with built-in method:
Color.lerp(myColor, Colors.white, 0.4) // 0 = keep as is, 1 = white

Related

UIColorPickerViewController is changing the input color slightly

I need to allow the user to choose a color on iOS.
I use the following code to fire up the color picker:
var picker = new UIColorPickerViewController();
picker.SupportsAlpha = true;
picker.Delegate = this;
picker.SelectedColor = color.ToUIColor();
PresentViewController(picker, true, null);
When the color picker displays, the color is always slightly off. For example:
input RGBA: (220, 235, 92, 255)
the initial color in the color picker might be:
selected color: (225, 234, 131, 255)
(these are real values from tests). Not a long way off... but enough to notice if you are looking for it.
I was wondering if the color picker grid was forcing the color to the
nearest color entry - but if that were true, you would expect certain colors to
stay fixed (i.e. if the input color exactly matches one of the grid colors,
it should stay unchanged). That does not happen.
p.s. I store colors in a cross platform fashion using simple RGBA values.
The ToUIColor converts to local UIColor using
new UIColor((nfloat)rgb.r, (nfloat)rgb.g, (nfloat)rgb.b, (nfloat)rgb.a);
From the hints in comments by #DonMag, I've got some way towards an answer, and also a set of resources that can help if you are struggling with this.
The key challenge is that mac and iOS use displayP3 as the ColorSpace, but most people use default {UI,NS,CG}Color objects, which use the sRGB ColorSpace (actually... technically they are Extended sRGB so they can cover the wider gamut of DisplayP3). If you want to know the difference between these three - there's resources below.
When you use the UIColorPickerViewController, it allows the user to choose colors in DisplayP3 color space (I show an image of the picker below, and you can see the "Display P3 Hex Colour" at the bottom).
If you give it a color in sRGB, I think it gets converted to DisplayP3. When you read the color, you need to convert back to sRGB, which is the step I missed.
However I found that using CGColor.CreateByMatchingToColorSpace, to convert from DisplayP3 to sRGB never quite worked. In the code below I convert to and from DisplayP3 and should have got back my original color, but I never did. I tried removing Gamma by converting to a Linear space on the way but that didn't help.
cg = new CGColor(...values...); // defaults to sRGB
// sRGB to DisplayP3
tmp = CGColor.CreateByMatchingToColorSpace(
CGColorSpace.CreateWithName("kCGColorSpaceDisplayP3"),
CGColorRenderingIntent.Default, cg, null);
// DisplayP3 to sRGB
cg2 = CGColor.CreateByMatchingToColorSpace(
CGColorSpace.CreateWithName("kCGColorSpaceExtendedSRGB"),
CGColorRenderingIntent.Default, tmp, null);
Then I found an excellent resource: http://endavid.com/index.php?entry=79 that included a set of matrices that can perform the conversions. And that seems to work.
So now I have extended CGColor as follows:
public static CGColor FromExtendedsRGBToDisplayP3(this CGColor c)
{
if (c.ColorSpace.Name != "kCGColorSpaceExtendedSRGB")
throw new Exception("Bad color space");
var mat = LinearAlgebra.Matrix<float>.Build.Dense(3, 3, new float[] { 0.8225f, 0.1774f, 0f, 0.0332f, 0.9669f, 0, 0.0171f, 0.0724f, 0.9108f });
var vect = LinearAlgebra.Vector<float>.Build.Dense(new float[] { (float)c.Components[0], (float)c.Components[1], (float)c.Components[2] });
vect = vect * mat;
var cg = new CGColor(CGColorSpace.CreateWithName("kCGColorSpaceDisplayP3"), new nfloat[] { vect[0], vect[1], vect[2], c.Components[3] });
return cg;
}
public static CGColor FromP3ToExtendedsRGB(this CGColor c)
{
if (c.ColorSpace.Name != "kCGColorSpaceDisplayP3")
throw new Exception("Bad color space");
var mat = LinearAlgebra.Matrix<float>.Build.Dense(3, 3, new float[] { 1.2249f, -0.2247f, 0f, -0.0420f, 1.0419f, 0f, -0.0197f, -0.0786f, 1.0979f });
var vect = LinearAlgebra.Vector<float>.Build.Dense(new float[] { (float)c.Components[0], (float)c.Components[1], (float)c.Components[2] });
vect = vect * mat;
var cg = new CGColor(CGColorSpace.CreateWithName("kCGColorSpaceExtendedSRGB"), new nfloat[] { vect[0], vect[1], vect[2], c.Components[3] });
return cg;
}
Note: there's lots of assumptions in the matrices w.r.t white point and gammas. But it works for me. Let me know if there are better approaches out there, or if you can tell me why my use of CGColor.CreateByMatchingToColorSpace didn't quite work.
Reading Resources:
Reading this: https://stackoverflow.com/a/49040628/6257435
then this: https://bjango.com/articles/colourmanagementgamut/
are essential starting points.
Image of the iOS Color Picker:

Swift dynamic color change from a range of Colors

I'm working on a donut-chart that needs to be able to support a huge amount of sections.
For this, each section of course needs it's own color. For this I need a way to dynamically make a new color for each section. This is easy. But it also needs to take a startColor and an EndColor, so it can use e.g. only blue colors.
I did manage to this using the following code:
var rStartValue: CGFloat = 16
var gStartValue: CGFloat = 177
var bStartValue: CGFloat = 216
var rEndValue: CGFloat = 30
var gEndValue: CGFloat = 30
var bEndValue: CGFloat = 38
for percentFill in percentFills {
let progressLine = CAShapeLayer()
if rStartValue < rEndValue {
rStartValue += 1
} else if rStartValue > rEndValue {
rStartValue -= 1
}
if gStartValue < gEndValue {
gStartValue += 1
} else if gStartValue > gEndValue {
gStartValue -= 1
}
if bStartValue < bEndValue {
bStartValue += 1
} else if bStartValue > bEndValue {
bStartValue -= 1
}
}
However. It's not the desired result yet.
I need it to step appropiately. Right now it just steps by 1 value in each loop. Which is okay if there's a ton of sections, but sometimes there's only maybe 17 or so.
So I need it to calculate how much to step based on the amount of sections.
After messing around with various versions of percentFills/various related variables.
I ended up here, hoping someone can help me figure out how to make these colours step correctly.
Here's an example of what I've been trying:
var rJump = CGFloat(percentFills.count)/(rStartValue-rEndValue)
var gJump = CGFloat(percentFills.count)/(gStartValue-gEndValue)
var bJump = CGFloat(percentFills.count)/(bStartValue-bEndValue)
Then replacing 1 in the loop with rJump, gJump or bJump. But Every variable I have tried to divide with the percentFills.count (total number of sections) doesn't provide the desired result.
The startValue should always be the first color, and the endValue should always be the last color. Then step from start to end, as evenly as possible.
Any help getting past this barrier would be greatly appreciated!
Instead of using RGB, you will probably be better off using HSL where the H is hue which is essentially the color.
You can use:
Swift:
init(hue hue: CGFloat, saturation saturation: CGFloat, brightness brightness: CGFloat, alpha alpha: CGFloat)
ObjectiveC:
+ (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha
all values are in the range 0.0 - 1.0.
See Apple Docs.
HSL is close to the way we experience color, RGB is the way most hardware displays color.
Here is an example from Interface Builder, the values are 0 - 360:
I figured it out.
It was a "little" problem with my math. Instead of doing:
var rJump = CGFloat(percentFills.count)/(rStartValue-rEndValue)
var gJump = CGFloat(percentFills.count)/(gStartValue-gEndValue)
var bJump = CGFloat(percentFills.count)/(bStartValue-bEndValue)
I simply needed to change it to this:
var rJump = (rStartValue-rEndValue)/CGFloat(percentFills.count)
var gJump = (gStartValue-gEndValue)/CGFloat(percentFills.count)
var bJump = (bStartValue-bEndValue)/CGFloat(percentFills.count)
That takes the difference in values. E.g. if you are starting red color is 100, and your end red color is 200, and you have 10 sections (or steps), it would look like this:
100-50 = 50 (This is the difference between the two)
50/10 (Then 50 is divided by the amount of sections (steps). = 5
Where 5 is the amount the red color needs to change for each step:
if rStartValue < rEndValue {
rStartValue += 5
} else if rStartValue > rEndValue {
rStartValue -= 5
}
Of course variable rJump should be used instead of the number.
Hoped that helped someone else if anyone runs into a similar issue.
Also Check out Zaph's answer. Using HBS as he mentions, you might only have to change 1 value and not 3, for each loop.

Setting Vector Feature Fill Opacity when you have a hexadecimal color

I'm trying to set the fill opacity of a vector feature in OL3 and can't figure out how to do it if I have a hexadecimal value...I've seen examples with rgba. Any suggestions?
Here's what I have:
style : function(feature, resolution) {
return [new ol.style.Style(
{
stroke : new ol.style.Stroke(
{
color : feature.get('color'),
width : 2
}),
fill : new ol.style.Fill(
{
color : feature.get('color'), //'#FF0000'
opacity : 0.2 // I thought this would work, but it's not an option
})
})]
}
This is late but might help someone.
Using rgba property is also possible.
fill: new ol.style.Fill({color: 'rgba(255, 255, 0, 0.63)'}),
You can use the ol.color.asArray function. That function converts color strings to color arrays.
So this is what you can use:
var hexColor = feature.get('color');
var color = ol.color.asArray(hexColor);
color = color.slice();
color[3] = 0.2; // change the alpha of the color
slice() is used to create a new color array. This is to avoid corrupting the "color strings to color arrays" cache that the ol.color.asArray function maintains.
See http://openlayers.org/en/master/apidoc/ol.color.html?unstable=true#asArray.
import ol_color from 'ol/color';
colorWithAlpha(color, alpha) {
const [r, g, b] = Array.from(ol_color.asArray(color));
return ol_color.asString([r, g, b, alpha]);
}

How can I filter rectangle areas in an OpenCv image by color?

With the following code i detect all squares in a gray scale image:
for (;contours != null; contours = contours.HNext)
{
var approxContour = contours.ApproxPoly(contours.Perimeter * 0.05,
contours.Storage);
var rect = approxContour.GetMinAreaRect();
if (IsSquare(rect, rect.size.Height * 0.1f))
boxes.Add(rect);
}
I'm looking for a way to filter the squares based by their color. E.g. I want to remove all squares with an average gray value less then 128.
Which OpenCv function do I have to use?
You have to use ROIs and GetAverage():
var rect = approxContour.GetMinAreaRect();
gray.ROI = approxContour.BoundingRectangle;
var average = gray.GetAverage();
gray.ROI = Rectangle.Empty;
if (average.Intensity > 100)
{
continue;
}

How do I reduce a bitmap to a known set of RGB colours

For a hobby project I'm going to build a program that when given an image bitmap will create a cross-stitch pattern as a PDF. I'll be using Cocoa/Objective C on a Mac.
The source bitmap will typically be a 24bpp image, but of the millions of colours available, only a few exist as cross-stitch threads. Threads come in various types. DMC is the most widely available, and almost their entire range is available as RGB values from various web sites. Here's one, for instance.
DMC# Name R G B
----- ------------------ --- --- ---
blanc White 255 255 255
208 Lavender - vy dk 148 91 128
209 Lavender - dk 206 148 186
210 Lavender - md 236 207 225
211 Lavender - lt 243 218 228
...etc...
My first problem, as I see it, is from a starting point of the RGB from a pixel in the image choosing the nearest colour available from the DMC set. What's the best way of finding the nearest DMC colour mathematically, and ensuring that it's a close fit as a colour too?
Although I'll be using Cocoa, feel free to use pseudo-code (or even Java!) in any code you post.
Use the LAB color space and find the color with the nearest euclidean distance. Doing this in the RGB color space will yield counter-intuitive results. (Or use the HSL color space.)
So just iterate over each pixel and find the color with the closest distance within the color space you choose. Note that the distance must be computed circularly for some color spaces (e.g. those employing hue).
(Most color quanization revolves around actually choosing a palette, but that has already been taken care of in your case, so you can't use the more popular quantization techniques.)
Also, check out this question.
To find the HSB hue in Cocoa, it looks like you can use the getHue method declared in NSColor.h.
However, if you just convert an image to a cross-stitch design using this technique, it will be very hard to actually stitch it. It will be full of single-pixel color fields, which sort of defeats the purpose of cross-stitching.
This is called color quantization, and there are many algorithms available.
One very basic is to just treat RGB colors as points in space, and use plain old Euclidian distance between colors to figure out how "close" they are. This has drawbacks, since human eyes have different sensitivity at different places in this space, so such a distance would not correspond well to how humans perceive the colors. You can use various weighting schemes to improve that situation.
Interresting... :)
You would not only identify the nearest colors, you would also want to reduce the number of colors used. You don't want to end up with a stitching pattern that uses hundreds of different colors...
I put together some code that does this on a basic level. (Sorry that it's in C#, I hope that it can be somewhat useful anyway.)
There is some further tweaking that needs to be done before the method works well, of course. The GetDistance method weights the importance of hue, saturation and brightness against each other, finding the best balance between those is of course important in order to find the color that looks closest.
There is also a lot that can be done with the method of reducing the palette. In the example I just picked the most used colors, but you probably want to weight in how alike the colors are in the palette. This can be done by picking the most used color, reduce the count for the remaining colors in the list depending on the distance to the picked color, and then resort the list.
The Hsl class that holds a DMC color, can calculate the distance to another color, and find the nearest color in a list of colors:
public class Hsl {
public string DmcNumber { get; private set; }
public Color Color { get; private set; }
public float Hue { get; private set; }
public float Saturation { get; private set; }
public float Brightness { get; private set; }
public int Count { get; set; }
public Hsl(Color c) {
DmcNumber = "unknown";
Color = c;
Hue = c.GetHue();
Saturation = c.GetSaturation();
Brightness = c.GetBrightness();
Count = 0;
}
public Hsl(string dmc, int r, int g, int b)
: this(Color.FromArgb(r, g, b))
{
DmcNumber = dmc;
}
private static float AngleDifference(float a1, float a2) {
float a = Math.Abs(a1 - a2);
if (a > 180f) {
a = 360f - a;
}
return a / 180f;
}
public float GetDistance(Hsl other) {
return
AngleDifference(Hue, other.Hue) * 3.0f +
Math.Abs(Saturation - other.Saturation) +
Math.Abs(Brightness - other.Brightness) * 4.0f;
}
public Hsl GetNearest(IEnumerable<Hsl> dmcColors) {
Hsl nearest = null;
float nearestDistance = float.MaxValue;
foreach (Hsl dmc in dmcColors) {
float distance = GetDistance(dmc);
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = dmc;
}
}
return nearest;
}
}
This code sets up a (heavily reduced) list of DMC colors, loads an image, counts the colors, reduces the palette and converts the image. You would of course also want to save the information from the reduced palette somewhere.
Hsl[] dmcColors = {
new Hsl("blanc", 255, 255, 255),
new Hsl("310", 0, 0, 0),
new Hsl("317", 167, 139, 136),
new Hsl("318", 197, 198, 190),
new Hsl("322", 81, 109, 135),
new Hsl("336", 36, 73, 103),
new Hsl("413", 109, 95, 95),
new Hsl("414", 167, 139, 136),
new Hsl("415", 221, 221, 218),
new Hsl("451", 179, 151, 143),
new Hsl("452", 210, 185, 175),
new Hsl("453", 235, 207, 185),
new Hsl("503", 195, 206, 183),
new Hsl("504", 206, 221, 193),
new Hsl("535", 85, 85, 89)
};
Bitmap image = (Bitmap)Image.FromFile(#"d:\temp\pattern.jpg");
// count colors used
List<Hsl> usage = new List<Hsl>();
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x++) {
Hsl color = new Hsl(image.GetPixel(x, y));
Hsl nearest = color.GetNearest(dmcColors);
int index = usage.FindIndex(h => h.Color.Equals(nearest.Color));
if (index != -1) {
usage[index].Count++;
} else {
nearest.Count = 1;
usage.Add(nearest);
}
}
}
// reduce number of colors by picking the most used
Hsl[] reduced = usage.OrderBy(c => -c.Count).Take(5).ToArray();
// convert image
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x++) {
Hsl color = new Hsl(image.GetPixel(x, y));
Hsl nearest = color.GetNearest(reduced);
image.SetPixel(x, y, nearest.Color);
}
}
image.Save(#"d:\temp\pattern.png", System.Drawing.Imaging.ImageFormat.Png);
get the source for the ppmquant application from the netpbm set of utilities
Others have pointed out various techniques for color quantization. It's possible to use techniques like Markov Random Fields to try to penalize the system for switching thread colors at neighboring pixel locations. There are some generic multi-label MRF libraries out there including Boykov's.
To use one of these, the data elements would be the input colors, the labels would be the set of thread colors, the data terms could be something like the Euclidean distance in LAB space suggested by bzlm, and the neighborhood terms would penalize for switching thread colors.
Depending on the relevance of the correctness of your color operations, remember to take color spaces into account. While I have studied this somewhat, due to my photography hobby, I'm still a bit confused about everything.
But, as previously mentioned, use LAB as much as possible, because (afaik) it's color space agnostic, while all other methods (RGB/HSL/CMYK) mean nothing (in theory) without a defined color space.
RGB, for example, is just three percentage values (0-255 => 0-100%, with 8-bit color depth). So, if you have an RGB-triplet of (0,255,0), it translates to "only green, and as much of it as possible". So, the question is "how red is red?". This is the question that a color space answers - sRGB 100%-green is not as green as AdobeRGB 100%-green. It's not even the same hue!
Sorry if this went to the offtopic side of things

Resources