Skip to content

Commit b82a2b7

Browse files
committed
add support for the AVIF, HEIC and HEIF formats
These 3 formats have in common that they use the HEIF container format specified in ISO/IEC 23008-12. The MIME types and variant types (file extension) for HEIC and HEIF are based on sections C.2 and D.2 of the specification. A HEIF container can contain multiple images, so the logic is kept as in `:ico` (take the size of the largest image). The size of an image is found in the `ispe` box.
1 parent 053bf5d commit b82a2b7

File tree

5 files changed

+208
-4
lines changed

5 files changed

+208
-4
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Supported formats (image type to be parsed as):
5757
- `:psd`
5858
- `:tiff`
5959
- `:webp` (VP8X animated in `v0.2.4`)
60+
- `:avif`
61+
- `:heic`
62+
- `:heif`
6063

6164
## Mime-types and Variants
6265

@@ -68,9 +71,14 @@ Each mime-type can be linked to at least one variant type:
6871

6972
| mime-type | variant type | description |
7073
| ------------------------- | ------------ | ------------------ |
74+
| `image/avif` | `AVIF` | |
7175
| `image/bmp` | `BMP` | |
7276
| `image/gif` | `GIF87a` | 87a gif spec |
7377
| `image/gif` | `GIF89a` | 89a gif spec |
78+
| `image/heic` | `HEIC` | |
79+
| `image/heic-sequence` | `HEICS` | |
80+
| `image/heif` | `HEIF` | |
81+
| `image/heif-sequence` | `HEIFS` | |
7482
| `image/x-icon` | `ICO` | |
7583
| `image/jpeg` | `baseJPEG` | baseline JPEG |
7684
| `image/jpeg` | `progJPEG` | progressive JPEG |
@@ -89,10 +97,10 @@ Each mime-type can be linked to at least one variant type:
8997
The variant type is created just to provide a bit more of information
9098
for every image format (if applicable).
9199

92-
*Note*: `:ico` returns the dimensions of the largest image contained (not the first found).
100+
*Note*: `:avif`, `:heic`, `:heif` and `:ico` return the dimensions of the largest image contained (not the first found).
93101

