Skip to content
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

* **Breaking:** `Image.drop_shadow/2` returns `{:error, %Image.Error{}}` for a non-numeric `:sigma` instead of raising `ArithmeticError`, and its `:opacity` and `:sigma` errors now carry `reason: :invalid_option` with `value` set to `{:opacity, value}` or `{:sigma, value}`. Validation still covers only `:opacity` and `:sigma`. `:dx`, `:dy` and unknown options remain unvalidated pending a move to an `Image.Options.DropShadow` module. ([#221](https://github.com/elixir-image/image/pull/221))

* **Breaking:** `Image.chroma_mask/2`, `Image.chroma_key/2`, `Image.replace_color/2` and their `!` variants now return `{:error, %Image.Error{reason: :invalid_option}}` when options from both masking strategies are supplied, or when only one of `:greater_than` and `:less_than` is supplied. The two strategies were always documented as mutually exclusive, but nothing enforced it: the color range strategy won and `:color` and `:threshold` were silently discarded, and a lone bound was discarded and replaced by auto chroma detection with the default threshold. ([#224](https://github.com/elixir-image/image/pull/224))

* `Image.affine/3` and `Image.rotate/3` now premultiply alpha explicitly only when the background is non-opaque, since libvips handles the other cases itself. `Image.shear/4` and `Image.translate/4` inherit this. ([#217](https://github.com/elixir-image/image/pull/217))

### Fixed
Expand All @@ -40,6 +42,8 @@

* Fix the specs for `Image.Math.cos/1` and `Image.Math.sin/1`, which omitted `{:error, Image.error()}`, and for eight math operators, which omitted the `Vimage.t()` they already accepted. All `@dialyzer` opt-outs and the `.dialyzer_ignore_warnings` file are removed. ([#221](https://github.com/elixir-image/image/pull/221))

* Fix the `:greater_than` and `:less_than` documentation for `Image.chroma_mask/2`, `Image.chroma_key/2` and `Image.replace_color/2`, which described the bounds the wrong way round in all six places they appeared. The mask covers the range between the two, so `:greater_than` is the lower bound and `:less_than` the upper. ([#224](https://github.com/elixir-image/image/pull/224))

### Removed

* **Breaking:** Removes `Image.Options.WarpPerspective`, replaced by `Image.Options.Mapim`. ([#216](https://github.com/elixir-image/image/pull/216))
Expand All @@ -48,6 +52,8 @@

* **Breaking:** Removes the `Image.Error` tuple constructors. `raise Image.Error, {:enoent, path}` and `raise Image.Error, {message, path}` now fall through to the catch-all clause. ([#218](https://github.com/elixir-image/image/pull/218))

* **Breaking:** Removes the `:sigma` and `:min_amplitude` options from `Image.chroma_mask/2`, `Image.chroma_key/2` and `Image.replace_color/2`. They were accepted and validated, but never read by any code path. ([#224](https://github.com/elixir-image/image/pull/224))

## Image 0.72.0

This is the changelog for Image version 0.72.0 released on July 22nd, 2026. For older changelogs please consult the release tag on [GitHub](https://github.com/elixir-image/image/tags)
Expand Down
108 changes: 80 additions & 28 deletions lib/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2328,11 +2328,16 @@ defmodule Image do

There are two masking strategies available: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand All @@ -2346,13 +2351,16 @@ defmodule Image do

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down Expand Up @@ -2382,7 +2390,7 @@ defmodule Image do
end
end

defp do_chroma_mask(%Vimage{} = image, %{color: color, threshold: threshold}) do
defp do_chroma_mask(%Vimage{} = image, %{strategy: :threshold, color: color, threshold: threshold}) do
alias Image.Math

# The mask is computed from the color bands only so any alpha
Expand All @@ -2401,7 +2409,11 @@ defmodule Image do
end
end

defp do_chroma_mask(%Vimage{} = image, %{greater_than: greater_than, less_than: less_than}) do
defp do_chroma_mask(%Vimage{} = image, %{
strategy: :range,
greater_than: greater_than,
less_than: less_than
}) do
alias Image.Math

with {:ok, greater} <- Math.greater_than(image, greater_than),
Expand Down Expand Up @@ -2433,11 +2445,16 @@ defmodule Image do

There are two masking strategies available: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand All @@ -2451,13 +2468,16 @@ defmodule Image do

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down Expand Up @@ -2504,11 +2524,16 @@ defmodule Image do

There are two masking strategies available: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand All @@ -2522,13 +2547,16 @@ defmodule Image do

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down Expand Up @@ -2579,11 +2607,16 @@ defmodule Image do

There are two masking strategies available: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand All @@ -2597,13 +2630,16 @@ defmodule Image do

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be masked. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down Expand Up @@ -5861,31 +5897,39 @@ defmodule Image do
There are two strategies available for selecting the
color or color range to be replaced: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name. The default is
`:auto` in which the average of the top left `10x10`
pixels of the image is used.

* `:threshold`is a positive integer to indicate the
* `:threshold` is a positive integer to indicate the
threshold around `:color` when calculating the mask.
The default is `20`.

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down Expand Up @@ -5951,31 +5995,39 @@ defmodule Image do
There are two strategies available for selecting the
color or color range to be replaced: the
thresholding strategy (default) and the color
range strategy.
range strategy. They are mutually exclusive:
combining options from both strategies, or
supplying only one of `:greater_than` and
`:less_than`, returns an error.

#### Threshold strategy

* `:color` is an RGB color which represents the the
Masks colors within a threshold of a single color.

* `:color` is an RGB color which represents the
chroma key to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name. The default is
`:auto` in which the average of the top left `10x10`
pixels of the image is used.

* `:threshold`is a positive integer to indicate the
* `:threshold` is a positive integer to indicate the
threshold around `:color` when calculating the mask.
The default is `20`.

#### Color range strategy

* `:greater_than` is an RGB color which represents the upper
Masks colors falling between two bounds. Both options
are required.

* `:greater_than` is an RGB color which represents the lower
end of the color range to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
representing a CSS color name.

* `:less_than` is an RGB color which represents the lower
* `:less_than` is an RGB color which represents the upper
end of the color range to be selected. The color can be an
integer between `0..255`, a three-element list of
integers representing an RGB color or an atom
Expand Down
Loading
Loading