In Quartz 2D, the CGColorSpaceCreateLab() function takes a range parameter defined as follows:
CGFloat range[4]: An array of 4 numbers that specify the range of valid values for the a* and b* components of the color space. The a* component represents values running from green to red, and the b* component represents values running from blue to yellow.
The question is, what does this parameter actually do?
I can think of at least three possible scenarios.
Option 1: Maybe it scales the component values
Say I have two color spaces with different ranges:
CGFloat range1[] = { -10, 10, -10, 10 };
CGFloat range2[] = { -100, 100, -100, 100 };
CGColorSpaceRef space1 = CGColorSpaceCreateLab(whitePoint, blackPoint, range1);
CGColorSpaceRef space2 = CGColorSpaceCreateLab(whitePoint, blackPoint, range2);
I create colors with the same component values and within the specified range of each space:
CGFloat components[] = { 50, 10, 10, 1 };
CGColorRef color1 = CGColorCreate(space1, components);
CGColorRef color2 = CGColorCreate(space2, components);
Do color1 and color2 represent the same color? Or does the range parameter scale the components in some way, making these two different colors? (If the latter, what value for range corresponds to the standard CIELAB coordinate scaling?
Option 2: Maybe it clips the component values
Given the two spaces defined above, say I create the following colors instead:
CGFloat components[] = { 50, 50, 50, 1 };
CGColorRef color1 = CGColorCreate(space1, components);
CGColorRef color2 = CGColorCreate(space2, components);
Now do color1 and color2 represent the same color? Or does the range parameter clip the components of color1 to { 50, 10, 10, 1 }? (If the latter, what's the point? An extremely rough approximation of a gamut definition? Making sure values stay within a range compatible with another data type?)
Option 3: Maybe it's used elsewhere, like when doing gamut mapping with a perceptual rendering intent
Having some idea of the range of L*a*b* values to expect might assist with gamut mapping, particularly in the perceptual case, but again, this seems like such a rough approximation that I don't see why it would be particularly useful.
Option 4: Something else?
I did some testing, and it looks like the answer is:
The range parameter scales the a* and b* component values.
Also, the component values do not appear to be clipped to the specified range.
So in the following example:
CGFloat range1[] = { -10, 10, -10, 10 };
CGFloat range2[] = { -100, 100, -100, 100 };
CGColorSpaceRef space1 = CGColorSpaceCreateLab(whitePoint, blackPoint, range1);
CGColorSpaceRef space2 = CGColorSpaceCreateLab(whitePoint, blackPoint, range2);
CGFloat components1[] = { 50, 10, 10, 1 };
CGColorRef color1 = CGColorCreate(space1, components1);
CGFloat components2[] = { 50, 100, 100, 1 };
CGColorRef color2 = CGColorCreate(space2, components2);
CGColorRef color3 = CGColorCreate(space1, components2);
color1 and color2 represent the same color.
color3 appears to retain the specified component values, even though they're outside the range specified by the color space.
Finally, it looks like a range specification of { -127, 127, -127, 127 } results in a color space with the standard CIELAB scales for the a* and b* axes.
If anyone has a more authoritative answer, please post!
Related
I'd like to use Nim to check the results of my Puppeteer test run executions.
Part of the end result is a screenshot. That screenshot should contain a certain amount of active colours. An active colour being orange, blue, red, or green. They indicate activity is present in the incoming data. Black, grey, and white need to be excluded, they only represent static data.
I haven't found a solution I can use yet.
import stb_image/read as stbi
var
w, h , c:int
data: seq[uint8]
cBin: array[256,int] #colour range was 0->255 afaict
data = stbi.load("screenshot.png",w,h,c,stbi.Default)
for d in data:
cBin[(int)d] = cBin[(int)d] + 1
echo cBin
Now I have a uint array, which I can see I can use to construct a histogram of the values, but I don't know how to map these to something like RGB values. Pointers anyone?
Is there a better package which has this automagically, I didn't spot one.
stbi.load() will return a sequence of interleaved uint8 color components. The number of interleaved components is determined either by c (i.e. channels_in_file) or desired_channels when it is non-zero.
For example, when channels_in_file == stbi.RGB and desired_channels == stbi.Default there are 3 interleaved components of red, green, and blue.
[
# r g b
255, 0, 0, # Pixel 1
0, 255, 0, # Pixel 2
0, 0, 255, # Pixel 3
]
You can process the above like:
import colors
for i in countUp(0, data.len - 3, step = stbi.RGB):
let
r = data[i + 0]
g = data[i + 1]
b = data[i + 2]
pixelColor = colors.rgb(r, g, b)
echo pixelColor
You can read more on this within comments for the stb_image.h.
I'd like to implement something like the powerpoint image below. A gradient that goes between three values.
It starts at A (-1), the mid point is B (0), and the end is C (1).
I have realised that I can save some effort by calculating the 'start' as a-to-b, and the 'end' as b-to-c. I can do as 2 sets of 2 gradients, instead of 1 gradient with three values.
But I'm stumped (despite googling) on how to get from one colour to another - ideally in the RGB colour space.
I'd like to be able to have something like this -
const colourSpace = (value, startColor, endColor) => {...}
colorSpace(-0.25, red, yellow) // some sort of orangey color
colorSpace(1, yellow, green) // fully green
colorSpace(0.8, yellow, green) // mostly green
This isn't a front-end application, so no CSS gradients - which is what google was mostly referencing.
Thanks all,
Ollie
If you aren't too worried about being perceptually consistent across the color space (you would need to work in something like LAB mode to do that), you can just take the linear interpolation in RGB space. Basically you take a distance (between 0 and 1), multiply it by the different in the coordinates, and add it to the first one. This will allow you to find arbitrary points (i.e colors) along the line between any two colors.
For example between red and yellow:
let canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
let rgb1 = [255, 0, 0] // red
let rgb2 = [255, 255, 0] // yellow
function getPoint(d, a1, a2) {
// find a color d% between a1 and a2
return a1.map((p, i) => Math.floor(a1[i] + d * (a2[i] - a1[i])))
}
// for demo purposes fill a canvas
for (let i = 0, j = 0; i < 1; i += .002, j++) {
let rgb = getPoint(i, rgb1, rgb2)
ctx.fillStyle = `rgba(${rgb.join(",")}, 1)`
ctx.fillRect(j, 0, 1, 200);
}
<canvas id="canvas" width="500"></canvas>
You can repeat this to get multiple 'stops' in the gradient.
I ended up using Chroma for converting between colour spaces.
i test a lot converters hex to hsv rgb to hsv and other options. But don't understand situation i have paint program which i see use HSV palette. i use TinyColor converter. I don't know why i sometimes get good color, sometimes not good.
This return good result red color:
var color = tinycolor("#FF0000"); //red
color.toHsv(); // return { h: 0, s: 1, v: 1 }
This return bad result not yellow color:
var color = tinycolor("#FFFF00"); //yellow
color.toHsv(); // return { h: 60, s: 1, v: 1 } and i get not yellow color
If i write in my hsv input like this:
h: 0.16
s: 1
v: 1
i get yellow collor WTF?
I see in my HSV palette i can write only one digit numbers like this:
1, 0.1, 0.99, max is 1 min is 0.00
Hue, the h in hsv, is traditionally expressed in degrees around a circle — the color wheel, which means it can have a value from 0º - 360º. See: http://en.wikipedia.org/wiki/Hue
It is sometimes convenient to express this as a percentage instead where 0= 0º, 0.5 = 180º, 1.0 = 360º, etc. The documentation for TinyColor explains that it will accept either input, but it is not clear what its default output is (at least from my quick scan).
It seem to be returning degrees, but your other application is expecting a percentage. A 60º hue is yellow, but you may need need to convert to a percentage for whatever application you're using with the hsv palette.
In this particular case, 60º/360º = 0.1667
I'm trying to create something a little like a quantize scale, but act like a linear color scale?
When I try to put multiple colors into a linear scale, it seems to only scale between the first two colors.
I'd like multiple colors like a quantize scale, but fade between those colors. I'm unsure if this is possible.
//red and green works ok
var color = d3.scale.linear()
.range(['red','green']);
//doesn't work - only red and green show
var color = d3.scale.linear()
.range(['red','green', 'blue']);
//red green and blue show, however it doesn't fade between the colors
var color = d3.scale.quantize()
.range(['red','green', 'blue']);
You have to use domain 'pivot' value like:
d3.scale.linear()
.domain([0, pivot, max])
.range(['red', 'green', 'blue']);
From the documentation for continuous scale domains:
Although continuous scales typically have two values each in their domain and range, specifying more than two values produces a piecewise scale. For example, to create a diverging color scale that interpolates between white and red for negative values, and white and green for positive values, say:
var color = d3.scaleLinear()
.domain([-1, 0, 1])
.range(["red", "white", "green"]);
color(-0.5); // "rgb(255, 128, 128)"
color(+0.5); // "rgb(128, 192, 128)"
Anto's solution works great if you want to blend 3 colors. In my case, I needed a way to blend an arbitrary number of colors. For me, the trick was to set up the domain correctly. Take for example, the following array of colors:
var colors = ['#084594', '#2171b5', '#4292c6', '#6baed6', '#9ecae1', '#c6dbef', '#eff3ff'];
You can create a domain array with values from -1 to +1 like this:
var domain = [-1];
var increment = 2/(colors.length-1);
for (var i=0; i<colors.length-2; i++){
var previous = domain[domain.length-1];
domain.push(previous+increment);
}
domain.push(1);
Once the domain array is created, you can create a color function like this:
var getColor = d3.scaleLinear()
.domain(domain)
.range(colors);
If you want to get color values at specific percentages (like chroma does), you can do something like this:
var p = 0.25; //Valid range for p is 0.0-1.0
var x = (p*2)-1;
var color = d3.color(getColor(x));
console.log(color.formatHex());
How to set an arbitrary HSLA color instead of gray20?
draw = Magick::Draw.new
draw.font_family = 'arial'
draw.pointsize = 12
draw.gravity = Magick::CenterGravity
draw.annotate(#canvas, size,size, x,y, text) { self.fill = 'gray20' }
Also, using gc.rectangle, how to set the HSLA color of the fill?
gc = Magick::Draw.new
gc.fill ????
gc.rectangle(x,y, x + size,y + size)
From the RMagick documentation:
Many RMagick methods expect color name arguments or return color names. A color name can be
an X11 color name such as "red", "chocolate", or "lightslategray".
an SVG color name (similar to the X color names), or
a string in one of the formats shown in the following table.
...
hsla(h,s,l,a)
And HSL documentation
hsla(33.3333%, 100%, 50%, 1.0) green with an alpha value of 1.0
hsla(120, 255, 127.5, 1.0) green with an alpha value of 1.0
So, use a string: fill 'hsl(0%,100%,100%,1)'