Current section
Files
Jump to
Current section
Files
lib/adam7/png.ex
defmodule Adam7.PNG do
alias Adam7.Pass
@moduledoc """
Module for handling PNG-specific behaviour of Adam7
"""
@doc """
Takes in raw image content along with basic dimensions about the image.
Converts that into seven separate "images" for decoding. Each "image" returned
is a list of binary, each binary corresponding to a scanline of pixels. Each
scanline is composed of a 1 byte indicator of the filter method followed by
`n` pixels where `n = bits_per_pixel * width`.
The scanlines should be defiltered before recomposing the pixels.
`bits_per_pixel` should be equal to the number of pixels per channel * the
number of channels per channel. So for a 16 bit RGB image, that would be
`16 * 3` while for a grayscale with alpha it would be `2 * 1`.
"""
def extract_images({width, height, bits_per_pixel}, content) do
Pass.sizes(width, height)
|> extract_images_scanlines(bits_per_pixel, content)
end
defp extract_images_scanlines(images_dimensions, bits_per_pixel, content) do
extract_images_scanlines(images_dimensions, bits_per_pixel, content, [])
end
defp extract_images_scanlines([], _bits_per_pixel, _content, images) do
Enum.reverse images
end
defp extract_images_scanlines([dimensions | rest_dimensions], bits_per_pixel, content, images) do
{image, rest_content} = extract_image_scanlines(dimensions, bits_per_pixel, content)
extract_images_scanlines(rest_dimensions, bits_per_pixel, rest_content, [image | images])
end
defp extract_image_scanlines(dimensions, bits_per_pixel, content) do
extract_image_scanlines(dimensions, bits_per_pixel, content, [])
end
defp extract_image_scanlines({0, _height}, _bits_per_pixel, content, scanlines) do
{Enum.reverse(scanlines), content}
end
defp extract_image_scanlines({_width, 0}, _bits_per_pixel, content, scanlines) do
{Enum.reverse(scanlines), content}
end
defp extract_image_scanlines({width, height}, bits_per_pixel, content, scanlines) do
{scanline, rest_content} = extract_image_scanline(width, bits_per_pixel, content)
extract_image_scanlines({width, height-1}, bits_per_pixel, rest_content, [scanline | scanlines])
end
defp extract_image_scanline(width, bits_per_pixel, content) do
# There is an additional byte at the beginning of the scanline to indicate the
# filter method.
scanline_size = 8 + width * bits_per_pixel
<<scanline::bits-size(scanline_size), rest_content::bits>> = content
{scanline, rest_content}
end
end