Packages
image
0.7.0
0.72.0
0.71.0
0.70.0
0.69.0
0.68.0
0.67.0
0.67.0-dev
retired
0.66.0
0.65.0
0.64.0
0.63.0
0.62.1
0.62.0
0.61.1
0.61.0
0.60.0
0.59.3
0.59.2
0.59.1
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.2
0.55.1
retired
0.55.0
0.54.4
0.54.3
0.54.2
0.54.1
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
retired
0.51.0
0.50.0
0.49.0
0.48.1
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.2
0.43.1
0.43.0
0.42.0
0.41.0
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.0
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
retired
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.4
0.14.2
0.14.1
0.14.0
retired
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.10.0-rc.0
retired
0.9.0
retired
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
An approachable image processing library primarily based upon Vix and libvips that is NIF-based, fast, multi-threaded, pipelined and has a low memory footprint.
Current section
Files
Jump to
Current section
Files
lib/image/exif/tag.ex
defmodule Image.Exif.Tag do
@moduledoc false
# Parse the different tag type values (strings, unsigned shorts, etc...)
@max_signed_32_bit_int 2_147_483_647
@type value :: binary()
@type context :: {value(), non_neg_integer(), (any() -> non_neg_integer())}
# unsigned byte, size = 1
@spec value(non_neg_integer(), non_neg_integer(), value(), context()) :: any()
def value(1, count, value, context),
do: decode_numeric(value, count, 2, context)
# ascii string, size = 1
def value(2, count, value, {exif, _offset, ru}) do
# ignore null-byte at end
length = count - 1
if count > 4 do
# + offset
offset = ru.(value)
<<_::binary-size(offset), string::binary-size(length), _::binary>> = exif
string
else
<<string::binary-size(length), _::binary>> = value
string
end
end
# unsigned short, size = 2
def value(3, count, value, context),
do: decode_numeric(value, count, 2, context)
# unsigned long, size = 4
def value(4, count, value, context),
do: decode_numeric(value, count, 4, context)
# unsigned rational, size = 8
def value(5, count, value, context),
do: decode_ratio(value, count, context, :unsigned)
# undefined, size = 1
def value(7, count, value, context),
do: decode_numeric(value, count, 1, context)
# signed rational, size = 8
def value(10, count, value, context),
do: decode_ratio(value, count, context, :signed)
# Handle malformed tags
def value(_, _, _, _), do: nil
@spec decode_numeric(
value :: value(),
non_neg_integer(),
non_neg_integer(),
context()
) :: any()
defp decode_numeric(value, count, size, {exif, _offset, ru}) do
length = count * size
values =
if length > 4 do
case exif do
<<_::binary-size(value), data::binary-size(length), _::binary>> -> data
# probably a maker_note or user_comment
_ -> nil
end
else
<<data::binary-size(length), _::binary>> = value
data
end
if values do
if count == 1 do
ru.(values)
else
read_unsigned_many(values, size, ru)
end
end
end
@spec decode_ratio(
value(),
non_neg_integer(),
context(),
:unsigned | :signed
) :: any()
defp decode_ratio(value_offset, count, {exif, _offset, ru}, signed) do
exif
|> decode_ratios(count, ru.(value_offset), ru, signed)
|> do_decode_ratio(count)
end
@spec do_decode_ratio(list(), non_neg_integer()) :: any()
defp do_decode_ratio([result | _], 1), do: result
defp do_decode_ratio(result, _), do: result
@spec decode_ratios(
value :: value(),
non_neg_integer(),
non_neg_integer(),
(any() -> non_neg_integer()),
:unsigned | :signed
) :: list()
defp decode_ratios(_data, 0, _offset, _ru, _signed), do: []
defp decode_ratios(data, count, offset, ru, signed) do
case data do
<<_::binary-size(offset), numerator::binary-size(4), denominator::binary-size(4),
rest::binary>> ->
d = maybe_signed_int(ru.(denominator), signed)
n = maybe_signed_int(ru.(numerator), signed)
result =
case {d, n} do
{1, n} -> n
{d, 1} -> "1/#{d}"
{0, _} -> :infinity
{d, n} -> round(n * 1000 / d) / 1000
end
[result | decode_ratios(rest, count - 1, 0, ru, signed)]
_ ->
[]
end
end
@spec read_unsigned_many(binary(), non_neg_integer(), ([any()] -> binary())) :: any()
defp read_unsigned_many(<<>>, _size, _ru), do: []
defp read_unsigned_many(data, size, ru) do
<<number::binary-size(size), rest::binary>> = data
[ru.(number) | read_unsigned_many(rest, size, ru)]
end
@spec maybe_signed_int(non_neg_integer(), :signed | :unsigned) :: non_neg_integer()
defp maybe_signed_int(x, :signed) when x > @max_signed_32_bit_int,
do: x - (@max_signed_32_bit_int + 1) * 2
# +ve or unsigned
defp maybe_signed_int(x, _), do: x
end