Packages
image
0.62.0
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/options/embed.ex
defmodule Image.Options.Embed do
@moduledoc """
Options and option validation for `Image.embed/4`.
"""
alias Image.Color
@typedoc """
Options applicable to `Image.embed/4`.
"""
@type embed_option ::
{:background_color, Color.t() | :average}
| {:background_transparency, Color.transparency()}
| {:extend_mode, extend_mode()}
| {:x, non_neg_integer() | :center}
| {:y, non_neg_integer() | :center}
@typedoc """
When extending the canvas the generated
pixels are determined by this option.
"""
@type extend_mode ::
:black
| :white
| :copy
| :repeat
| :mirror
| :background
@typedoc """
Options list for `Image.embed/4`.
"""
@type embed_options :: [embed_option()]
@doc """
Validate the options for `Image.embed/4`.
"""
def validate_options(image, width, height, options) when is_list(options) do
options = Keyword.merge(default_options(), options)
case Enum.reduce_while(options, options, &validate_option(&1, image, width, height, &2)) do
{:error, value} ->
{:error, value}
options ->
options =
options
|> Map.new()
|> adjust_transparency(Image.bands(image), Image.has_alpha?(image))
|> adjust_extend_mode()
{:ok, options}
end
end
def validate_options(_image, _width, _height, %{} = options) do
{:ok, options}
end
defp validate_option({:background_color, :average}, image, _width, _height, options) do
case Image.average(image) do
color when is_list(color) ->
options = Keyword.put(options, :background_color, color)
{:cont, options}
{:error, reason} ->
{:halt, {:error, "Could not get the image average: #{inspect(reason)}"}}
end
end
defp validate_option({:background_color, color} = option, _image, _width, _height, options) do
case Color.validate_color(color) do
{:ok, color} ->
{:cont, Keyword.put(options, :background_color, color)}
_other ->
{:halt, invalid_option(option)}
end
end
defp validate_option(
{:background_transparency, transparency} = option,
_image,
_width,
_height,
options
) do
case Color.validate_transparency(transparency) do
{:ok, transparency} ->
{:cont, Keyword.put(options, :background_transparency, transparency)}
_other ->
{:halt, invalid_option(option)}
end
end
defp validate_option({:extend_mode, extend}, _image, _width, _height, options) do
case Image.ExtendMode.validate_extend(extend) do
{:ok, extend_mode} ->
options = Keyword.put(options, :extend_mode, extend_mode)
{:cont, options}
{:error, reason} ->
{:halt, {:error, reason}}
end
end
defp validate_option({:x, :center}, image, width, height, options) do
x = trunc((width - Image.width(image)) / 2)
validate_option({:x, x}, image, width, height, options)
end
defp validate_option({:y, :center}, image, width, height, options) do
y = trunc((height - Image.height(image)) / 2)
validate_option({:y, y}, image, width, height, options)
end
defp validate_option({:x, x}, image, width, _height, options) when is_integer(x) do
x = normalize_dim(x, width)
if x + width >= Image.width(image) do
options = Keyword.put(options, :x, x)
{:cont, options}
else
{:halt, offset_error(:x, x)}
end
end
defp validate_option({:y, y}, image, _width, height, options) when is_integer(y) do
y = normalize_dim(y, height)
if y + height >= Image.height(image) do
options = Keyword.put(options, :y, y)
{:cont, options}
else
{:halt, offset_error(:y, y)}
end
end
defp validate_option(option, _image, _width, _height, _options) do
{:halt, {:error, invalid_option(option)}}
end
defp invalid_option(option) do
"Invalid option or option value: #{inspect(option)}"
end
defp adjust_transparency(%{extend_mode: :VIPS_EXTEND_BLACK} = options, _bands, true = _has_alpha?) do
options
|> Map.put(:extend_mode, :VIPS_EXTEND_BACKGROUND)
|> Map.put(:background_color, [0, 0, 0, options.background_transparency])
|> Map.delete(:background_transparency)
end
defp adjust_transparency(%{extend_mode: :VIPS_EXTEND_WHITE} = options, _bands, true = _has_alpha?) do
options
|> Map.put(:extend_mode, :VIPS_EXTEND_BACKGROUND)
|> Map.put(:background_color, [255, 255, 255, options.background_transparency])
|> Map.delete(:background_transparency)
end
defp adjust_transparency(
%{extend_mode: :VIPS_EXTEND_BACKGROUND} = options,
bands,
true = _has_alpha?
) do
if length(options.background) == bands do
options
else
options
|> Map.put(
:background_color,
List.insert_at(options.background, -1, options.background_transparency)
)
end
|> Map.delete(:background_transparency)
end
defp adjust_transparency(%{extend_mode: :VIPS_EXTEND_BACKGROUND} = options, 1, _has_alpha?) do
background_color =
options.background
|> hd()
|> List.wrap()
options
|> Map.put(:background_color, background_color)
|> Map.delete(:background_transparency)
end
defp adjust_transparency(options, _bands, _has_alpha?) do
options
end
defp adjust_extend_mode(options) do
if options.background_color != [0, 0, 0] do
Map.put(options, :extend_mode, :VIPS_EXTEND_BACKGROUND)
else
options
end
end
@doc false
def normalize_dim(a, _max) when a >= 0, do: a
def normalize_dim(a, max) when a < 0, do: max + a
defp default_options do
[
x: :center,
y: :center,
extend_mode: :black,
background_color: :black,
background_transparency: :opaque
]
end
defp offset_error(_dim, value) when value < 0 do
{:error, "The canvas image must be larger than the image being embedded in it"}
end
defp offset_error(dim, value) do
{:error,
"#{inspect(dim)} offset #{inspect(value)} cannot fit the image inside the embedded image"}
end
end