Skip to content

Commit 8487c71

Browse files
committed
Convert srgb -> lab
1 parent a4f718a commit 8487c71

File tree

3 files changed

+115
-17
lines changed

3 files changed

+115
-17
lines changed

apps/components_guide_web/lib/components_guide_web/live/color.ex

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ defmodule ComponentsGuideWeb.ColorLive do
55
defmodule State do
66
defstruct color: {:lab, 50, 100, -128}
77

8+
def from_input(
9+
"#" <>
10+
<<r1::utf8>> <>
11+
<<r2::utf8>> <> <<g1::utf8>> <> <<g2::utf8>> <> <<b1::utf8>> <> <<b2::utf8>>
12+
) do
13+
# <<cp::utf8>>
14+
r = <<r1::utf8>> <> <<r2::utf8>>
15+
g = <<g1::utf8>> <> <<g2::utf8>>
16+
b = <<b1::utf8>> <> <<b2::utf8>>
17+
18+
r = String.to_integer(r, 16) / 255
19+
g = String.to_integer(g, 16) / 255
20+
b = String.to_integer(b, 16) / 255
21+
22+
color = {:srgb, r, g, b} |> Styling.convert(:lab)
23+
24+
IO.puts("INPUT #{r} #{g} #{b} OUTPUT #{inspect(color)}")
25+
%State{color: color}
26+
end
27+
828
def set_color(state = %__MODULE__{}, color), do: %{state | color: color}
929

1030
def l(%__MODULE__{color: {:lab, l, _, _}}), do: l
@@ -13,8 +33,6 @@ defmodule ComponentsGuideWeb.ColorLive do
1333

1434
def css(%__MODULE__{color: color}), do: StylingHelpers.to_css(color)
1535

16-
defp clamp_0_1(n), do: n |> max(0) |> min(1)
17-
1836
defp to_srgb(%__MODULE__{color: color}) do
1937
{:srgb, r, g, b} = color |> StylingHelpers.convert(:srgb) |> StylingHelpers.clamp()
2038
{r, g, b}
@@ -23,11 +41,12 @@ defmodule ComponentsGuideWeb.ColorLive do
2341
def hex(state = %__MODULE__{}) do
2442
{r, g, b} = state |> to_srgb()
2543

26-
digits = [r, g, b]
27-
|> Enum.map(fn c ->
28-
round(c * 255) |> Integer.to_string(16) |> String.pad_leading(2, "0")
29-
end)
30-
|> Enum.join()
44+
digits =
45+
[r, g, b]
46+
|> Enum.map(fn c ->
47+
round(c * 255) |> Integer.to_string(16) |> String.pad_leading(2, "0")
48+
end)
49+
|> Enum.join()
3150

3251
"##{digits}"
3352
end
@@ -37,11 +56,12 @@ defmodule ComponentsGuideWeb.ColorLive do
3756
swatch_size = 100
3857
{:lab, l, a, b} = assigns.state.color
3958

40-
gradient = Styling.linear_gradient("150grad", [
41-
{:lab, l * 1.5, a * 1.2, b * 0.8},
42-
{:lab, l, a, b},
43-
{:lab, l * 0.5, a * 0.8, b * 1.2},
44-
])
59+
gradient =
60+
Styling.linear_gradient("150grad", [
61+
{:lab, l * 1.5, a * 1.2, b * 0.8},
62+
{:lab, l, a, b},
63+
{:lab, l * 0.5, a * 0.8, b * 1.2}
64+
])
4565

