Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2014 Chris Maddox

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
45 changes: 18 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
Imagineer
=========

**N.B.**
Until 1.0 is reached, each beta release my include backwards incompatible changes.
1.0 will include parsing and writing of PNGs and JPEGs.

Image parsing in Elixir.

Currently Imagineer only supports PNGs. To load an image, create a new `Imagineer.Image` and pass it to the load function. Once the image is processed (or the raw binary is placed in the `raw` field by some other means), passing it to `process` will parse all of its data.
Currently Imagineer only supports PNGs. To load an image, call the load function on the Imagineer module. Once the image is processed (or the raw binary is placed in the `raw` field by some other means), passing it to `process` will parse all of its data.

```elixir
alias Imagineer.Image
image = %Image{uri: "./test/support/images/alpaca.png"} |>
Image.load() |>
Image.process()
Imagineer.load("./test/support/images/png/alpaca.png")
# =>
# %Imagineer.Image{
# alias: nil,
# attributes: %{
# color_type: 2,
# compression: 0,
# filter_method: 0,
# interface_method: 0,
# pixel_dimensions: {
# 5669,
# 5669,
# :meter
# }
# },
# bit_depth: 8,
# color_format: :rgb8,
# content: <<120, 1, 141, 189, 7, 148, 92, 213, 149, 254, 123, 99, 229, 208, 213, 57, 75, 106, 229, 0, 66, 66, 18, 32, 178, 49, 57, 216, 132, 193, 9, 99, 96, 108, 6, 131, 3, 14, 51, 255, 97, 198, 30, 71, 156, ...>>,
# format: :png,
# height: 96,
# mask: nil,
# raw: <<137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 96, 0, 0, 0, 96, 8, 2, 0, 0, 0, 109, 250, 224, 111, 0, 0, 12, 70, 105, 67, 67, 80, ...>>,
# uri: "./test/support/images/alpaca.png",
# width: 96}
{:ok,
%Imagineer.Image.PNG{alias: nil,
attributes: %{"XML:com.adobe.xmp": "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 5.4.0\">\n <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n <rdf:Description rdf:about=\"\"\n xmlns:exif=\"http://ns.adobe.com/exif/1.0/\">\n <exif:PixelXDimension>96</exif:PixelXDimension>\n <exif:PixelYDimension>96</exif:PixelYDimension>\n </rdf:Description>\n </rdf:RDF>\n</x:xmpmeta>\n",
pixel_dimensions: {5669, 5669, :meter}}, bit_depth: 8, color_format: :rgb8,
color_type: 2, comment: nil, compression: :zlib,
data_content: <<120, 1, 141, 189, 7, 148, 92, 213, 149, 254, 123, 99, 229, 208, 213, 57, 75, 106, 229, 0,
66, 66, 18, 32, 178, 49, 57, 216, 132, 193, 9, 99, 96, 108, 6, 131, 3, 14, 51, 255, 97, ...>>,
decompressed_data: nil, filter_method: :five_basics, format: :png, gamma: nil,
height: 96, interface_method: 0, mask: nil, palette: [],
pixels: [], # 96 rows of 96 3-element tuples each omitted for sanity.
raw: <<137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 96, 0, 0, 0, 96, 8, 2, 0, 0, #0, 109, 250, ...>>,
scanlines: [], unfiltered_rows: [], uri: nil, width: 96}}
```
29 changes: 8 additions & 21 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for third-
# party users, it should be done in your mix.exs file.
level = if System.get_env("DEBUG") do
:debug
else
:info
end

# Sample configuration:
#
# config :logger, :console,
# level: :info,
# format: "$date $time [$level] $metadata$message\n",
# metadata: [:user_id]

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
config :logger, :console,
level: level,
format: "$date $time [$level] $metadata$message\n"
31 changes: 31 additions & 0 deletions lib/imagineer.ex
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
defmodule Imagineer do
alias Imagineer.FormatDetector
alias Imagineer.Image.PNG
alias Imagineer.Image.JPG

@doc """
Loads the file from the given location and processes it into the correct
file type.
Returns `{:ok, image}` if successful or `{:error, error_message}` otherwise
"""
@spec load(uri :: binary) :: {:ok, %{}} | {:error, binary}
def load(uri) do
case File.read(uri) do
{:ok, file} ->
detect_type_and_process(file)
{:error, reason} -> {:error, "Could not open #{uri} #{file_error_description(reason)}" }
end
end

defp detect_type_and_process(content) do
case FormatDetector.detect(content) do
:png ->
{:ok, PNG.process(content)}
:jpg ->
{:ok, JPG.process(content)}
:unknown ->
{:error, "Unknown or unsupported image format."}
end
end

defp file_error_description(:enoent), do: "because the file does not exist."
defp file_error_description(reason), do: "due to #{reason}."
end
9 changes: 9 additions & 0 deletions lib/imagineer/format_detector.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Imagineer.FormatDetector do
@png_signature <<137::size(8), ?P, ?N, ?G,
13::size(8), 10::size(8), 26::size(8), 10::size(8)>>
@jpg_signature <<255::size(8), 216::size(8)>>

def detect(<<@png_signature, _rest::binary>>), do: :png
def detect(<<@jpg_signature, _rest::binary>>), do: :jpg
def detect(_), do: :unknown
end
41 changes: 3 additions & 38 deletions lib/imagineer/image.ex
Original file line number Diff line number Diff line change
@@ -1,40 +1,5 @@
defmodule Imagineer.Image do
defstruct alias: nil, width: nil, height: nil, mask: nil, bit_depth: nil,
color_format: nil, uri: nil, format: nil, attributes: %{}, content: <<>>,
raw: nil, comment: nil, mime_type: nil, components: nil
alias Imagineer.Image
alias Imagineer.Image.PNG
alias Imagineer.Image.JPG

@png_signature <<137::size(8), 80::size(8), 78::size(8), 71::size(8),
13::size(8), 10::size(8), 26::size(8), 10::size(8)>>
@jpg_signature <<255::size(8), 216::size(8)>>

def load(%Image{uri: uri}=image) do
case File.read(uri) do
{:ok, file} ->
%Image{image | raw: file}
{:error, reason} -> {:error, "Could not open #{uri} due to #{reason}" }
end
end

def process(%Image{format: :png}=image) do
PNG.process(image)
end

def process(%Image{format: :jpg}=image) do
JPG.process(image)
end

def process(%Image{raw: raw}=image) when not is_nil(raw) do
process %Image{ image | format: detect_format(image.raw) }
end

defp detect_format(<<@png_signature, _png_body::binary>>) do
:png
end

defp detect_format(<<@jpg_signature, _png_body::binary>>) do
:jpg
end
use Behaviour
@type supported_image :: %{}
defcallback process(raw_content :: bitstring) :: supported_image
end
Loading