defmodule Identicon do
@moduledoc """
`Identicon` is a module that that helps to convert a sting
into a spacial type of image
"""
@doc """
Main function that will do the conversion.
"""
@spec main(String.t()) :: any
def main(str) do
str
|> string_to_md5
|> pick_color
|> build_grid
|> build_pixel_map
|> draw_image
|> save_image(str)
end
@spec save_image(binary, String.t()) :: :ok | {:error, atom}
def save_image(image, filename) do
File.write("#{filename}.png", image)
end
@spec draw_image(%Identicon.Image{}) :: binary
def draw_image(%Identicon.Image{color: color, pixel_map: pixel_map}) do
image = :egd.create(250, 250)
fill = :egd.color(color)
brown = :egd.color({97, 88, 109})
yellow = :egd.color({226, 211, 182})
:egd.filledRectangle(image, {0, 0}, {250, 250}, brown)
for i <- 0..5 do
for j <- 0..5 do
h1 = i * 50
h2 = j * 50
:egd.filledRectangle(image, {h1 + 2, h2 + 2}, {h1 + 48, h2 + 48}, yellow)
end
end
Enum.each(
pixel_map,
fn {start, stop} ->
:egd.filledRectangle(image, start, stop, fill)
end
)
:egd.render(image)
end
@spec build_pixel_map(%Identicon.Image{}) :: %Identicon.Image{}
def build_pixel_map(%Identicon.Image{grid: grid} = image) do
pixel_map =
Enum.map(grid, fn {_value, index} ->
horizontal = rem(index, 5) * 50
vertical = div(index, 5) * 50
top_left = {horizontal + 2, vertical + 2}
bottom_right = {horizontal + 48, vertical + 48}
{top_left, bottom_right}
end)
%Identicon.Image{image | pixel_map: pixel_map}
end
@spec build_grid(%Identicon.Image{}) :: %Identicon.Image{}
def build_grid(%Identicon.Image{hex: hex} = image) do
grid =
hex
|> Enum.chunk_every(3, 3, :discard)
|> Enum.map(&mirror_row/1)
|> List.flatten()
|> Enum.with_index()
|> filter_odds_squares
%Identicon.Image{image | grid: grid}
end
@spec filter_odds_squares([{term, integer}]) :: [{term, integer}]
def filter_odds_squares(grid) do
Enum.filter(grid, fn {value, _index} ->
rem(value, 2) == 0
end)
end
@spec mirror_row([byte]) :: [byte]
def mirror_row(row) do
[hex_1, hex_2 | _tail] = row
row ++ [hex_2, hex_1]
end
@spec pick_color(%Identicon.Image{}) :: %Identicon.Image{}
def pick_color(%Identicon.Image{hex: [r, g, b | _tail]} = image) do
%Identicon.Image{image | color: {r, g, b}}
end
@spec string_to_md5(Stream.t()) :: %Identicon.Image{hex: [byte]}
def string_to_md5(str) do
hex =
:crypto.hash(:md5, str)
|> :binary.bin_to_list()
%Identicon.Image{hex: hex}
end
end