4666
~L"""
4767
<article class="text-2xl max-w-lg mx-auto text-white">
@@ -70,7 +90,7 @@ defmodule ComponentsGuideWeb.ColorLive do
7090
<dt class="font-bold">Hex:
7191
<dd><%= State.hex(@state) %>
7292
<dt class="font-bold">CSS:
73-
<dd><%= State.css(@state) %>
93+
<dd><pre class="text-base whitespace-pre-wrap"><code><%= State.css(@state) %></code></pre>
7494
<dt class="font-bold">Gradient CSS:
7595
<dd><pre class="text-base whitespace-pre-wrap"><code><%= gradient %></code></pre>
7696
</dl>
@@ -82,10 +102,27 @@ defmodule ComponentsGuideWeb.ColorLive do
82102
{:ok, assign(socket, state: %State{})}
83103
end
84104

105+
def handle_params(%{"definition" => definition}, _path, socket) do
106+
state = State.from_input(definition)
107+
{:noreply, socket |> assign(:state, state)}
108+
end
109+
110+
def handle_params(%{}, _path, socket) do
111+
state = %State{}
112+
{:noreply, socket |> assign(:state, state)}
113+
end
114+
85115
def handle_event("lab_changed", %{"l" => l, "a" => a, "b" => b}, socket) do
86116
l = l |> String.to_integer()
87117
a = a |> String.to_integer()
88118
b = b |> String.to_integer()
89-
{:noreply, assign(socket, :state, socket.assigns.state |> State.set_color({:lab, l, a, b}))}
119+
120+
state = socket.assigns.state |> State.set_color({:lab, l, a, b})
121+
hex = state |> State.hex()
122+
123+
{:noreply,
124+
socket
125+
|> assign(:state, state)
126+
|> push_patch(to: Routes.color_path(socket, :show, hex), replace: true)}
90127
end
91128
end

apps/components_guide_web/lib/components_guide_web/router.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ defmodule ComponentsGuideWeb.Router do
2929
AccessibilityFirstController,
3030
only: [:index, :show]
3131

32-
get "/color", ColorController, :index
32+
live "/color", ColorLive, :index
33+
live "/color/:definition", ColorLive, :show
3334

3435
get "/swiftui", SwiftUIController, :index
3536
get "/react+typescript", ReactTypescriptController, :index

apps/components_guide_web/lib/components_guide_web/views/styling_helpers.ex

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ defmodule ComponentsGuideWeb.StylingHelpers do
1313
end
1414
end
1515

16+
defp convert_component({:srgb, c}, :linear_srgb) do
17+
if c < 0.04045 do
18+
c / 12.92
19+
else
20+
:math.pow((c + 0.055) / 1.055, 2.4)
21+
end
22+
end
23+
1624
def convert({:linear_srgb, r, g, b}, :srgb) do
1725
{
1826
:srgb,
@@ -22,16 +30,43 @@ defmodule ComponentsGuideWeb.StylingHelpers do
2230
}
2331
end
2432

25-
def convert({:xyz, x, y, z}, :srgb) do
33+
def convert({:xyz, x, y, z}, :linear_srgb) do
2634
{
2735
:linear_srgb,
2836
x * 3.1338561 - y * 1.6168667 - 0.4906146 * z,
2937
x * -0.9787684 + y * 1.9161415 + 0.0334540 * z,
3038
x * 0.0719453 - y * 0.2289914 + 1.4052427 * z
3139
}
40+
end
41+
42+
def convert({:xyz, _, _, _} = input, :srgb) do
43+
input
44+
|> convert(:linear_srgb)
3245
|> convert(:srgb)
3346
end
3447

48+
def convert({:srgb, r, g, b}, :linear_srgb) do
49+
{
50+
:linear_srgb,
51+
convert_component({:srgb, r}, :linear_srgb),
52+
convert_component({:srgb, g}, :linear_srgb),
53+
convert_component({:srgb, b}, :linear_srgb)
54+
}
55+
end
56+
57+
def convert({:linear_srgb, r, g, b}, :xyz) do
58+
{
59+
:xyz,
60+
0.4360747 * r + 0.3850649 * g + 0.1430804 * b,
61+
0.2225045 * r + 0.7168786 * g + 0.0606169 * b,
62+
0.0139322 * r + 0.0971045 * g + 0.7141733 * b
63+
}
64+
end
65+
66+
def convert({:srgb, _, _, _} = input, :xyz) do
67+
input |> convert(:linear_srgb) |> convert(:xyz)
68+
end
69+
3570
@xn 0.96422
3671
@yn 1.00000
3772
@zn 0.82521
@@ -62,8 +97,33 @@ defmodule ComponentsGuideWeb.StylingHelpers do
6297
}
6398
end
6499

100+
def convert({:xyz, x, y, z}, :lab) do
101+
converter = fn v ->
102+
if v > @e do
103+
:math.pow(v, 1.0 / 3.0)
104+
else
105+
(@k * v + 16) / 116
106+
end
107+
end
108+
109+
f0 = converter.(x / @xn)
110+
f1 = converter.(y / @yn)
111+
f2 = converter.(z / @zn)
112+
113+
{
114+
:lab,
115+
116 * f1 - 16,
116+
500 * (f0 - f1),
117+
200 * (f1 - f2)
118+
}
119+
end
120+
65121
def convert({:lab, _l, _a, _b} = input, :srgb) do
66-
convert(input, :xyz) |> convert(:srgb)
122+
input |> convert(:xyz) |> convert(:srgb)
123+
end
124+
125+
def convert({:srgb, _, _, _} = input, :lab) do
126+
input |> convert(:xyz) |> convert(:lab)
67127
end
68128

69129
defp clamp_0_1(n) when is_number(n), do: n |> max(0) |> min(1)

0 commit comments

Comments
 (0)