Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add contain transform #19

Merged
merged 2 commits into from
Dec 2, 2024
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
2 changes: 2 additions & 0 deletions lib/image_plug/param_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule ImagePlug.ParamParser do
Transform.Crop
| Transform.Focus
| Transform.Scale
| Transform.Contain
| Transform.Output

@typedoc """
Expand All @@ -15,6 +16,7 @@ defmodule ImagePlug.ParamParser do
{Transform.Crop, Transform.Crop.Parameters.t()}
| {Transform.Focus, Transform.Focus.Parameters.t()}
| {Transform.Scale, Transform.Scale.Parameters.t()}
| {Transform.Contain, Transform.Contain.Parameters.t()}
| {Transform.Output, Transform.Output.Parameters.t()}

@type transform_chain() :: list(transform_chain_item())
Expand Down
2 changes: 2 additions & 0 deletions lib/image_plug/param_parser/twicpics.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ defmodule ImagePlug.ParamParser.Twicpics do
"crop" => ImagePlug.Transform.Crop,
"resize" => ImagePlug.Transform.Scale,
"focus" => ImagePlug.Transform.Focus,
"contain" => ImagePlug.Transform.Contain,
"output" => ImagePlug.Transform.Output
}

@parsers %{
ImagePlug.Transform.Crop => Twicpics.CropParser,
ImagePlug.Transform.Scale => Twicpics.ScaleParser,
ImagePlug.Transform.Focus => Twicpics.FocusParser,
ImagePlug.Transform.Contain => Twicpics.ContainParser,
ImagePlug.Transform.Output => Twicpics.OutputParser
}

Expand Down
40 changes: 40 additions & 0 deletions lib/image_plug/param_parser/twicpics/contain_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule ImagePlug.ParamParser.Twicpics.ContainParser do
import ImagePlug.ParamParser.Twicpics.Common

alias ImagePlug.Transform.Contain.ContainParams

@doc """
Parses a string into a `ImagePlug.Transform.Contain.ContainParams` struct.

Returns a `ImagePlug.Transform.Contain.ContainParams` struct.

## Format

```
<width>x<height>
```

## Units

Type | Format
--------- | ------------
`pixel` | `<int>`

## Examples

iex> ImagePlug.ParamParser.Twicpics.ContainParser.parse("250x25.5")
{:ok, %ImagePlug.Transform.Contain.ContainParams{width: {:int, 250}, height: {:float, 25.5}}}
"""
def parse(input) do
cond do
Regex.match?(~r/^(.+)x(.+)$/, input) ->
Regex.run(~r/^(.+)x(.+)$/, input, capture: :all_but_first)
|> with_parsed_units(fn [width, height] ->
{:ok, %ContainParams{width: width, height: height}}
end)

true ->
{:error, {:parameter_parse_error, input}}
end
end
end
44 changes: 44 additions & 0 deletions lib/image_plug/transform/contain.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
defmodule ImagePlug.Transform.Contain do
@behaviour ImagePlug.Transform

alias ImagePlug.Transform
alias ImagePlug.TransformState

defmodule ContainParams do
@doc """
The parsed parameters used by `ImagePlug.Transform.Contain`.
"""
defstruct [:width, :height]

@type t :: %__MODULE__{width: ImagePlug.imgp_length(), height: ImagePlug.imgp_length()}
end

@impl ImagePlug.Transform
def execute(%TransformState{} = state, %ContainParams{width: width, height: height}) do
with {:ok, target_width} <- Transform.to_pixels(state, :width, width),
{:ok, target_height} <- Transform.to_pixels(state, :height, height),
{:ok, width_and_height} <-
fit_inside(state, %{width: target_width, height: target_height}),
{:ok, scaled_image} <- do_scale(state.image, width_and_height) do
# reset focus to :center on scale
%TransformState{state | image: scaled_image, focus: :center}
end
end

def fit_inside(%TransformState{image: image}, target) do
original_ar = Image.width(image) / Image.height(image)
target_ar = target.width / target.height

if original_ar > target_ar do
{:ok, %{width: target.width, height: round(target.width / original_ar)}}
else
{:ok, %{width: round(target.height * original_ar), height: target.height}}
end
end

def do_scale(image, %{width: width, height: height}) do
width_scale = width / Image.width(image)
height_scale = height / Image.height(image)
Image.resize(image, width_scale, vertical_scale: height_scale)
end
end
12 changes: 12 additions & 0 deletions test/param_parser/twicpics_parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ defmodule ParamParser.TwicpicsParserTest do
doctest ImagePlug.ParamParser.Twicpics.CropParser
doctest ImagePlug.ParamParser.Twicpics.ScaleParser
doctest ImagePlug.ParamParser.Twicpics.FocusParser
doctest ImagePlug.ParamParser.Twicpics.ContainParser
doctest ImagePlug.ParamParser.Twicpics.OutputParser

alias ImagePlug.ParamParser.Twicpics
alias ImagePlug.Transform.Crop
alias ImagePlug.Transform.Scale
alias ImagePlug.Transform.Focus
alias ImagePlug.Transform.Contain

defp random_base_unit,
do:
Expand Down Expand Up @@ -127,4 +130,13 @@ defmodule ParamParser.TwicpicsParserTest do
assert parsed == expected
end
end

test "contain params parser" do
check all width <- random_root_unit(),
height <- random_root_unit() do
str_params = "#{unit_str(width)}x#{unit_str(height)}"
parsed = Twicpics.ContainParser.parse(str_params)
assert {:ok, %Contain.ContainParams{width: width, height: height}} == parsed
end
end
end
Loading