defmodule DataMatrix.Render.PNG do @moduledoc false @signature <<0x89, "PNG\r\n", 0x1A, "\n">> @defaults %{ module_size: 10, dark: <<0, 0, 0>>, light: <<255, 255, 255>> } @doc """ """ def format(%{nrow: nrow, ncol: ncol, matrix: rows}, opts \\ []) do opts = Map.merge(@defaults, Map.new(opts)) opts = %{ opts | dark: to_rgb(opts[:dark]), light: to_rgb(opts[:light]) } width = ncol * opts[:module_size] height = nrow * opts[:module_size] idhr = png_chunk("IHDR", <>) idat = png_chunk("IDAT", pixels(rows, opts)) iend = png_chunk("IEND", "") [@signature, idhr, idat, iend] |> List.flatten() |> Enum.join() end defp to_rgb("#" <> rgb) do <> end defp to_rgb(rgb), do: rgb defp png_chunk(type, binary) do length = byte_size(binary) crc = :erlang.crc32(type <> binary) [<>, type, binary, <>] end defp pixels(rows, opts) do rows |> Enum.map(&row_pixels(&1, opts)) |> Enum.join() |> :zlib.compress() end defp row_pixels(row, opts) do pixels = row |> Stream.map(&module_pixels(&1, opts)) |> Enum.join() :binary.copy(<<0>> <> pixels, opts[:module_size]) end defp module_pixels(0, opts) do :binary.copy(<>, opts[:module_size]) end defp module_pixels(1, opts) do :binary.copy(<>, opts[:module_size]) end end