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
39 changes: 38 additions & 1 deletion lib/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5907,6 +5907,12 @@ defmodule Image do
a list of 3 or 4 float values depending on the image
color space.

## Discrete rotation

When `angle` is a multiple of 90, and all displacement options
are unset, `nil`, `0` or `0.0`, the rotation will be done as a
discrete operation in order to preserve source pixel values.

## Notes

The displacement parameters cause the image canvas to be
Expand Down Expand Up @@ -5935,10 +5941,41 @@ defmodule Image do

def rotate(%Vimage{} = image, angle, options \\ []) when is_number(angle) do
with {:ok, options} <- Options.Rotate.validate_options(options) do
Operation.rotate(image, angle, options)
rot_angle = rot_angle(angle, options)

if rot_angle do
Operation.rot(image, rot_angle)
else
Operation.rotate(image, angle, options)
end
end
end

defp rot_angle(angle, options) do
if Options.Rotate.no_displacement?(options) do
to_rot_angle(angle)
end
end

defp to_rot_angle(angle) when is_integer(angle) and rem(angle, 90) == 0 do
angle
|> Integer.mod(360)
|> rot_angle_from_degrees()
end

defp to_rot_angle(angle) when is_float(angle) and angle == trunc(angle) do
angle
|> trunc()
|> to_rot_angle()
end

defp to_rot_angle(_angle), do: nil

defp rot_angle_from_degrees(0), do: :VIPS_ANGLE_D0
defp rot_angle_from_degrees(90), do: :VIPS_ANGLE_D90
defp rot_angle_from_degrees(180), do: :VIPS_ANGLE_D180
defp rot_angle_from_degrees(270), do: :VIPS_ANGLE_D270

@doc """
Rotate an image clockwise (to the
right) by a number of degrees.
Expand Down
12 changes: 12 additions & 0 deletions lib/image/options/rotate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,16 @@ defmodule Image.Options.Rotate do
defp invalid_option(option) do
"Invalid option or option value: #{inspect(option)}"
end

@doc false
def no_displacement?(options) do
empty_displacement?(options, :idx) and
empty_displacement?(options, :idy) and
empty_displacement?(options, :odx) and
empty_displacement?(options, :ody)
end

defp empty_displacement?(options, key) do
Keyword.get(options, key, 0) in [nil, 0, 0.0]
end
end
Binary file modified test/support/validate/Kip_small_rotate-90.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/support/validate/Kip_small_rotate90.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading