defmodule Fledex.Color.Conversion.Rainbow do
import Bitwise
use Fledex.Color.Types
alias Fledex.Color.Utils
@k255 255
@k171 171
@k170 170
@k85 85
@spec hsv2rgb(hsv, (rgb -> rgb)) :: rgb
def hsv2rgb({h,s,v}, extra_color_correction) do
determine_rgb(h)
|> extra_color_correction.()
|> desaturate(s)
|> scale_brightness(v)
end
@spec determine_rgb(byte) :: rgb
defp determine_rgb(h) do
main = {(h &&& 0x80) > 0, (h &&& 0x40) > 0, (h &&& 0x20) > 0}
offset = h &&& 0x1F
offset8 = offset <<< 3
third = Utils.scale8(offset8, Kernel.trunc(256/3))
twothird = Utils.scale8(offset8, Kernel.trunc((256*2) / 3))
build_rgb(main, third, twothird)
end
@spec build_rgb({boolean, boolean, boolean}, byte, byte) :: rgb
defp build_rgb({:false,:false,:false}, third, _twothird) do
{@k255 - third, third, 0}
end
defp build_rgb({:false, :false, :true}, third, _twothird) do
{@k171, @k85 + third, 0}
end
defp build_rgb({:false, :true, :false}, third, twothird) do
{@k171 - twothird, @k170 + third, 0}
end
defp build_rgb({:false, :true, :true}, third, _twothird) do
{0, @k255 - third, third}
end
defp build_rgb({:true, :false, :false}, _third, twothird) do
{0, @k171 - twothird, @k85 + twothird}
end
defp build_rgb({:true, :false, :true}, third, _twothird) do
{third, 0, @k255 - third}
end
defp build_rgb({:true, :true, :false}, third, _twothird) do
{@k85 + third, 0, @k171 - third}
end
defp build_rgb({:true, :true, :true}, third, _twothird) do
{@k170 + third, 0, @k85 - third}
end
@spec desaturate(rgb, byte) :: rgb
defp desaturate({r, g, b}, 255), do: {r, g, b}
defp desaturate(_, 0), do: {255, 255, 255}
defp desaturate({r,g,b}, s) do
desat = 255 - s
desat = Utils.scale8(desat, desat, true)
satscale = 255 - desat
{r,g,b} = Utils.nscale8({r,g,b}, satscale, true)
{r+desat, g+desat, b+desat}
end
# scales the brightness (the v part of HSV, aka HSB)
@spec scale_brightness(rgb, byte) :: rgb
defp scale_brightness(rgb, 255), do: rgb
defp scale_brightness(_, v) when ((v*v) >>> 8) == 0, do: {0, 0, 0}
defp scale_brightness({r,g,b}, v) do
val = Utils.scale8(v, v, true)
Utils.nscale8({r,g,b}, val, true)
end
end