Search code examples
c++color-spacehsl

Algorithm for color picker


I'm looking to understand the basic algorithm behind a color picker like this:

color picker image

I'd like to implement something like this from scratch but want to understand the basic algorithm first.


Solution

  • I believe this is intended to follow the hue-saturation-brightness model (or something close to it like hue-saturation-value, anyway).

    With HSB, it's usually easiest to think of colors as a cylinder. The Hue represents a rotation around the circle. Saturation is the position along the axis of the cylinder, and brightness is the distance from the center to the outside.

    Looking at things this way, the band on the right represents the Hue, so as we move it, we're selecting a rotation around the cylinder.

    enter image description here

    The square on the left is a slice of that cylinder from the center to the outside. The Y axis of that square represents lightness/brightness. At 0 brightness (the bottom) we get black, regardless of anything else.

    The X axis of the square represents saturation. At the far left is 0 saturation, so at the very edge, we just have a band of pure greys going from pure black at the bottom to pure white at the top. Moving to the right, saturation increases until we get to the right side, where we have pure (in this case) reds varying from minimum brightness at the bottom to maximum brightness at the top.

    Note that these (essentially all cylindrical models of color) have some...oddities about how they represent color. For example, At a light/brightness of 0, the other two input (hue and saturation) become meaningless, because you get pure black regardless of them. Likewise, at maximum lightness/brightness, you get pure white, regardless of the values of the other two.

    If you're going to code this from scratch anyway, I'd personally consider trying to implement a bi-conic model. Instead of a cylinder, this treats colors as a double-cone, with a radius of 0 at each end, and some maximum radius at the middle. At minimum or maximum brightness, we have a radius of 0, accurately reflecting the fact that pure black or pure white has no color component. Approximately halfway between those two extremes, we have the possibility for maximum saturation.

    The main difference with a biconic model would be that on the left instead of a square, we'd end up with a triangle. Much like the square, the left side would have pure greys, going from pure black at the bottom to pure white at the top. Going to the right, we'd have varying levels of saturation at that brightness, with maximum saturation available exactly halfway between the two. At lower and higher levels of brightness, less and less saturation would be available, until we got to the extremes, where the saturation would always be zero.

    enter image description here

    At the bottom we have a lightness of zero, so the only color you can pick is black. Likewise, at the very top the only color you can pick is white. Halfway between the two, you can pick the most saturated color at the chosen angle (pure red in this particular case). As you get closer to the top/bottom, you can get higher/lower brightness, and maximum saturation available is reduced.

    Just like the cylindrical models, you also need some way to choose the hue. Personally, I'd put it on the left instead of the right, but maybe that's just me. I'd also draw it as a circle, showing the colors blended together, and have a line that user pulls to a chosen angle on the circle. At least in my opinion, this would be less confusing. The weakness is that a circle obviously takes up more screen space than a rectangle (but let's face it: 640x480 displays aren't terribly common any more, so this isn't much of an issue).

    enter image description here