Skip to main content

src/sparklinekit@internal@color.erl

-module(sparklinekit@internal@color).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sparklinekit/internal/color.gleam").
-export([with_alpha/2, parse_hex/1, parse_or/2, over/2, to_hex_rgb/1, to_hex_rgba/1]).
-export_type([rgba/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-type rgba() :: {rgba, integer(), integer(), integer(), integer()}.

-file("src/sparklinekit/internal/color.gleam", 72).
?DOC(false).
-spec with_alpha(rgba(), float()) -> rgba().
with_alpha(Rgba, Factor) ->
    {rgba, R, G, B, A} = Rgba,
    Clamped = case Factor of
        F when F < +0.0 ->
            +0.0;

        F@1 when F@1 > 1.0 ->
            1.0;

        F@2 ->
            F@2
    end,
    New_a = erlang:round(erlang:float(A) * Clamped),
    {rgba, R, G, B, New_a}.

-file("src/sparklinekit/internal/color.gleam", 165).
?DOC(false).
-spec parse_nibble(binary()) -> {ok, integer()} | {error, nil}.
parse_nibble(Grapheme) ->
    case string:lowercase(Grapheme) of
        <<"0"/utf8>> ->
            {ok, 0};

        <<"1"/utf8>> ->
            {ok, 1};

        <<"2"/utf8>> ->
            {ok, 2};

        <<"3"/utf8>> ->
            {ok, 3};

        <<"4"/utf8>> ->
            {ok, 4};

        <<"5"/utf8>> ->
            {ok, 5};

        <<"6"/utf8>> ->
            {ok, 6};

        <<"7"/utf8>> ->
            {ok, 7};

        <<"8"/utf8>> ->
            {ok, 8};

        <<"9"/utf8>> ->
            {ok, 9};

        <<"a"/utf8>> ->
            {ok, 10};

        <<"b"/utf8>> ->
            {ok, 11};

        <<"c"/utf8>> ->
            {ok, 12};

        <<"d"/utf8>> ->
            {ok, 13};

        <<"e"/utf8>> ->
            {ok, 14};

        <<"f"/utf8>> ->
            {ok, 15};

        _ ->
            {error, nil}
    end.

-file("src/sparklinekit/internal/color.gleam", 154).
?DOC(false).
-spec parse_byte(binary()) -> {ok, integer()} | {error, nil}.
parse_byte(Pair) ->
    case gleam@string:to_graphemes(Pair) of
        [Hi, Lo] ->
            gleam@result:'try'(
                parse_nibble(Hi),
                fun(Hi_v) ->
                    gleam@result:'try'(
                        parse_nibble(Lo),
                        fun(Lo_v) -> {ok, (Hi_v * 16) + Lo_v} end
                    )
                end
            );

        _ ->
            {error, nil}
    end.

-file("src/sparklinekit/internal/color.gleam", 136).
?DOC(false).
-spec long_hex(binary(), boolean()) -> {ok, rgba()} | {error, nil}.
long_hex(Body, Full_alpha) ->
    case Full_alpha of
        true ->
            gleam@result:'try'(
                parse_byte(gleam@string:slice(Body, 0, 2)),
                fun(R) ->
                    gleam@result:'try'(
                        parse_byte(gleam@string:slice(Body, 2, 2)),
                        fun(G) ->
                            gleam@result:'try'(
                                parse_byte(gleam@string:slice(Body, 4, 2)),
                                fun(B) -> {ok, {rgba, R, G, B, 255}} end
                            )
                        end
                    )
                end
            );

        false ->
            gleam@result:'try'(
                parse_byte(gleam@string:slice(Body, 0, 2)),
                fun(R@1) ->
                    gleam@result:'try'(
                        parse_byte(gleam@string:slice(Body, 2, 2)),
                        fun(G@1) ->
                            gleam@result:'try'(
                                parse_byte(gleam@string:slice(Body, 4, 2)),
                                fun(B@1) ->
                                    gleam@result:'try'(
                                        parse_byte(
                                            gleam@string:slice(Body, 6, 2)
                                        ),
                                        fun(A) ->
                                            {ok, {rgba, R@1, G@1, B@1, A}}
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
    end.

-file("src/sparklinekit/internal/color.gleam", 187).
?DOC(false).
-spec expand(integer()) -> integer().
expand(Nibble) ->
    (Nibble * 16) + Nibble.

-file("src/sparklinekit/internal/color.gleam", 117).
?DOC(false).
-spec short_hex(binary(), boolean()) -> {ok, rgba()} | {error, nil}.
short_hex(Body, Full_alpha) ->
    case {gleam@string:to_graphemes(Body), Full_alpha} of
        {[R, G, B], true} ->
            gleam@result:'try'(
                parse_nibble(R),
                fun(R_v) ->
                    gleam@result:'try'(
                        parse_nibble(G),
                        fun(G_v) ->
                            gleam@result:'try'(
                                parse_nibble(B),
                                fun(B_v) ->
                                    {ok,
                                        {rgba,
                                            expand(R_v),
                                            expand(G_v),
                                            expand(B_v),
                                            255}}
                                end
                            )
                        end
                    )
                end
            );

        {[R@1, G@1, B@1, A], false} ->
            gleam@result:'try'(
                parse_nibble(R@1),
                fun(R_v@1) ->
                    gleam@result:'try'(
                        parse_nibble(G@1),
                        fun(G_v@1) ->
                            gleam@result:'try'(
                                parse_nibble(B@1),
                                fun(B_v@1) ->
                                    gleam@result:'try'(
                                        parse_nibble(A),
                                        fun(A_v) ->
                                            {ok,
                                                {rgba,
                                                    expand(R_v@1),
                                                    expand(G_v@1),
                                                    expand(B_v@1),
                                                    expand(A_v)}}
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            );

        {_, _} ->
            {error, nil}
    end.

-file("src/sparklinekit/internal/color.gleam", 33).
?DOC(false).
-spec parse_hex(binary()) -> {ok, rgba()} | {error, nil}.
parse_hex(Value) ->
    case gleam_stdlib:string_starts_with(Value, <<"#"/utf8>>) of
        false ->
            {error, nil};

        true ->
            Body = gleam@string:drop_start(Value, 1),
            case string:length(Body) of
                3 ->
                    short_hex(Body, true);

                4 ->
                    short_hex(Body, false);

                6 ->
                    long_hex(Body, true);

                8 ->
                    long_hex(Body, false);

                _ ->
                    {error, nil}
            end
    end.

-file("src/sparklinekit/internal/color.gleam", 50).
?DOC(false).
-spec parse_or(binary(), rgba()) -> rgba().
parse_or(Value, Fallback) ->
    case parse_hex(Value) of
        {ok, Rgba} ->
            Rgba;

        {error, _} ->
            Fallback
    end.

-file("src/sparklinekit/internal/color.gleam", 199).
?DOC(false).
-spec clamp_byte(integer()) -> integer().
clamp_byte(Value) ->
    case Value of
        V when V < 0 ->
            0;

        V@1 when V@1 > 255 ->
            255;

        V@2 ->
            V@2
    end.

-file("src/sparklinekit/internal/color.gleam", 103).
?DOC(false).
-spec blend_channel(integer(), integer(), float(), float(), float()) -> integer().
blend_channel(Fg_channel, Bg_channel, Fg_alpha, Bg_alpha, Out_alpha) ->
    Fc = erlang:float(Fg_channel),
    Bc = erlang:float(Bg_channel),
    Blended = case Out_alpha of
        +0.0 -> +0.0;
        -0.0 -> -0.0;
        Gleam@denominator -> ((Fc * Fg_alpha) + ((Bc * Bg_alpha) * (1.0 - Fg_alpha)))
        / Gleam@denominator
    end,
    clamp_byte(erlang:round(Blended)).

-file("src/sparklinekit/internal/color.gleam", 85).
?DOC(false).
-spec over(rgba(), rgba()) -> rgba().
over(Fg, Bg) ->
    {rgba, Fr, Fg_, Fb, Fa} = Fg,
    {rgba, Br, Bg_, Bb, Ba} = Bg,
    Fa_f = erlang:float(Fa) / 255.0,
    Ba_f = erlang:float(Ba) / 255.0,
    Out_a_f = Fa_f + (Ba_f * (1.0 - Fa_f)),
    case Out_a_f =< +0.0 of
        true ->
            {rgba, 0, 0, 0, 0};

        false ->
            {rgba,
                blend_channel(Fr, Br, Fa_f, Ba_f, Out_a_f),
                blend_channel(Fg_, Bg_, Fa_f, Ba_f, Out_a_f),
                blend_channel(Fb, Bb, Fa_f, Ba_f, Out_a_f),
                clamp_byte(erlang:round(Out_a_f * 255.0))}
    end.

-file("src/sparklinekit/internal/color.gleam", 191).
?DOC(false).
-spec two_hex(integer()) -> binary().
two_hex(Value) ->
    S = gleam@int:to_base16(clamp_byte(Value)),
    case string:length(S) of
        1 ->
            <<"0"/utf8, (string:lowercase(S))/binary>>;

        _ ->
            string:lowercase(S)
    end.

-file("src/sparklinekit/internal/color.gleam", 59).
?DOC(false).
-spec to_hex_rgb(rgba()) -> binary().
to_hex_rgb(Rgba) ->
    {rgba, R, G, B, _} = Rgba,
    <<<<<<"#"/utf8, (two_hex(R))/binary>>/binary, (two_hex(G))/binary>>/binary,
        (two_hex(B))/binary>>.

-file("src/sparklinekit/internal/color.gleam", 65).
?DOC(false).
-spec to_hex_rgba(rgba()) -> binary().
to_hex_rgba(Rgba) ->
    {rgba, R, G, B, A} = Rgba,
    <<<<<<<<"#"/utf8, (two_hex(R))/binary>>/binary, (two_hex(G))/binary>>/binary,
            (two_hex(B))/binary>>/binary,
        (two_hex(A))/binary>>.