infer/matchers/
image.rs

1use std::convert::TryInto;
2
3/// Returns whether a buffer is JPEG image data.
4pub fn is_jpeg(buf: &[u8]) -> bool {
5    buf.len() > 2 && buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF
6}
7
8/// Returns whether a buffer is jpg2 image data.
9pub fn is_jpeg2000(buf: &[u8]) -> bool {
10    buf.len() > 12
11        && buf[0] == 0x0
12        && buf[1] == 0x0
13        && buf[2] == 0x0
14        && buf[3] == 0xC
15        && buf[4] == 0x6A
16        && buf[5] == 0x50
17        && buf[6] == 0x20
18        && buf[7] == 0x20
19        && buf[8] == 0xD
20        && buf[9] == 0xA
21        && buf[10] == 0x87
22        && buf[11] == 0xA
23        && buf[12] == 0x0
24}
25
26/// Returns whether a buffer is PNG image data.
27pub fn is_png(buf: &[u8]) -> bool {
28    buf.len() > 3 && buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47
29}
30
31/// Returns whether a buffer is GIF image data.
32pub fn is_gif(buf: &[u8]) -> bool {
33    buf.len() > 2 && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
34}
35
36/// Returns whether a buffer is WEBP image data.
37pub fn is_webp(buf: &[u8]) -> bool {
38    buf.len() > 11 && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50
39}
40
41/// Returns whether a buffer is Canon CR2 image data.
42pub fn is_cr2(buf: &[u8]) -> bool {
43    buf.len() > 9
44        && ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0)
45            || (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A))
46        && buf[8] == 0x43
47        && buf[9] == 0x52
48        && buf[10] == 0x02 // CR2 major version
49}
50
51/// Returns whether a buffer is TIFF image data.
52pub fn is_tiff(buf: &[u8]) -> bool {
53    buf.len() > 9
54        && ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0)
55            || (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A))
56        && buf[8] != 0x43
57        && buf[9] != 0x52
58        && !is_cr2(buf) // To avoid conflicts differentiate Tiff from CR2
59}
60
61/// Returns whether a buffer is BMP image data.
62pub fn is_bmp(buf: &[u8]) -> bool {
63    buf.len() > 1 && buf[0] == 0x42 && buf[1] == 0x4D
64}
65
66/// Returns whether a buffer is jxr image data.
67pub fn is_jxr(buf: &[u8]) -> bool {
68    buf.len() > 2 && buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0xBC
69}
70
71/// Returns whether a buffer is Photoshop PSD image data.
72pub fn is_psd(buf: &[u8]) -> bool {
73    buf.len() > 3 && buf[0] == 0x38 && buf[1] == 0x42 && buf[2] == 0x50 && buf[3] == 0x53
74}
75
76/// Returns whether a buffer is ICO icon image data.
77pub fn is_ico(buf: &[u8]) -> bool {
78    buf.len() > 3 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01 && buf[3] == 0x00
79}
80
81/// Returns whether a buffer is HEIF image data.
82pub fn is_heif(buf: &[u8]) -> bool {
83    if buf.is_empty() {
84        return false;
85    }
86
87    if !is_isobmff(buf) {
88        return false;
89    }
90
91    if let Some((major, _minor, compatible)) = get_ftyp(buf) {
92        if major == b"heic" {
93            return true;
94        }
95
96        if major == b"mif1" || major == b"msf1" {
97            for b in compatible {
98                if b == b"heic" {
99                    return true;
100                }
101            }
102        }
103    }
104
105    false
106}
107
108/// Returns whether a buffer is AVIF image data.
109pub fn is_avif(buf: &[u8]) -> bool {
110    if buf.is_empty() {
111        return false;
112    }
113
114    if !is_isobmff(buf) {
115        return false;
116    }
117
118    if let Some((major, _minor, compatible)) = get_ftyp(buf) {
119        if major == b"avif" || major == b"avis" {
120            return true;
121        }
122
123        for b in compatible {
124            if b == b"avif" || b == b"avis" {
125                return true;
126            }
127        }
128    }
129
130    false
131}
132
133// IsISOBMFF checks whether the given buffer represents ISO Base Media File Format data
134fn is_isobmff(buf: &[u8]) -> bool {
135    if buf.len() < 16 {
136        return false;
137    }
138
139    if &buf[4..8] != b"ftyp" {
140        return false;
141    }
142
143    let ftyp_length = u32::from_be_bytes(buf[0..4].try_into().unwrap()) as usize;
144    buf.len() >= ftyp_length
145}
146
147// GetFtyp returns the major brand, minor version and compatible brands of the ISO-BMFF data
148fn get_ftyp(buf: &[u8]) -> Option<(&[u8], &[u8], impl Iterator<Item = &[u8]>)> {
149    if buf.len() < 16 {
150        return None;
151    }
152
153    let ftyp_length = u32::from_be_bytes(buf[0..4].try_into().unwrap()) as usize;
154
155    let major = &buf[8..12];
156    let minor = &buf[12..16];
157    let compatible = (16..ftyp_length).step_by(4).map(move |i| &buf[i..i + 4]);
158
159    Some((major, minor, compatible))
160}