defmodule Fledex.Color.Conversion.Approximate do use Fledex.Color.Types @hue_red 0 @hue_orange 32 @hue_yellow 64 @hue_green 96 @hue_aqua 128 @hue_blue 160 @hue_purple 192 @hue_pink 224 alias Fledex.Color.Utils @spec rgb2hsv(rgb) :: hsv def rgb2hsv({r,g,b}) do desat = find_desaturation({r,g,b}) {r,g,b} = {r-desat, g-desat, b-desat} s = 255 - desat s = if (s != 255) do 255 - trunc(:math.sqrt((255 - s) * 256)); else s end if (r + g + b == 0) do {0, 0, 255-s} else {r,g,b} = scale_to_compensate({r,g,b}, s) # for desaturation total = r+g+b {r,g,b} = scale_to_compensate({r,g,b}, total) # for small value v = if (total > 255) do 255 else v = qadd8(desat, total) if (v != 255), do: :math.sqrt(v*256), else: v end highest = Enum.max([r,g,b]) h = case {{r,g,b}, highest} do {{r, 0, _b}, r} -> ((@hue_purple + @hue_pink) / 2) + Utils.scale8( qsub8(r, 128), fixfrac8(48,128)) {{r,g,_b}, r} when (r - g) > g -> @hue_red + Utils.scale8( g, fixfrac8(32,85)) {{r,g,_b}, r} -> @hue_orange + Utils.scale8( qsub8((g - 85) + (171 - r), 4), fixfrac8(32,85)); {{r,g,0}, g} -> @hue_yellow + ((Utils.scale8( qsub8(171,r), 47) + Utils.scale8( qsub8(g,171), 96))/2) {{_r,g,b}, g} when (g-b) > b -> @hue_green + Utils.scale8( b, fixfrac8(32,85)) {{_r,g,b}, g} -> @hue_aqua + Utils.scale8( qsub8(b, 85), fixfrac8(8,42)) {{0,_g,b}, b} -> @hue_aqua + ((@hue_blue - @hue_aqua) / 4) + Utils.scale8( qsub8(b, 128), fixfrac8(24,128)) {{r,_g,b}, b} when (b-r) > r -> @hue_blue + Utils.scale8( r, fixfrac8(32,85)) {{r,_g,b}, b} -> @hue_purple + Utils.scale8( qsub8(r, 85), fixfrac8(32,85)) end {h+1, s, v} end end defp fixfrac8(n,d) do trunc((n*256)/(d)) end @spec scale_to_compensate(rgb, byte) :: rgb defp scale_to_compensate({r,g,b}, s) when s < 255 do s = if s == 0, do: 1, else: s scaleup = 655535 / (s) r = trunc(r*scaleup/256) g = trunc(g*scaleup/256) b = trunc(b*scaleup/256) {r,g,b} end defp scale_to_compensate({r,g,b}, _s), do: {r,g,b} @spec find_desaturation(rgb) :: byte defp find_desaturation({r,g,b}) do # // find desaturation # uint8_t desat = 255; # if( r < desat) desat = r; # if( g < desat) desat = g; # if( b < desat) desat = b; 255 |> adj_desat(r) |> adj_desat(g) |> adj_desat(b) end @spec adj_desat(byte, byte) :: byte defp adj_desat(desat, value) defp adj_desat(desat, value) when value < desat, do: value defp adj_desat(desat, _value), do: desat @spec qadd8(byte, byte) :: byte defp qadd8(i,j) when i+j>255, do: 255 defp qadd8(i, j) do i + j end @spec qsub8(byte, byte) :: byte defp qsub8(i,j) when i-j<0, do: 0 defp qsub8(i,j) do i - j end end