defmodule Image.Options.New do @moduledoc """ Options for new images. """ alias Image.{Color, BandFormat, Interpretation} @type t :: [ {:bands, pos_integer()} | {:format, Image.BandFormat.t()} | {:interpretation, Image.Interpretation.t()} | {:color, float() | Image.pixel()} | {:x_res, number()} | {:y_res, number()} | {:x_offset, number()} | {:y_offset, number()} ] @default_bands 3 def default_options do [ format: {:u, 8}, interpretation: :srgb, color: 0, x_res: 0, y_res: 0, x_offset: 0, y_offset: 0 ] end @doc """ Validate the options for `Image.new/2`. """ def validate_options(options) do options = Keyword.merge(default_options(), options) options = case Enum.reduce_while(options, options, &validate_option(&1, &2)) do {:error, value} -> {:error, value} options -> {:ok, options} end case options do {:ok, options} -> options |> Map.new() |> set_default_bands() |> wrap(:ok) other -> other end end defp validate_option({:format, format}, options) when is_tuple(format) do case BandFormat.image_format_from_nx(format) do {:ok, format} -> {:cont, Keyword.put(options, :format, format)} {:error, reason} -> {:halt, {:error, reason}} end end defp validate_option({:format, format}, options) when is_atom(format) do case BandFormat.nx_format(format) do {:ok, _nx_type} -> {:cont, options} {:error, reason} -> {:halt, {:error, reason}} end end defp validate_option({:interpretation, interpretation}, options) do case Interpretation.validate_interpretation(interpretation) do {:ok, interpretation} -> {:cont, Keyword.put(options, :interpretation, interpretation)} {:error, reason} -> {:halt, {:error, reason}} end end @numeric_options [:x_res, :y_res, :x_offset, :y_offset, :bands] defp validate_option({option, value}, options) when option in @numeric_options and is_number(value) and value >= 0 do {:cont, options} end defp validate_option({:color, color}, options) do case Color.rgb_color(color) do {:ok, color} -> rgb = if Keyword.keyword?(color), do: Keyword.fetch!(color, :rgb), else: color {:cont, Keyword.put(options, :color, rgb)} {:error, reason} -> {:halt, {:error, reason}} end end defp validate_option({option, value}, _options) do {:halt, {:error, invalid_option(option, value)}} end def set_default_bands(%{bands: _bands} = options) do options end def set_default_bands(%{color: color} = options) when is_integer(color) do Map.put(options, :bands, @default_bands) end def set_default_bands(%{color: color} = options) when is_list(color) do Map.put(options, :bands, length(color)) end @doc false def invalid_option(option) do "Invalid option or option value: #{inspect(option)}" end @doc false def invalid_option(option, value) do "Invalid option or option value: #{option}: #{inspect(value)}" end defp wrap(term, atom) do {atom, term} end end