diff --git a/c_src/pipe.c b/c_src/pipe.c index 650db6c..b1898fd 100644 --- a/c_src/pipe.c +++ b/c_src/pipe.c @@ -385,11 +385,12 @@ static bool cancel_select(ErlNifEnv *env, int *fd) { static void fd_rt_dtor(ErlNifEnv *env, void *obj) { debug("fd_rt_dtor called"); + int *fd = (int *)obj; + close_fd(fd); } static void fd_rt_stop(ErlNifEnv *env, void *obj, int fd, int is_direct_call) { debug("fd_rt_stop called %d", fd); - close_fd(&fd); } static void fd_rt_down(ErlNifEnv *env, void *obj, ErlNifPid *pid, diff --git a/c_src/vips_foreign.c b/c_src/vips_foreign.c index 3d8659f..a403874 100644 --- a/c_src/vips_foreign.c +++ b/c_src/vips_foreign.c @@ -135,6 +135,72 @@ ERL_NIF_TERM nif_foreign_find_save(ErlNifEnv *env, int argc, return ret; } +ERL_NIF_TERM nif_foreign_find_load_source(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + ASSERT_ARGC(argc, 1); + + ErlNifTime start; + ERL_NIF_TERM ret; + VipsSource *source; + const char *name; + + start = enif_monotonic_time(ERL_NIF_USEC); + + if (!erl_term_to_g_object(env, argv[0], (GObject **)&source)) { + ret = make_error(env, "Failed to get VipsSource"); + goto exit; + } + + name = vips_foreign_find_load_source(source); + + if (!name) { + error("Failed to find the loader for the source. error: %s", + vips_error_buffer()); + vips_error_clear(); + ret = make_error(env, "Failed to find loader for the source"); + goto exit; + } + + ret = make_ok(env, make_binary(env, name)); + +exit: + notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC)); + return ret; +} + +ERL_NIF_TERM nif_foreign_find_save_target(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + ASSERT_ARGC(argc, 1); + + ErlNifTime start; + ERL_NIF_TERM ret; + char suffix[VIPS_PATH_MAX]; + const char *name; + + start = enif_monotonic_time(ERL_NIF_USEC); + + if (!get_binary(env, argv[0], suffix, VIPS_PATH_MAX)) { + ret = make_error(env, "Failed to get suffix"); + goto exit; + } + + name = vips_foreign_find_save_target(suffix); + + if (!name) { + error("Failed to find saver for the target. error: %s", + vips_error_buffer()); + vips_error_clear(); + ret = make_error(env, "Failed to find saver for the target"); + goto exit; + } + + ret = make_ok(env, make_binary(env, name)); + +exit: + notify_consumed_timeslice(env, start, enif_monotonic_time(ERL_NIF_USEC)); + return ret; +} + ERL_NIF_TERM nif_foreign_get_suffixes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { ASSERT_ARGC(argc, 0); diff --git a/c_src/vips_foreign.h b/c_src/vips_foreign.h index 665384a..4f92bd1 100644 --- a/c_src/vips_foreign.h +++ b/c_src/vips_foreign.h @@ -15,6 +15,12 @@ ERL_NIF_TERM nif_foreign_find_load(ErlNifEnv *env, int argc, ERL_NIF_TERM nif_foreign_find_save(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM nif_foreign_find_load_source(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM nif_foreign_find_save_target(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]); + ERL_NIF_TERM nif_foreign_get_suffixes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); diff --git a/c_src/vix.c b/c_src/vix.c index 5b44531..a097db9 100644 --- a/c_src/vix.c +++ b/c_src/vix.c @@ -155,8 +155,13 @@ static ErlNifFunc nif_funcs[] = { /* VipsForeign */ {"nif_foreign_find_load", 1, nif_foreign_find_load, 0}, {"nif_foreign_find_save", 1, nif_foreign_find_save, 0}, - {"nif_foreign_find_load_buffer", 1, nif_foreign_find_load_buffer, 0}, + {"nif_foreign_find_load_buffer", 1, nif_foreign_find_load_buffer, + ERL_NIF_DIRTY_JOB_IO_BOUND}, + // it might read bytes form the file {"nif_foreign_find_save_buffer", 1, nif_foreign_find_save_buffer, 0}, + {"nif_foreign_find_load_source", 1, nif_foreign_find_load_source, + ERL_NIF_DIRTY_JOB_IO_BOUND}, // it might read bytes from source + {"nif_foreign_find_save_target", 1, nif_foreign_find_save_target, 0}, {"nif_foreign_get_suffixes", 0, nif_foreign_get_suffixes, 0}, {"nif_foreign_get_loader_suffixes", 0, nif_foreign_get_loader_suffixes, 0}, diff --git a/lib/vix/nif.ex b/lib/vix/nif.ex index 588e34a..739d342 100644 --- a/lib/vix/nif.ex +++ b/lib/vix/nif.ex @@ -186,6 +186,12 @@ defmodule Vix.Nif do def nif_foreign_find_save(_filename), do: :erlang.nif_error(:nif_library_not_loaded) + def nif_foreign_find_load_source(_source), + do: :erlang.nif_error(:nif_library_not_loaded) + + def nif_foreign_find_save_target(_suffix), + do: :erlang.nif_error(:nif_library_not_loaded) + def nif_foreign_get_suffixes, do: :erlang.nif_error(:nif_library_not_loaded) diff --git a/lib/vix/source_pipe.ex b/lib/vix/source_pipe.ex index 4f1feb5..a03647b 100644 --- a/lib/vix/source_pipe.ex +++ b/lib/vix/source_pipe.ex @@ -14,6 +14,7 @@ defmodule Vix.SourcePipe do defstruct bin: [], client_pid: nil end + @spec new() :: {pid, Vix.Vips.Source.t()} def new do {:ok, pipe} = GenServer.start_link(__MODULE__, nil) source = GenServer.call(pipe, :source, :infinity) @@ -37,7 +38,13 @@ defmodule Vix.SourcePipe do def handle_continue(nil, _) do case Nif.nif_source_new() do {:ok, {fd, source}} -> - {:noreply, %SourcePipe{fd: fd, pending: %Pending{}, source: source}} + source_pipe = %SourcePipe{ + fd: fd, + pending: %Pending{}, + source: %Vix.Vips.Source{ref: source} + } + + {:noreply, source_pipe} {:error, reason} -> {:stop, reason, nil} diff --git a/lib/vix/target_pipe.ex b/lib/vix/target_pipe.ex index fb1fd4a..4029d9d 100644 --- a/lib/vix/target_pipe.ex +++ b/lib/vix/target_pipe.ex @@ -7,17 +7,20 @@ defmodule Vix.TargetPipe do @moduledoc false + @type t() :: struct + defstruct [:fd, :pending, :task_result, :task_pid] defmodule Pending do @moduledoc false - defstruct size: nil, client_pid: nil + defstruct size: nil, client_pid: nil, opts: [] end @default_buffer_size 65_535 - def new(vips_image, suffix) do - GenServer.start_link(__MODULE__, %{image: vips_image, suffix: suffix}) + @spec new(Vix.Vips.Image.t(), String.t(), keyword) :: GenServer.on_start() + def new(image, suffix, opts) do + GenServer.start_link(__MODULE__, %{image: image, suffix: suffix, opts: opts}) end def read(process, max_size \\ @default_buffer_size) @@ -31,15 +34,15 @@ defmodule Vix.TargetPipe do # Server - def init(%{image: image, suffix: suffix}) do + def init(%{image: image, suffix: suffix, opts: opts}) do Process.flag(:trap_exit, true) - {:ok, nil, {:continue, %{image: image, suffix: suffix}}} + {:ok, nil, {:continue, %{image: image, suffix: suffix, opts: opts}}} end - def handle_continue(%{image: image, suffix: suffix}, _) do + def handle_continue(%{image: image, suffix: suffix, opts: opts}, _) do case Nif.nif_target_new() do {:ok, {fd, target}} -> - pid = start_task(image, target, suffix) + pid = start_task(image, %Vix.Vips.Target{ref: target}, suffix, opts) {:noreply, %TargetPipe{fd: fd, task_pid: pid, pending: %Pending{}}} {:error, reason} -> @@ -98,9 +101,21 @@ defmodule Vix.TargetPipe do {:noreply, state} end - defp start_task(image, target, suffix) do + @spec start_task(Vix.Vips.Image.t(), Vix.Vips.Target.t(), String.t(), keyword) :: pid + defp start_task(%Vix.Vips.Image{} = image, target, suffix, []) do spawn_link(fn -> - result = Nif.nif_image_to_target(image, target, suffix) + result = Nif.nif_image_to_target(image.ref, target.ref, suffix) + Process.exit(self(), result) + end) + end + + defp start_task(image, target, suffix, opts) do + spawn_link(fn -> + result = + with {:ok, saver} <- Vix.Vips.Foreign.find_save_target(suffix) do + Vix.Vips.Operation.Helper.operation_call(saver, [image, target], opts) + end + Process.exit(self(), result) end) end diff --git a/lib/vix/vips/foreign.ex b/lib/vix/vips/foreign.ex index 825244c..8a9e773 100644 --- a/lib/vix/vips/foreign.ex +++ b/lib/vix/vips/foreign.ex @@ -1,23 +1,46 @@ defmodule Vix.Vips.Foreign do - alias Vix.Nif @moduledoc false + alias Vix.Nif + + @type operation_name :: String.t() + + @spec find_load_buffer(binary) :: {:ok, operation_name} | {:error, String.t()} def find_load_buffer(bin) do Nif.nif_foreign_find_load_buffer(bin) end + @spec find_save_buffer(String.t()) :: {:ok, operation_name} | {:error, String.t()} def find_save_buffer(suffix) do Nif.nif_foreign_find_save_buffer(suffix) end + @doc """ + Returns Vips operation name which can load the passed file + """ + @spec find_load(String.t()) :: {:ok, operation_name} | {:error, String.t()} def find_load(filename) do Nif.nif_foreign_find_load(filename) end + @doc """ + Returns Vips operation name which can save an image to passed format + """ + @spec find_save(String.t()) :: {:ok, operation_name} | {:error, String.t()} def find_save(filename) do Nif.nif_foreign_find_save(filename) end + @spec find_load_source(Vix.Vips.Source.t()) :: {:ok, operation_name} | {:error, String.t()} + def find_load_source(%Vix.Vips.Source{ref: vips_source}) do + Nif.nif_foreign_find_load_source(vips_source) + end + + @spec find_save_target(String.t()) :: {:ok, operation_name} | {:error, String.t()} + def find_save_target(suffix) do + Nif.nif_foreign_find_save_target(suffix) + end + def get_suffixes do with {:ok, suffixes} <- Nif.nif_foreign_get_suffixes() do {:ok, Enum.uniq(suffixes)} diff --git a/lib/vix/vips/image.ex b/lib/vix/vips/image.ex index f5136f9..fc9aef3 100644 --- a/lib/vix/vips/image.ex +++ b/lib/vix/vips/image.ex @@ -447,6 +447,7 @@ defmodule Vix.Vips.Image do @doc since: "0.31.0" def new_from_file(path, opts) do with {:ok, path} <- normalize_path(path), + :ok <- validate_options(opts), {:ok, loader} <- Vix.Vips.Foreign.find_load(path), {:ok, {ref, _optional}} <- Operation.Helper.operation_call(loader, [path], opts) do {:ok, wrap_type(ref)} @@ -504,7 +505,8 @@ defmodule Vix.Vips.Image do """ @spec new_from_buffer(binary(), keyword()) :: {:ok, t()} | {:error, term()} def new_from_buffer(bin, opts \\ []) do - with {:ok, loader} <- Vix.Vips.Foreign.find_load_buffer(bin), + with :ok <- validate_options(opts), + {:ok, loader} <- Vix.Vips.Foreign.find_load_buffer(bin), {:ok, {ref, _optional}} <- Operation.Helper.operation_call(loader, [bin], opts) do {:ok, wrap_type(ref)} end @@ -580,26 +582,18 @@ defmodule Vix.Vips.Image do :ok = Image.write_to_file(image, "puppies.png") ``` - Optional param `opts` string is passed to the image loader. It is a string - of the format "[name=value,...]". + Optional param `opts` is passed to the image loader. Available + options depends on the format. ```elixir - Image.new_from_enum(stream, "[shrink=2]") - ``` - - Will read the stream with downsampling by a factor of two. - - The full set of options available depend upon the image format. You - can find all options available at the command-line. To see a summary - of the available options for the JPEG loader: - - ```shell - $ vips jpegload_source + Image.new_from_enum(stream, [shrink: 2]) ``` + You can find all options available for a format under operation function in + [Operation](./search.html?q=load+-buffer+-filename+-profile) module. """ - @spec new_from_enum(Enumerable.t(), String.t()) :: {:ok, t()} | {:error, term()} - def new_from_enum(enum, opts \\ "") do + @spec new_from_enum(Enumerable.t(), String.t() | keyword) :: {:ok, t()} | {:error, term()} + def new_from_enum(enum, opts \\ []) do parent = self() pid = @@ -625,9 +619,17 @@ defmodule Vix.Vips.Image do end) receive do - {^pid, source} -> - Nif.nif_image_new_from_source(source, opts) + # for backward compatibility + {^pid, source} when is_binary(opts) -> + Nif.nif_image_new_from_source(source.ref, opts) |> wrap_type() + + {^pid, source} -> + with :ok <- validate_options(opts), + {:ok, loader} <- Vix.Vips.Foreign.find_load_source(source), + {:ok, {ref, _optional}} <- Operation.Helper.operation_call(loader, [source], opts) do + {:ok, wrap_type(ref)} + end end end @@ -649,29 +651,22 @@ defmodule Vix.Vips.Image do |> Stream.run() ``` - Second param `suffix` determines the format of the output - stream. Save options may be appended to the suffix as - "[name=value,...]". + Optional param `opts` is passed to the image saver. Available + options depends on the file format. You can find all options + available for a format under operation function in + [Operation](./search.html?q=save+buffer+-filename+-profile) module. ```elixir - Image.write_to_stream(vips_image, ".jpg[Q=90]") + Image.write_to_stream(vips_image, ".jpg", Q: 90) ``` - - Options are specific to save operation. You can find out all - available options for the save operation at command line. For - example: - - ```shell - $ vips jpegsave_target - ``` - """ - @spec write_to_stream(t(), String.t()) :: Enumerable.t() - def write_to_stream(%Image{ref: vips_image}, suffix) do + @spec write_to_stream(t(), String.t(), keyword) :: Enumerable.t() + + @doc since: "0.32.0" + def write_to_stream(%Image{ref: _} = image, suffix, opts) do Stream.resource( fn -> - {:ok, pipe} = Vix.TargetPipe.new(vips_image, suffix) - pipe + init_write_stream(image, suffix, opts) end, fn pipe -> ret = Vix.TargetPipe.read(pipe) @@ -693,6 +688,10 @@ defmodule Vix.Vips.Image do ) end + def write_to_stream(%Image{ref: _} = image, suffix) do + write_to_stream(%Image{ref: _} = image, suffix, []) + end + @doc """ Converts an Image to a nested list. @@ -804,12 +803,9 @@ defmodule Vix.Vips.Image do def write_to_file(%Image{ref: _} = image, path, opts) do path = normalize_string(Path.expand(path)) - case Vix.Vips.Foreign.find_save(path) do - {:ok, saver} -> - Operation.Helper.operation_call(saver, [image, path], opts) - - error -> - error + with :ok <- validate_options(opts), + {:ok, saver} <- Vix.Vips.Foreign.find_save(path) do + Operation.Helper.operation_call(saver, [image, path], opts) end end @@ -834,7 +830,7 @@ defmodule Vix.Vips.Image do ```elixir # returns image in JPEG format as binary with Q factor set 90 - Image.write_to_buffer(img, ".jpg", [Q: 90]) + Image.write_to_buffer(img, ".jpg", Q: 90) ``` If you want more control over the saver, Use specific format saver @@ -845,12 +841,9 @@ defmodule Vix.Vips.Image do @doc since: "0.32.0" def write_to_buffer(%Image{ref: _} = image, suffix, opts) do - case Vix.Vips.Foreign.find_save_buffer(normalize_string(suffix)) do - {:ok, saver} -> - Operation.Helper.operation_call(saver, [image], opts) - - error -> - error + with :ok <- validate_options(opts), + {:ok, saver} <- Vix.Vips.Foreign.find_save_buffer(normalize_string(suffix)) do + Operation.Helper.operation_call(saver, [image], opts) end end @@ -1650,4 +1643,27 @@ defmodule Vix.Vips.Image do true -> raise ArgumentError, "integer size must be <= 32bit" end end + + @spec validate_options(keyword) :: :ok | {:error, String.t()} + defp validate_options(opts) do + if Keyword.keyword?(opts) do + :ok + else + {:error, "Opts must be a keyword list"} + end + end + + @spec init_write_stream(Image.t(), String.t(), keyword) :: term | no_return + defp init_write_stream(image, suffix, opts) do + with :ok <- validate_options(opts), + {:ok, pipe} <- Vix.TargetPipe.new(image, suffix, opts) do + pipe + else + {:error, reason} when is_binary(reason) -> + raise Error, reason + + {:error, reason} -> + raise Error, inspect(reason) + end + end end diff --git a/lib/vix/vips/source.ex b/lib/vix/vips/source.ex index 394c3f2..5468c09 100644 --- a/lib/vix/vips/source.ex +++ b/lib/vix/vips/source.ex @@ -2,9 +2,13 @@ defmodule Vix.Vips.Source do @moduledoc false alias Vix.Type + alias __MODULE__ @behaviour Type - @opaque t() :: reference() + + @type t() :: %Source{ref: reference} + + defstruct [:ref] @impl Type def typespec do @@ -17,8 +21,16 @@ defmodule Vix.Vips.Source do def default(nil), do: :unsupported @impl Type - def to_nif_term(_value, _data), do: raise("VipsSource is not implemented yet") + def to_nif_term(source, _data) do + case source do + %Source{ref: ref} -> + ref + + value -> + raise ArgumentError, message: "expected Vix.Vips.Source given: #{inspect(value)}" + end + end @impl Type - def to_erl_term(_value), do: raise("VipsSource is not implemented yet") + def to_erl_term(ref), do: %Source{ref: ref} end diff --git a/lib/vix/vips/target.ex b/lib/vix/vips/target.ex index ec7c644..7690255 100644 --- a/lib/vix/vips/target.ex +++ b/lib/vix/vips/target.ex @@ -2,9 +2,13 @@ defmodule Vix.Vips.Target do @moduledoc false alias Vix.Type + alias __MODULE__ @behaviour Type - @opaque t() :: reference() + + @type t() :: %Target{ref: reference()} + + defstruct [:ref] @impl Type def typespec do @@ -17,8 +21,16 @@ defmodule Vix.Vips.Target do def default(nil), do: :unsupported @impl Type - def to_nif_term(_value, _data), do: raise("VipsTarget is not implemented yet") + def to_nif_term(target, _data) do + case target do + %Target{ref: ref} -> + ref + + value -> + raise ArgumentError, message: "expected Vix.Vips.Source given: #{inspect(value)}" + end + end @impl Type - def to_erl_term(_value), do: raise("VipsTarget is not implemented yet") + def to_erl_term(ref), do: %Target{ref: ref} end diff --git a/mix.exs b/mix.exs index a8e7864..2d8c18a 100644 --- a/mix.exs +++ b/mix.exs @@ -131,7 +131,7 @@ defmodule Vix.MixProject do {:dialyxir, "~> 1.4", only: [:dev], runtime: false}, {:ex_doc, ">= 0.0.0", only: :dev}, {:excoveralls, "~> 0.15", only: :test}, - {:temp, "~> 0.4", only: :test, runtime: false} + {:briefly, "~> 0.5.0", only: :test} ] end diff --git a/mix.lock b/mix.lock index 36d65f3..e3807f7 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,5 @@ %{ + "briefly": {:hex, :briefly, "0.5.1", "ee10d48da7f79ed2aebdc3e536d5f9a0c3e36ff76c0ad0d4254653a152b13a8a", [:mix], [], "hexpm", "bd684aa92ad8b7b4e0d92c31200993c4bc1469fc68cd6d5f15144041bd15cb57"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, @@ -18,5 +19,4 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, - "temp": {:hex, :temp, "0.4.9", "eb6355bfa7925a568b3d9eb3bb57e89aa6d2b78bfe8dfb6b698e090631b7f41f", [:mix], [], "hexpm", "bc8bf7b27d9105bef933ef4bf4ba37ac6b899dbeba329deaa88c60b62d6b4b6d"}, } diff --git a/test/vix/foreign_test.exs b/test/vix/foreign_test.exs index b08165c..72bb1ef 100644 --- a/test/vix/foreign_test.exs +++ b/test/vix/foreign_test.exs @@ -20,4 +20,18 @@ defmodule Vix.Vips.ForeignTest do test "find_save" do assert {:ok, "VipsForeignSaveJpegFile"} = Foreign.find_save("puppies.jpg") end + + test "find_load_source" do + bin = File.read!(img_path("puppies.jpg")) + + assert {pipe, source} = Vix.SourcePipe.new() + assert :ok = Vix.SourcePipe.write(pipe, bin) + assert :ok = Vix.SourcePipe.stop(pipe) + + assert {:ok, "VipsForeignLoadJpegSource"} = Foreign.find_load_source(source) + end + + test "find_save_target" do + assert {:ok, "VipsForeignSaveJpegTarget"} = Foreign.find_save_target(".jpg") + end end diff --git a/test/vix/vips/image_test.exs b/test/vix/vips/image_test.exs index 8f22fdd..dd6e2a3 100644 --- a/test/vix/vips/image_test.exs +++ b/test/vix/vips/image_test.exs @@ -8,12 +8,6 @@ defmodule Vix.Vips.ImageTest do doctest Image - setup do - Temp.track!() - dir = Temp.mkdir!() - {:ok, %{dir: dir}} - end - test "new_from_file" do assert {:error, :invalid_path} == Image.new_from_file("invalid.jpg", []) assert {:error, "Failed to find load"} == Image.new_from_file(__ENV__.file, []) @@ -56,56 +50,56 @@ defmodule Vix.Vips.ImageTest do assert is_reference(ref) end - test "new_from_buffer", %{dir: dir} do + test "new_from_buffer" do assert {:error, "Failed to find load buffer"} == Image.new_from_buffer(<<>>) path = img_path("puppies.jpg") assert {:ok, img_from_buf} = Image.new_from_buffer(File.read!(path)) - img_buf_out_path = Temp.path!(suffix: ".png", basedir: dir) + img_buf_out_path = Briefly.create!(extname: ".png") assert :ok == Image.write_to_file(img_from_buf, img_buf_out_path) {:ok, img_from_file} = Image.new_from_file(path) - img_file_out_path = Temp.path!(suffix: ".png", basedir: dir) + img_file_out_path = Briefly.create!(extname: ".png") assert :ok == Image.write_to_file(img_from_file, img_file_out_path) assert File.read!(img_buf_out_path) == File.read!(img_file_out_path) end describe "write_to_file" do - test "write_to_file", %{dir: dir} do + test "write_to_file" do path = img_path("puppies.jpg") {:ok, %Image{ref: ref} = im} = Image.new_from_file(path) assert is_reference(ref) - out_path = Temp.path!(suffix: ".png", basedir: dir) + out_path = Briefly.create!(extname: ".png") assert :ok == Image.write_to_file(im, out_path) stat = File.stat!(out_path) assert stat.size > 0 and stat.type == :regular end - test "write_to_file supports optional arguments", %{dir: dir} do + test "write_to_file supports optional arguments" do {:ok, img} = Image.new_from_file(img_path("puppies.jpg")) - out_path1 = Temp.path!(suffix: ".png", basedir: dir) + out_path1 = Briefly.create!(extname: ".png") assert :ok = Image.write_to_file(img, out_path1, compression: 0) - out_path2 = Temp.path!(suffix: ".png", basedir: dir) + out_path2 = Briefly.create!(extname: ".png") assert :ok = Image.write_to_file(img, out_path2, compression: 9) # currently I only found this option to be verifiable easily! assert File.stat!(out_path1).size > File.stat!(out_path2).size end - test "write_to_file supports optional options suffix", %{dir: dir} do + test "write_to_file supports optional options suffix" do {:ok, img} = Image.new_from_file(img_path("puppies.jpg")) - out_path1 = Temp.path!(suffix: ".png", basedir: dir) + out_path1 = Briefly.create!(extname: ".png") assert :ok = Image.write_to_file(img, out_path1 <> "[compression=0]") - out_path2 = Temp.path!(suffix: ".png", basedir: dir) + out_path2 = Briefly.create!(extname: ".png") assert :ok = Image.write_to_file(img, out_path2 <> "[compression=9]") # currently I only found this option to be verifiable easily! @@ -113,7 +107,7 @@ defmodule Vix.Vips.ImageTest do end end - test "new_matrix_from_array", %{dir: _dir} do + test "new_matrix_from_array" do assert {:ok, img} = Image.new_matrix_from_array(2, 2, [ [-1, -1], @@ -130,7 +124,7 @@ defmodule Vix.Vips.ImageTest do end describe "new_from_list" do - test "when argument is range", %{dir: _dir} do + test "when argument is range" do assert {:ok, img} = Image.new_from_list(0..2) assert %{width: 3, height: 1} = Image.headers(img) @@ -139,7 +133,7 @@ defmodule Vix.Vips.ImageTest do Image.write_to_binary(img) end - test "when argument is range within list", %{dir: _dir} do + test "when argument is range within list" do assert {:ok, img} = Image.new_from_list([0..1, -1..0]) assert %{width: 2, height: 2} = Image.headers(img) @@ -151,7 +145,7 @@ defmodule Vix.Vips.ImageTest do >>} = Image.write_to_binary(img) end - test "when list is 1D", %{dir: _dir} do + test "when list is 1D" do assert {:ok, img} = Image.new_from_list([0, 1, 2]) assert %{width: 3, height: 1} = Image.headers(img) @@ -160,7 +154,7 @@ defmodule Vix.Vips.ImageTest do Image.write_to_binary(img) end - test "when list is 2D", %{dir: _dir} do + test "when list is 2D" do assert {:ok, img} = Image.new_from_list([ [1, 2, 3], @@ -177,7 +171,7 @@ defmodule Vix.Vips.ImageTest do end end - test "mutate", %{dir: _dir} do + test "mutate" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) {:ok, updated_image} = @@ -190,7 +184,7 @@ defmodule Vix.Vips.ImageTest do assert {:ok, 0} = Image.header_value(updated_image, "new-field") end - test "get_fields", %{dir: _dir} do + test "get_fields" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, fields} = Image.header_field_names(im) @@ -210,12 +204,12 @@ defmodule Vix.Vips.ImageTest do ) end - test "get_header", %{dir: _dir} do + test "get_header" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, 518} = Image.header_value(im, "width") end - test "headers", %{dir: _dir} do + test "headers" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert %{ @@ -239,19 +233,19 @@ defmodule Vix.Vips.ImageTest do } = Image.headers(im) end - test "get_header binary", %{dir: _dir} do + test "get_header binary" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, <<_::binary>>} = Image.header_value(im, "exif-data") end - test "get_as_string", %{dir: _dir} do + test "get_as_string" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, "((VipsInterpretation) VIPS_INTERPRETATION_sRGB)"} = Image.header_value_as_string(im, "interpretation") end - test "macro generated function", %{dir: _dir} do + test "macro generated function" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert 518 == Image.width(im) assert :VIPS_INTERPRETATION_sRGB == Image.interpretation(im) @@ -261,7 +255,7 @@ defmodule Vix.Vips.ImageTest do end describe "write_to_buffer" do - test "write image to buffer", %{dir: _dir} do + test "write image to buffer" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) {:ok, _bin} = Image.write_to_buffer(im, ".jpg[Q=90]") end @@ -287,7 +281,7 @@ defmodule Vix.Vips.ImageTest do end end - test "new image from other image", %{dir: _dir} do + test "new image from other image" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) {:ok, new_im} = Image.new_from_image(im, [250]) @@ -296,7 +290,7 @@ defmodule Vix.Vips.ImageTest do assert Image.bands(new_im) == 1 end - test "image has an alpha band", %{dir: _dir} do + test "image has an alpha band" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) refute Image.has_alpha?(im) @@ -304,57 +298,105 @@ defmodule Vix.Vips.ImageTest do assert Image.has_alpha?(im) end - test "new_from_enum", %{dir: dir} do - {:ok, image} = - File.stream!(img_path("puppies.jpg"), [], 1024) - |> Image.new_from_enum("") + describe "new_from_enum" do + test "new_from_enum" do + {:ok, image} = + File.stream!(img_path("puppies.jpg"), [], 1024) + |> Image.new_from_enum("") - out_path = Temp.path!(suffix: ".png", basedir: dir) - :ok = Image.write_to_file(image, out_path) + out_path = Briefly.create!(extname: ".png") + :ok = Image.write_to_file(image, out_path) - stat = File.stat!(out_path) - assert stat.size > 0 and stat.type == :regular - end + stat = File.stat!(out_path) + assert stat.size > 0 and stat.type == :regular + end - @tag capture_log: true - test "new_from_enum invalid data write" do - {:error, "Failed to create image from VipsSource"} = Image.new_from_enum(1..100) - end + @tag capture_log: true + test "new_from_enum invalid data write" do + {:error, "Failed to find loader for the source"} = Image.new_from_enum(1..100) + end - test "premature end of new_from_enum" do - {:error, "Failed to create image from VipsSource"} = - File.stream!(img_path("puppies.jpg"), [], 100) - |> Stream.take(1) - |> Image.new_from_enum("") - end + test "premature end of new_from_enum" do + {:error, "Failed to create image from VipsSource"} = + File.stream!(img_path("puppies.jpg"), [], 100) + |> Stream.take(1) + |> Image.new_from_enum("") + end - test "write_to_stream", %{dir: dir} do - {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + test "passing options as keyword" do + {:ok, img1} = Image.new_from_file(img_path("puppies.jpg")) - out_path = Temp.path!(suffix: ".png", basedir: dir) + {:ok, img2} = + File.stream!(img_path("puppies.jpg"), [], 1024) + |> Image.new_from_enum(shrink: 2) - :ok = - Image.write_to_stream(im, ".png") - |> Stream.into(File.stream!(out_path)) - |> Stream.run() + assert Image.width(img1) == 2 * Image.width(img2) + end - stat = File.stat!(out_path) - assert stat.size > 0 and stat.type == :regular + test "passing options as string" do + {:ok, img1} = Image.new_from_file(img_path("puppies.jpg")) + + {:ok, img2} = + File.stream!(img_path("puppies.jpg"), [], 1024) + |> Image.new_from_enum("[shrink=2]") + + assert Image.width(img1) == 2 * Image.width(img2) + end end - test "write_to_stream with invalid suffix", %{dir: dir} do - {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + describe "write_to_stream" do + test "write_to_stream" do + {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + + out_path = Briefly.create!(extname: ".png") + + :ok = + Image.write_to_stream(im, ".png") + |> Stream.into(File.stream!(out_path)) + |> Stream.run() + + stat = File.stat!(out_path) + assert stat.size > 0 and stat.type == :regular + end + + test "write_to_stream with invalid suffix" do + {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + + out_path = Briefly.create!(extname: ".png") + + assert_raise Vix.Vips.Image.Error, fn -> + Image.write_to_stream(im, ".invalid") + |> Stream.into(File.stream!(out_path)) + |> Stream.run() + end + end + + test "passing options as keyword" do + {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + + buf1 = + Image.write_to_stream(im, ".png", compression: 0) + |> Enum.into([]) + + {:ok, buf2} = Image.write_to_buffer(im, ".png", compression: 9) + + assert IO.iodata_length(buf1) > byte_size(buf2) + end + + test "passing options as string" do + {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) + + buf1 = + Image.write_to_stream(im, ".png[compression=0]") + |> Enum.into([]) - out_path = Temp.path!(suffix: ".png", basedir: dir) + {:ok, buf2} = Image.write_to_buffer(im, ".png", compression: 9) - assert_raise Vix.Vips.Image.Error, fn -> - Image.write_to_stream(im, ".invalid") - |> Stream.into(File.stream!(out_path)) - |> Stream.run() + assert IO.iodata_length(buf1) > byte_size(buf2) end end - test "new_from_binary", %{dir: dir} do + test "new_from_binary" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) # same image in raw pixel format bin = File.read!(img_path("puppies.raw")) @@ -368,7 +410,7 @@ defmodule Vix.Vips.ImageTest do Image.format(im) ) - out_path = Temp.path!(suffix: ".png", basedir: dir) + out_path = Briefly.create!(extname: ".png") :ok = Image.write_to_file(image, out_path) stat = File.stat!(out_path) @@ -421,7 +463,7 @@ defmodule Vix.Vips.ImageTest do end end - test "new_from_binary and write_to_binary endianness handling", %{dir: dir} do + test "new_from_binary and write_to_binary endianness handling" do {width, height} = {125, 125} # generate a test pixel data in native endianness @@ -438,7 +480,7 @@ defmodule Vix.Vips.ImageTest do {:ok, expected} = Image.new_from_file(img_path("gradient.png")) assert_images_equal(expected, img) - out_path = Temp.path!(suffix: ".v", basedir: dir) + out_path = Briefly.create!(extname: ".v") :ok = Image.write_to_file(img, out_path) {:ok, vimg} = Image.new_from_file(out_path) diff --git a/test/vix/vips/operation/helper_test.exs b/test/vix/vips/operation/helper_test.exs index 232afd0..b67f54a 100644 --- a/test/vix/vips/operation/helper_test.exs +++ b/test/vix/vips/operation/helper_test.exs @@ -6,13 +6,7 @@ defmodule Vix.Vips.Operation.HelperTest do import Vix.Support.Images - setup do - Temp.track!() - dir = Temp.mkdir!() - {:ok, %{dir: dir}} - end - - test "operation_call", %{dir: dir} do + test "operation_call" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, out} = @@ -22,7 +16,7 @@ defmodule Vix.Vips.Operation.HelperTest do extend: :VIPS_EXTEND_COPY ) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("gravity_puppies.jpg"), out_path) diff --git a/test/vix/vips/operation_test.exs b/test/vix/vips/operation_test.exs index 13d4295..b533137 100644 --- a/test/vix/vips/operation_test.exs +++ b/test/vix/vips/operation_test.exs @@ -6,33 +6,27 @@ defmodule Vix.Vips.OperationTest do import Vix.Support.Images - setup do - Temp.track!() - dir = Temp.mkdir!() - {:ok, %{dir: dir}} - end - - test "invert", %{dir: dir} do + test "invert" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, out} = Operation.invert(im) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("invert_puppies.jpg"), out_path) end - test "affine", %{dir: dir} do + test "affine" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, out} = Operation.affine(im, [1, 0, 0, 0.5]) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("affine_puppies.jpg"), out_path) end - test "gravity", %{dir: dir} do + test "gravity" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) assert {:ok, out} = @@ -40,25 +34,25 @@ defmodule Vix.Vips.OperationTest do extend: :VIPS_EXTEND_COPY ) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("gravity_puppies.jpg"), out_path) end - test "conv with simple edge detection kernel", %{dir: dir} do + test "conv with simple edge detection kernel" do {:ok, im} = Image.new_from_file(img_path("puppies.jpg")) {:ok, mask} = Image.new_matrix_from_array(3, 3, [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]) assert {:ok, out} = Operation.conv(im, mask, precision: :VIPS_PRECISION_FLOAT) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("conv_puppies.jpg"), out_path) end - test "additional return values", %{dir: _dir} do + test "additional return values" do {:ok, im} = Image.new_from_file(img_path("black_on_white.jpg")) assert {:ok, {min, %{x: _, y: _, "out-array": [min], "x-array": [_ | _], "y-array": [_ | _]}}} = @@ -67,17 +61,17 @@ defmodule Vix.Vips.OperationTest do assert min in [-0.0, +0.0] end - test "required output order", %{dir: _dir} do + test "required output order" do {:ok, im} = Image.new_from_file(img_path("black_on_white.jpg")) assert Operation.find_trim(im) == {:ok, {41, 44, 45, 45}} end - test "when unsupported argument is passed", %{dir: _dir} do + test "when unsupported argument is passed" do buf = File.read!(img_path("alpha_band.png")) assert {:ok, {%Image{}, _}} = Operation.pngload_buffer(buf, foo: "bar") end - test "operation error", %{dir: _dir} do + test "operation error" do {:ok, im} = Image.new_from_file(img_path("black_on_white.jpg")) assert Operation.affine(im, [1, 1, 1, 1]) == @@ -85,17 +79,17 @@ defmodule Vix.Vips.OperationTest do "operation build: vips__transform_calc_inverse: singular or near-singular matrix"} end - test "image type mismatch error", %{dir: _dir} do + test "image type mismatch error" do assert_raise ArgumentError, "expected Vix.Vips.Image. given: :invalid", fn -> Operation.invert(:invalid) end end - test "enum parameter", %{dir: dir} do + test "enum parameter" do {:ok, im} = Image.new_from_file(img_path("black_on_white.jpg")) {:ok, out} = Operation.flip(im, :VIPS_DIRECTION_HORIZONTAL) - out_path = Temp.path!(suffix: ".jpg", basedir: dir) + out_path = Briefly.create!(extname: ".jpg") :ok = Image.write_to_file(out, out_path) assert_files_equal(img_path("black_on_white_hflip.jpg"), out_path)