diff --git a/lib/image/pixel.ex b/lib/image/pixel.ex index 10044db..a2a03e1 100644 --- a/lib/image/pixel.ex +++ b/lib/image/pixel.ex @@ -525,26 +525,20 @@ defmodule Image.Pixel do end end + # Encoders grouped by their alpha band's max value + @alpha_max_255 [:uchar_rgb, :uchar_cmyk, :uchar_hsv, :uchar_grey, :float_lab, :float_lch, :short_lab] + @alpha_max_65535 [:ushort_rgb, :ushort_grey] + # The alpha band uses the same numeric type as the rest of the # interpretation: 0..255 for uchar, 0..65535 for ushort, 0.0..1.0 - # for float-typed bands. LABS / Lab / LCH happen to be float-typed - # interpretations whose alpha band is also a float in [0, 1]. + # for float-typed bands. scRGB is the only float interpretation whose + # alpha is in [0, 1]. LABS / Lab / LCH carry a 0..255 alpha band + # despite their float/short color bands. defp scale_alpha_to_encoder(alpha, encoder) do case encoder do - e when e in [:uchar_rgb, :uchar_cmyk, :uchar_hsv, :uchar_grey] -> - scale(alpha, 255) - - e when e in [:ushort_rgb, :ushort_grey] -> - scale(alpha, 65_535) - - :float_rgb -> - alpha * 1.0 - - e when e in [:float_lab, :float_lch] -> - alpha * 1.0 - - :short_lab -> - round(alpha * 65_535) + e when e in @alpha_max_255 -> scale(alpha, 255) + e when e in @alpha_max_65535 -> scale(alpha, 65_535) + :float_rgb -> alpha * 1.0 end end diff --git a/test/pixel_test.exs b/test/pixel_test.exs index e279a51..c104ce5 100644 --- a/test/pixel_test.exs +++ b/test/pixel_test.exs @@ -120,6 +120,39 @@ defmodule Image.PixelTest do end end + describe "to_pixel/3 alpha scale matches the interpretation's max alpha" do + defp with_alpha(colorspace) do + {:ok, srgba} = Image.new(2, 2, color: [10, 20, 30, 255]) + {:ok, image} = Image.to_colorspace(srgba, colorspace) + image + end + + test "opaque alpha resolves to 255 for every 0..255-scale interpretation" do + for colorspace <- [:srgb, :cmyk, :hsv, :bw, :lab, :lch, :labs] do + image = with_alpha(colorspace) + {:ok, pixel} = Pixel.to_pixel(image, :red, alpha: :opaque) + assert List.last(pixel) == 255, "#{colorspace} opaque alpha was #{List.last(pixel)}" + end + end + + test "scRGB opaque alpha stays 1.0" do + image = with_alpha(:scrgb) + assert {:ok, [_r, _g, _b, alpha]} = Pixel.to_pixel(image, :red, alpha: :opaque) + assert alpha == 1.0 + end + + test "a plain color (no explicit :alpha) also synthesizes opaque 255 on Lab" do + image = with_alpha(:lab) + assert {:ok, [_l, _a, _b, 255]} = Pixel.to_pixel(image, :red) + end + + test "alpha 0.5 resolves to 128 on Lab" do + image = with_alpha(:lab) + assert {:ok, [_l, _a, _b, alpha]} = Pixel.to_pixel(image, :red, alpha: 0.5) + assert alpha == 128 + end + end + describe "to_pixel/3 against a CMYK image" do setup do {:ok, image} = Image.new(2, 2, color: :black)