94102
The guessing functions try to detect the format of the binary by testing every available type based on its global usage (popularity, [usage of image file formats](https://w3techs.com/technologies/overview/image_format/all), but still keeping the `:png` as the first one):
95-
- `:png`, `:jpeg`, `:gif`, `:bmp`, `:ico`, `:tiff`, `:webp`, `:psd`, `:jp2`, `:pnm`
103+
- `:png`, `:jpeg`, `:gif`, `:bmp`, `:ico`, `:tiff`, `:webp`, `:psd`, `:jp2`, `:pnm`, `:avif`, `:heic`, `:heif`
96104

97105
**Warnings:**
98106

lib/ex_image_info.ex

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,14 @@ defmodule ExImageInfo do
4141
4242
| mime-type | variant type | description |
4343
| ------------------------- | ------------ | ------------------ |
44+
| `image/avif` | `AVIF` | |
4445
| `image/bmp` | `BMP` | |
4546
| `image/gif` | `GIF87a` | 87a gif spec |
4647
| `image/gif` | `GIF89a` | 89a gif spec |
48+
| `image/heic` | `HEIC` | |
49+
| `image/heic-sequence` | `HEICS` | |
50+
| `image/heif` | `HEIF` | |
51+
| `image/heif-sequence` | `HEIFS` | |
4752
| `image/x-icon` | `ICO` | |
4853
| `image/jpeg` | `baseJPEG` | baseline JPEG |
4954
| `image/jpeg` | `progJPEG` | progressive JPEG |
@@ -65,10 +70,12 @@ defmodule ExImageInfo do
6570
*Note*: `:ico` returns the dimensions of the largest image contained (not the first found).
6671
6772
The guessing functions try to detect the format of the binary by testing every available type based on its global usage (popularity, [usage of image file formats](https://w3techs.com/technologies/overview/image_format/all), but still keeping the `:png` as the first one):
68-
- `:png`, `:jpeg`, `:gif`, `:bmp`, `:ico`, `:tiff`, `:webp`, `:psd`, `:jp2`, `:pnm`
73+
- `:png`, `:jpeg`, `:gif`, `:bmp`, `:ico`, `:tiff`, `:webp`, `:psd`, `:jp2`, `:pnm`, `:avif`, `:heic`, `:heif`
6974
"""
7075
alias ExImageInfo.Types.BMP
7176
alias ExImageInfo.Types.GIF
77+
alias ExImageInfo.Types.HEIC
78+
alias ExImageInfo.Types.HEIF
7279
alias ExImageInfo.Types.ICO
7380
alias ExImageInfo.Types.JP2
7481
alias ExImageInfo.Types.JPEG
@@ -81,7 +88,21 @@ defmodule ExImageInfo do
8188
# Guessing function ordered by global usage
8289
# https://w3techs.com/technologies/overview/image_format/all
8390
# but still keeping :png as the first
84-
@types [:png, :jpeg, :gif, :bmp, :ico, :tiff, :webp, :psd, :jp2, :pnm]
91+
@types [
92+
:png,
93+
:jpeg,
94+
:gif,
95+
:bmp,
96+
:ico,
97+
:tiff,
98+
:webp,
99+
:psd,
100+
:jp2,
101+
:pnm,
102+
:avif,
103+
:heic,
104+
:heif
105+
]
85106

86107
@typedoc "The supported image formats"
87108
@type image_format ::
@@ -95,6 +116,9 @@ defmodule ExImageInfo do
95116
| :psd
96117
| :jp2
97118
| :pnm
119+
| :avif
120+
| :heic
121+
| :heif
98122

99123
## Public API
100124

@@ -138,6 +162,9 @@ defmodule ExImageInfo do
138162
def seems?(binary, :jp2), do: JP2.seems?(binary)
139163
def seems?(binary, :pnm), do: PNM.seems?(binary)
140164
def seems?(binary, :ico), do: ICO.seems?(binary)
165+
def seems?(binary, :avif), do: AVIF.seems?(binary)
166+
def seems?(binary, :heic), do: HEIC.seems?(binary)
167+
def seems?(binary, :heif), do: HEIF.seems?(binary)
141168
def seems?(_, _), do: nil
142169

143170
@doc """
@@ -209,6 +236,9 @@ defmodule ExImageInfo do
209236
def type(binary, :jp2), do: JP2.type(binary)
210237
def type(binary, :pnm), do: PNM.type(binary)
211238
def type(binary, :ico), do: ICO.type(binary)
239+
def type(binary, :avif), do: AVIF.type(binary)
240+
def type(binary, :heic), do: HEIC.type(binary)
241+
def type(binary, :heif), do: HEIF.type(binary)
212242
def type(_, _), do: nil
213243

214244
@doc """
@@ -280,6 +310,9 @@ defmodule ExImageInfo do
280310
def info(binary, :jp2), do: JP2.info(binary)
281311
def info(binary, :pnm), do: PNM.info(binary)
282312
def info(binary, :ico), do: ICO.info(binary)
313+
def info(binary, :avif), do: AVIF.info(binary)
314+
def info(binary, :heic), do: HEIC.info(binary)
315+
def info(binary, :heif), do: HEIF.info(binary)
283316
def info(_, _), do: nil
284317

285318
@doc """

lib/ex_image_info/types/avif.ex

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
defmodule ExImageInfo.Types.AVIF do
2+
@moduledoc false
3+
4+
alias ExImageInfo.Types.HEIF
5+
6+
@behaviour ExImageInfo.Detector
7+
8+
@mime "image/avif"
9+
@signature <<"ftypavif">>
10+
@ftype "AVIF"
11+
12+
## Public API
13+
14+
def seems?(<<_::size(32), @signature, _rest::binary>>), do: true
15+
def seems?(_), do: false
16+
17+
def info(<<_::size(32), @signature, rest::binary>>),
18+
do: HEIF.info(rest, @mime, @ftype)
19+
20+
def info(_), do: nil
21+
22+
def type(<<_::size(32), @signature, _rest::binary>>), do: {@mime, @ftype}
23+
def type(_), do: nil
24+
end

lib/ex_image_info/types/heic.ex

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
defmodule ExImageInfo.Types.HEIC do
2+
@moduledoc false
3+
4+
alias ExImageInfo.Types.HEIF
5+
6+
@behaviour ExImageInfo.Detector
7+
8+
@mime_heic "image/heic"
9+
@mime_heic_sequence "image/heic-sequence"
10+
@signature_heic <<"ftypheic">>
11+
@signature_heim <<"ftypheim">>
12+
@signature_heis <<"ftypheis">>
13+
@signature_heix <<"ftypheix">>
14+
@signature_hevc <<"ftyphevc">>
15+
@signature_hevm <<"ftyphevm">>
16+
@signature_hevs <<"ftyphevs">>
17+
@signature_hevx <<"ftyphevx">>
18+
@ftype_heic "HEIC"
19+
@ftype_heic_sequence "HEICS"
20+
21+
## Public API
22+
23+
def seems?(<<_::size(32), @signature_heic, _rest::binary>>), do: true
24+
def seems?(<<_::size(32), @signature_heim, _rest::binary>>), do: true
25+
def seems?(<<_::size(32), @signature_heis, _rest::binary>>), do: true
26+
def seems?(<<_::size(32), @signature_heix, _rest::binary>>), do: true
27+
def seems?(_), do: false
28+
29+
def info(<<_::size(32), @signature_heic, rest::binary>>),
30+
do: HEIF.info(rest, @mime_heic, @ftype_heic)
31+
32+
def info(<<_::size(32), @signature_heim, rest::binary>>),
33+
do: HEIF.info(rest, @mime_heic, @ftype_heic)
34+
35+
def info(<<_::size(32), @signature_heis, rest::binary>>),
36+
do: HEIF.info(rest, @mime_heic, @ftype_heic)
37+
38+
def info(<<_::size(32), @signature_heix, rest::binary>>),
39+
do: HEIF.info(rest, @mime_heic, @ftype_heic)
40+
41+
def info(<<_::size(32), @signature_hevc, rest::binary>>),
42+
do: HEIF.info(rest, @mime_heic_sequence, @ftype_heic_sequence)
43+
44+
def info(<<_::size(32), @signature_hevm, rest::binary>>),
45+
do: HEIF.info(rest, @mime_heic_sequence, @ftype_heic_sequence)
46+
47+
def info(<<_::size(32), @signature_hevs, rest::binary>>),
48+
do: HEIF.info(rest, @mime_heic_sequence, @ftype_heic_sequence)
49+
50+
def info(<<_::size(32), @signature_hevx, rest::binary>>),
51+
do: HEIF.info(rest, @mime_heic_sequence, @ftype_heic_sequence)
52+
53+
def info(_), do: nil
54+
55+
def type(<<_::size(32), @signature_heic, _rest::binary>>),
56+
do: {@mime_heic, @ftype_heic}
57+
58+
def type(<<_::size(32), @signature_heim, _rest::binary>>),
59+
do: {@mime_heic, @ftype_heic}
60+
61+
def type(<<_::size(32), @signature_heis, _rest::binary>>),
62+
do: {@mime_heic, @ftype_heic}
63+
64+
def type(<<_::size(32), @signature_heix, _rest::binary>>),
65+
do: {@mime_heic, @ftype_heic}
66+
67+
def type(<<_::size(32), @signature_hevc, _rest::binary>>),
68+
do: {@mime_heic_sequence, @ftype_heic_sequence}
69+
70+
def type(<<_::size(32), @signature_hevm, _rest::binary>>),
71+
do: {@mime_heic_sequence, @ftype_heic_sequence}
72+
73+
def type(<<_::size(32), @signature_hevs, _rest::binary>>),
74+
do: {@mime_heic_sequence, @ftype_heic_sequence}
75+
76+
def type(<<_::size(32), @signature_hevx, _rest::binary>>),
77+
do: {@mime_heic_sequence, @ftype_heic_sequence}
78+
79+
def type(_), do: nil
80+
end

lib/ex_image_info/types/heif.ex

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
defmodule ExImageInfo.Types.HEIF do
2+
@moduledoc false
3+
4+
@behaviour ExImageInfo.Detector
5+
6+
@mime_heif "image/heif"
7+
@mime_heif_sequence "image/heif-sequence"
8+
@signature_mif1 <<"ftypmif1">>
9+
@signature_msf1 <<"ftypmsf1">>
10+
@ftype_heif "HEIF"
11+
@ftype_heif_sequence "HEIFS"
12+
13+
## Public API
14+
15+
def seems?(<<_::size(32), @signature_mif1, _rest::binary>>), do: true
16+
def seems?(<<_::size(32), @signature_msf1, _rest::binary>>), do: true
17+
def seems?(_), do: false
18+
19+
def info(<<_::size(32), @signature_mif1, rest::binary>>) do
20+
info(rest, @mime_heif, @ftype_heif)
21+
end
22+
23+
def info(<<_::size(32), @signature_msf1, rest::binary>>),
24+
do: info(rest, @mime_heif_sequence, @ftype_heif_sequence)
25+
26+
def info(_), do: nil
27+
28+
def info(binary, mime, ftype) do
29+
case size(binary) do
30+
{w, h} -> {mime, w, h, ftype}
31+
_ -> nil
32+
end
33+
end
34+
35+
def type(<<_::size(32), @signature_mif1, _rest::binary>>),
36+
do: {@mime_heif, @ftype_heif}
37+
38+
def type(<<_::size(32), @signature_msf1, _rest::binary>>),
39+
do: {@mime_heif_sequence, @ftype_heif_sequence}
40+
41+
def type(_), do: nil
42+
43+
defp size(binary) do
44+
[_hd | ispe_boxes] = String.split(binary, "ispe")
45+
46+
if length(ispe_boxes) > 0 do
47+
{width, height} =
48+
ispe_boxes
49+
|> Enum.map(fn <<_::size(32), width::size(32), height::size(32), _rest::binary>> ->
50+
{width, height}
51+
end)
52+
|> Enum.max_by(&elem(&1, 0))
53+
54+
{width, height}
55+
else
56+
nil
57+
end
58+
end
59+
end

0 commit comments

Comments
 (0)