Packages
image
0.52.2
0.72.0
0.71.0
0.70.0
0.69.0
0.68.0
0.67.0
0.67.0-dev
retired
0.66.0
0.65.0
0.64.0
0.63.0
0.62.1
0.62.0
0.61.1
0.61.0
0.60.0
0.59.3
0.59.2
0.59.1
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.2
0.55.1
retired
0.55.0
0.54.4
0.54.3
0.54.2
0.54.1
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
retired
0.51.0
0.50.0
0.49.0
0.48.1
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.2
0.43.1
0.43.0
0.42.0
0.41.0
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.0
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
retired
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.4
0.14.2
0.14.1
0.14.0
retired
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.10.0-rc.0
retired
0.9.0
retired
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
An approachable image processing library primarily based upon Vix and libvips that is NIF-based, fast, multi-threaded, pipelined and has a low memory footprint.
Current section
Files
Jump to
Current section
Files
lib/image/blurhash/decoder.ex
defmodule Image.Blurhash.Decoder do
@moduledoc false
import Image.Blurhash.Utils
alias Image.Blurhash.Base83
import Bitwise
def decode(blurhash, width, height) do
with {:ok, {components_x, components_y}, rest} <- size_flag(blurhash),
{:ok, max_ac, rest} <- max_ac(rest),
{:ok, {average_color, dc}, rest} <- average_color_and_dc(rest),
{:ok, matrix} <- construct_matrix(rest, max_ac, components_x, components_y, dc) do
pixels = construct_pixel_iodata(width, height, matrix)
{:ok, pixels, average_color}
end
end
defp size_flag(blurhash) do
with {:ok, encoded_flag, rest} <- Base83.decode_number(blurhash, 1) do
x = rem(encoded_flag, 9) + 1
y = floor(encoded_flag / 9) + 1
{:ok, {x, y}, rest}
end
end
defp max_ac(blurhash) do
with {:ok, quantized_max, rest} <- Base83.decode_number(blurhash, 1) do
{:ok, (quantized_max + 1) / 166, rest}
end
end
defp average_color_and_dc(blurhash) do
with {:ok, raw, rest} <- Base83.decode_number(blurhash, 4) do
{r, g, b} = color = {bsr(raw, 16), band(bsr(raw, 8), 255), band(raw, 255)}
dc = {srgb_to_linear(r), srgb_to_linear(g), srgb_to_linear(b)}
{:ok, {color, dc}, rest}
end
end
def construct_matrix(encoded_ac, max_ac, x, y, dc) do
size = x * y - 1
try do
# We start with 1 because {0, 0} is the DC
{ac_values, rest} =
Enum.map_reduce(1..size, encoded_ac, fn index, rest ->
case Base83.decode_number(rest, 2) do
{:ok, value, rest} ->
# add matrix position with the color since we will need it for
# inverse dct later
matrix_pos = {rem(index, x), floor(index / x)}
quantized_r = floor(value / (19 * 19))
quantized_g = floor(rem(floor(value / 19), 19))
quantized_b = rem(value, 19)
r = unquantize_color(quantized_r, max_ac)
g = unquantize_color(quantized_g, max_ac)
b = unquantize_color(quantized_b, max_ac)
{{matrix_pos, {r, g, b}}, rest}
# Haven't found a more elegant solution to throwing in this case
error ->
throw(error)
end
end)
if rest != "" do
{:error, "Unexpected number of blurhash components"}
else
{r, g, b} = dc
matrix = [{{0, 0}, {r, g, b}} | ac_values]
{:ok, matrix}
end
catch
{:error, _} -> {:error, "Invalid blurhash"}
end
end
def construct_pixel_iodata(width, height, matrix) do
Enum.reduce((height - 1)..0, [], fn y, acc ->
pixel_row =
Enum.reduce((width - 1)..0, [], fn x, acc ->
{linear_r, linear_g, linear_b} =
Enum.reduce(matrix, {0, 0, 0}, fn {{component_x, component_y},
{current_red, current_green, current_blue}},
{red, green, blue} ->
idct_basis =
:math.cos(:math.pi() * x * component_x / width) *
:math.cos(:math.pi() * y * component_y / height)
{red + current_red * idct_basis, green + current_green * idct_basis,
blue + current_blue * idct_basis}
end)
r = linear_to_srgb(linear_r)
g = linear_to_srgb(linear_g)
b = linear_to_srgb(linear_b)
[<<r::8, g::8, b::8>> | acc]
end)
[pixel_row | acc]
end)
end
end