defmodule Fledex.Color.Utils do
use Fledex.Color.Types
use Fledex.Color.Names
import Bitwise
@spec scale8_video_addition(boolean, 0..255, 0..255) :: 0|1
defp scale8_video_addition(false, _value, _scale), do: 0
defp scale8_video_addition(true, value, scale) when value != 0 and scale != 0, do: 1
defp scale8_video_addition(_, _, _), do: 0
@spec scale8(0..255, 0..255, boolean) :: 0..255
def scale8(value, scale, video \\ false)
def scale8(0, _scale, _video), do: 0
def scale8(value, scale, video) do
addition = scale8_video_addition(video, value, scale)
((value * scale) >>> 8) + addition
end
@spec nscale8(rgb, 0..255, boolean) :: rgb
def nscale8(rgb, scale, video \\ true)
def nscale8({r,g,b}, scale, video) when is_integer(scale) do
nscale8({r,g,b}, {scale, scale, scale}, video)
end
@spec nscale8(rgb, rgb, boolean) :: rgb
def nscale8({r,g,b}, {sr,sg,sb}, video) do
{scale8(r, sr, video), scale8(g, sg, video), scale8(b, sb, video)}
end
@spec nscale8(colorint, rgb, boolean) :: colorint
def nscale8(color, rgb, video) do
split_into_subpixels(color)
|> nscale8(rgb, video)
|> combine_subpixels()
end
@doc """
Splits the rgb-integer value into it's subpixels and returns an
`{r,g,b}` tupel
"""
@spec split_into_subpixels(colorint) :: rgb
def split_into_subpixels(elem) do
r = elem |> Bitwise.&&&(0xFF0000) |> Bitwise.>>>(16)
g = elem |> Bitwise.&&&(0x00FF00) |> Bitwise.>>>(8)
b = elem |> Bitwise.&&&(0x0000FF)
{r, g, b}
end
@doc """
This function adds the given subpixels `[{r1,g1,b1}, {r2,g2,b2}, ...]` together.
The result {r1+r2+..., g1+g2+..., b1+b2+...} is probably outside of the standard
8bit range and will have to be rescaled
"""
@spec add_subpixels(list(rgb)) :: {pos_integer, pos_integer, pos_integer}
def add_subpixels(elems) do
Enum.reduce(elems, {0,0,0}, fn {r,g,b}, {accr, accg, accb} ->
{r+accr, g+accg, b+accb}
end)
end
@spec avg({pos_integer, pos_integer, pos_integer}, pos_integer) :: rgb
@doc """
This function rescales the rgb values with count (default: 1) and combines
them to a single integer
"""
def avg({r,g,b}, count \\ 1) do
r = Kernel.trunc(r/count)
g = Kernel.trunc(g/count)
b = Kernel.trunc(b/count)
{r,g,b}
end
@spec cap({pos_integer, pos_integer, pos_integer}, Range.t) :: rgb
def cap({r,g,b}, min_max \\ 0..255) do
{do_cap(r, min_max), do_cap(g, min_max), do_cap(b, min_max)}
end
@spec do_cap(pos_integer, Range.t) :: pos_integer
defp do_cap(value, min..max) when min<=max do
case value do
value when value < min -> min
value when value > max -> max
value -> value
end
end
@spec combine_subpixels(rgb) :: colorint
def combine_subpixels({r,g,b}) do
(r<<<16) + (g<<<8) + b
end
@spec convert_to_subpixels((colorint | atom | rgb)) :: rgb
def convert_to_subpixels(rgb) do
case rgb do
x when is_atom(x) -> get_color_int(x) |> split_into_subpixels()
x when is_integer(x) -> split_into_subpixels(x)
x -> x
end
end
end