Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_graphics/src/heif.rs

51.5 KiB, 98 runs

created by r1870400018:20456, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! A reader of the HEIF container: the boxes that say which picture a file holds and where its
2//! bytes are.
3//!
4//! This is the half of HEIC that is not a codec. A HEIF file is ISO base media file format boxes --
5//! the same length-prefixed structure [`crate::mp4`] writes -- carrying a set of *items* rather
6//! than a track: one of them is the picture, the rest are its thumbnail, its Exif block, its colour
7//! profile and, on a modern phone, the several dozen tiles the picture is actually cut into. None
8//! of that is compressed and none of it needs a decoder, so it is read here and read completely,
9//! and what a decoder is then handed is a run of bytes and the configuration record that describes
10//! them.
11//!
12//! # Why this exists before any HEVC decoder does
13//!
14//! Two things a photograph library needs are in the container and not in the coded picture.
15//!
16//! The **size** is one. A reader that takes the first `ispe` box it meets gets the thumbnail's
17//! extent, because a phone writes the thumbnail's properties first: a four-thousand-pixel
18//! photograph is then indexed as five hundred and twelve pixels square. The size is only right if
19//! the primary item is resolved -- `pitm` names it, `ipma` says which properties are its -- and, for
20//! a picture stored as a grid, only the `grid` item itself carries the assembled extent, since no
21//! tile knows how many tiles are beside it.
22//!
23//! The **Exif block** is the other, and it hangs off the primary item by an `iref` of type `cdsc`
24//! rather than sitting in a box of its own.
25//!
26//! # What is read
27//!
28//! `ftyp`, and inside `meta`: `hdlr`, `pitm`, `iinf` and its `infe` entries, `iref`, `iprp` with the
29//! property container `ipco` and the association table `ipma`, `iloc`, and `idat`. Of the
30//! properties, `ispe` (extent), `hvcC` (the HEVC decoder configuration), `irot` (rotation), `pixi`
31//! (bits a channel) and `clap` (a cropping window) are kept; the rest, including the ICC profile in
32//! `colr`, are recorded as present and left where they are.
33//!
34//! # What is refused
35//!
36//! A file whose boxes do not tile it exactly; a box that claims to end beyond its parent; a `meta`
37//! with no `pitm`, or a `pitm` naming an item that no `iinf` describes; an item whose extents fall
38//! outside the file or outside `idat`; a grid naming a number of tiles that is not its rows times
39//! its columns; and more items than [`MAX_ITEMS`], which no photograph is.
40//!
41//! Nothing here decodes a pixel. A single-tile picture yields one run of bytes and its `hvcC`; a
42//! grid yields the geometry and each tile's bytes in raster order; and a caller with no HEVC
43//! decoder can still say how big the picture is, which way up it goes, and what its camera wrote.
44//!
45//! # References
46//!
47//! The box structure is ISO/IEC 14496-12 (§8.11 for the metadata boxes). The image-specific
48//! items, properties and the `grid` derivation are ISO/IEC 23008-12 (§6 for the item structure,
49//! §6.5 for the properties, §6.6.2.3 for the grid). The decoder configuration record `hvcC` carries
50//! is ISO/IEC 14496-15 §8.3.3. Each non-obvious constant below names the clause it comes from.
51//!
52//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
53//! Anthropic Claude
54
55use crate::{
56 hevc,
57 pixmap::Pixmap,
58};
59
60use oxedyne_fe2o3_core::prelude::*;
61
62use std::collections::BTreeMap;
63
64// The most items one file may hold. A photograph cut into tiles of five hundred and twelve pixels
65// needs one item a tile, so a very large picture can reach a few hundred; sixty-five thousand is a
66// ceiling against a length that is a mistake rather than a limit anything real approaches.
67pub const MAX_ITEMS: usize = 65_536;
68
69// How deep the box tree may nest before it is treated as malformed. The deepest legal path here
70// is meta > iprp > ipco > a property, which is three.
71pub const MAX_DEPTH: usize = 8;
72
73/// A run of bytes belonging to one item.
74///
75/// An item is allowed to be scattered, and a grid's tiles routinely are, so an item's bytes are a
76/// list of these rather than one offset and one length.
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct Extent {
79 pub off: u64, // offset into whatever Where says it is in
80 pub len: u64, // bytes
81}
82
83/// What an item's extents are offsets into.
84///
85/// `iloc` calls this the construction method (ISO/IEC 14496-12 §8.11.3.3). Method 2 -- an item
86/// stored inside another item -- is legal and is not written by any camera; it is read as far as
87/// saying so, and refused rather than guessed at.
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum Where {
90 File, // offsets into the file, where a coded picture lives
91 Idat, // into the idat box, where a small derivation like a grid lives
92 Item, // into another item, which this reader refuses
93}
94
95/// One item: what it is, and where its bytes are.
96#[derive(Clone, Debug)]
97pub struct Item {
98 pub id: u32, // what iinf, iref, ipma and iloc all name it by
99 pub kind: [u8; 4], // hvc1 a coded picture, grid a derivation, Exif, mime for XMP
100 pub primary: bool, // did pitm name this one?
101 pub place: Where, // what its extents are offsets into
102 pub extents: Vec<Extent>, // where its bytes are, in order
103}
104
105/// A picture assembled out of tiles, ISO/IEC 23008-12 §6.6.2.3.
106///
107/// The assembled extent is **not** the tiles' extent summed: the grid is allowed to be larger than
108/// the picture and the picture is then cropped out of its top left, which is how a photograph of
109/// twelve hundred and eighty by nine hundred and sixty comes out of six tiles of five hundred and
110/// twelve square.
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112pub struct Grid {
113 pub rows: u16, // tiles down
114 pub cols: u16, // tiles across
115 pub width: u32, // the assembled picture's width in pixels, after cropping
116 pub height: u32, // and its height
117}
118
119/// One property out of `ipco`, in the order the box holds them.
120///
121/// The ones a decoder or a library needs are read into their own shape; everything else is kept as
122/// its type and its span, so that a caller wanting the ICC profile can find it without this module
123/// having an opinion about colour management.
124#[derive(Clone, Debug)]
125pub enum Prop {
126 Extent { // ispe: the item's extent in pixels, before any rotation
127 w: u32,
128 h: u32,
129 },
130 Config(Extent), // hvcC: the HEVC decoder configuration record, as a span of the file
131 Rotation(u8), // irot: quarter turns anticlockwise the viewer applies, 0 to 3
132 Depth(Vec<u8>), // pixi: how many bits each channel carries
133 Other([u8; 4], Extent), // anything else, as its type and the span of its body
134}
135
136/// A HEIF file's metadata, read whole.
137///
138/// It borrows the bytes it was read from, so an item's data is handed back as a slice rather than
139/// copied.
140#[derive(Clone, Debug)]
141pub struct Heif<'a> {
142 bytes: &'a [u8], // the bytes the boxes were read out of
143 brand: [u8; 4], // from ftyp: HEVC or AV1
144 items: Vec<Item>, // in the order iinf listed them
145 primary: u32, // the identifier pitm named
146 props: Vec<Prop>, // the properties in ipco, indexed from one by ipma
147 owned: BTreeMap<u32, Vec<u16>>, // which properties belong to which item
148 derived: BTreeMap<u32, Vec<u32>>, // what each item derives from, out of dimg
149 describes: BTreeMap<u32, Vec<u32>>, // what each item describes, out of cdsc
150 idat: Option<Extent>, // the span where a derivation's bytes live
151}
152
153/// What the primary item turned out to be.
154#[derive(Clone, Debug)]
155pub enum Picture {
156 One { // one coded picture, and the configuration that reads it
157 item: u32,
158 size: (u32, u32), // its extent in pixels
159 },
160 Tiled { // cut into tiles, in raster order from the top left
161 grid: Grid,
162 tiles: Vec<u32>, // the items holding the tiles, row by row
163 },
164 // A codec this container reader identifies but does not describe further, which is how a
165 // JPEG inside a HEIF wrapper arrives.
166 Foreign {
167 item: u32,
168 kind: [u8; 4], // its four-character type
169 },
170}
171
172impl<'a> Heif<'a> {
173
174 /// Reads a file's boxes.
175 ///
176 /// The whole file is wanted rather than its head: `iloc` addresses `mdat` by absolute offset,
177 /// so an item's bytes cannot be handed back from a prefix. A caller with only a head can still
178 /// call this and will get the metadata, and [`Self::data`] will then refuse the item rather
179 /// than return a short slice.
180 pub fn read(bytes: &'a [u8]) -> Outcome<Self> {
181 Self::parse(bytes, true)
182 }
183
184 /// Reads the boxes out of the front of a file.
185 ///
186 /// A library that has read the first few tens of kilobytes to work out what a file is has the
187 /// metadata already, and the metadata is all that is needed to say how big the picture is. The
188 /// last box is allowed to run past the end of what was read -- it is `mdat`, and its bytes were
189 /// never asked for -- and no item is checked against the file's length, since the file is
190 /// longer than the buffer by construction. [`Self::data`] refuses afterwards rather than
191 /// returning a short slice.
192 pub fn head(bytes: &'a [u8]) -> Outcome<Self> {
193 Self::parse(bytes, false)
194 }
195
196 fn parse(bytes: &'a [u8], whole: bool) -> Outcome<Self> {
197 // Asked before the walk rather than after it. A walk that meets a JPEG's first bytes
198 // reports a box four gigabytes long, which is true and useless.
199 if bytes.len() < 8 || &bytes[4..8] != b"ftyp" {
200 return Err(err!(
201 "The file does not open with a file type box, so it is not a HEIF file at all. \
202 The extension is not evidence: a fifth of the .heic files in one real library are \
203 JPEG under another name.";
204 Invalid, Input, Decode));
205 }
206 let mut heif = Self {
207 bytes,
208 brand: [0; 4],
209 items: Vec::new(),
210 primary: 0,
211 props: Vec::new(),
212 owned: BTreeMap::new(),
213 derived: BTreeMap::new(),
214 describes: BTreeMap::new(),
215 idat: None,
216 };
217 let mut found_meta = false;
218 res!(walk_from(bytes, 0, bytes.len(), 0, whole, &mut |kind, body| {
219 match &kind {
220 b"ftyp" => {
221 if body.len < 4 {
222 return Err(err!(
223 "The file type box is {} bytes, and a brand is four.", body.len;
224 Invalid, Input, Decode));
225 }
226 let at = body.off as usize;
227 heif.brand.copy_from_slice(&bytes[at..at + 4]);
228 Ok(Walk::Over)
229 },
230 b"meta" => {
231 found_meta = true;
232 // A full box: one byte of version and three of flags before the children.
233 Ok(Walk::Into(4))
234 },
235 _ => Ok(Walk::Over),
236 }
237 }));
238 if !found_meta {
239 return Err(err!(
240 "The file carries no metadata box, so it holds no items."; Invalid, Input, Missing));
241 }
242 // The second pass reads the boxes whose meaning depends on the others being in hand: the
243 // properties have to exist before `ipma` can point at them, and the items before `iloc`
244 // can place them. Walking twice costs nothing measurable -- these boxes are a few tens of
245 // kilobytes and hold no compression -- and it keeps each reader below free of ordering
246 // rules the format does not actually guarantee.
247 res!(heif.read_meta(whole));
248 if whole {
249 res!(heif.check());
250 }
251 Ok(heif)
252 }
253
254 /// The brand from `ftyp`, which is `heic` for an HEVC picture and `avif` for an AV1 one.
255 pub fn brand(&self) -> [u8; 4] {
256 self.brand
257 }
258
259 pub fn items(&self) -> &[Item] {
260 &self.items
261 }
262
263 pub fn primary(&self) -> Outcome<&Item> {
264 match self.items.iter().find(|i| i.id == self.primary) {
265 Some(item) => Ok(item),
266 None => Err(err!(
267 "The primary item is number {}, which no item information entry describes.",
268 self.primary;
269 Invalid, Input, Missing)),
270 }
271 }
272
273 /// What the primary item is: one coded picture, a grid of them, or something foreign.
274 pub fn picture(&self) -> Outcome<Picture> {
275 let item = res!(self.primary());
276 if &item.kind == b"grid" {
277 let grid = res!(self.grid(item.id));
278 let tiles = match self.derived.get(&item.id) {
279 Some(ids) => ids.clone(),
280 None => Vec::new(),
281 };
282 let want = grid.rows as usize * grid.cols as usize;
283 if tiles.len() != want {
284 return Err(err!(
285 "The grid is {} by {} and so wants {} tiles, and {} are named.",
286 grid.cols, grid.rows, want, tiles.len();
287 Invalid, Input, Decode));
288 }
289 return Ok(Picture::Tiled { grid, tiles });
290 }
291 if &item.kind == b"hvc1" || &item.kind == b"hev1" || &item.kind == b"av01" {
292 let size = res!(self.extent_of(item.id));
293 return Ok(Picture::One { item: item.id, size });
294 }
295 Ok(Picture::Foreign { item: item.id, kind: item.kind })
296 }
297
298 /// The picture's width and height in pixels, as it is meant to be looked at.
299 ///
300 /// This is the number a library indexes and lays a tile out by, and it is **not** the first
301 /// `ispe` in the file: a grid's extent comes from the grid, and a quarter turn in `irot`
302 /// exchanges the two.
303 pub fn size(&self) -> Outcome<(u32, u32)> {
304 let (w, h) = res!(self.extent());
305 Ok(if res!(self.rotation()) % 2 == 1 { (h, w) } else { (w, h) })
306 }
307
308 /// The picture's width and height in pixels **as they are coded**, before any rotation.
309 ///
310 /// This is the pair a caller wants where it applies the Exif orientation itself, which is the
311 /// usual arrangement in a library that also reads JPEG: a phone writes `irot` *and* an Exif
312 /// orientation saying the same thing, so a reader that turns the picture by both turns it
313 /// twice and lays a portrait photograph out landscape again.
314 pub fn extent(&self) -> Outcome<(u32, u32)> {
315 match res!(self.picture()) {
316 Picture::One { size, .. } => Ok(size),
317 Picture::Tiled { grid, .. } => Ok((grid.width, grid.height)),
318 Picture::Foreign { item, .. } => self.extent_of(item),
319 }
320 }
321
322 /// How far the picture is turned, in quarter turns anticlockwise.
323 ///
324 /// Zero where the file says nothing, which is the common case: a phone that writes `irot` also
325 /// writes the Exif orientation, and a reader that applies both turns the picture twice.
326 pub fn rotation(&self) -> Outcome<u8> {
327 let item = res!(self.primary());
328 for prop in self.props_of(item.id) {
329 if let Prop::Rotation(turns) = prop {
330 return Ok(*turns);
331 }
332 }
333 Ok(0)
334 }
335
336 /// The HEVC decoder configuration record for an item, as bytes.
337 ///
338 /// Every tile of a grid shares one of these, and it is associated with the tiles rather than
339 /// with the grid, so a caller asks for a tile's.
340 pub fn config(&self, item: u32) -> Outcome<&'a [u8]> {
341 for prop in self.props_of(item) {
342 if let Prop::Config(span) = prop {
343 return self.slice(*span);
344 }
345 }
346 Err(err!(
347 "Item {} carries no decoder configuration record.", item; Invalid, Input, Missing))
348 }
349
350 /// An item's bytes, gathered out of the file in extent order.
351 ///
352 /// A single-extent item -- which nearly every one is -- borrows rather than copies.
353 pub fn data(&self, item: u32) -> Outcome<std::borrow::Cow<'a, [u8]>> {
354 let found = match self.items.iter().find(|i| i.id == item) {
355 Some(i) => i,
356 None => return Err(err!("There is no item {} in this file.", item; Invalid, Input)),
357 };
358 let base = match found.place {
359 Where::File => Extent { off: 0, len: self.bytes.len() as u64 },
360 Where::Idat => match self.idat {
361 Some(span) => span,
362 None => return Err(err!(
363 "Item {} says its bytes are in the item data box, and there is none.", item;
364 Invalid, Input, Missing)),
365 },
366 Where::Item => return Err(err!(
367 "Item {} is stored inside another item, which this reader does not follow.", item;
368 Invalid, Input, Unknown)),
369 };
370 if found.extents.len() == 1 {
371 let one = found.extents[0];
372 return Ok(std::borrow::Cow::Borrowed(res!(self.slice(Extent {
373 off: base.off + one.off, len: one.len }))));
374 }
375 let mut out = Vec::new();
376 for span in &found.extents {
377 out.extend_from_slice(res!(self.slice(Extent {
378 off: base.off + span.off, len: span.len })));
379 }
380 Ok(std::borrow::Cow::Owned(out))
381 }
382
383 /// The Exif block the camera wrote, without the four-byte offset header that precedes it.
384 ///
385 /// It hangs off the primary item by a `cdsc` reference, and its payload begins with a
386 /// four-byte offset to the TIFF header (ISO/IEC 23008-12 §A.2.1) which is skipped here so that
387 /// what comes back is what an Exif reader expects: `MM` or `II` and then the first directory.
388 pub fn exif(&self) -> Outcome<Option<&'a [u8]>> {
389 let primary = self.primary;
390 for item in &self.items {
391 if &item.kind != b"Exif" {
392 continue;
393 }
394 let describes_primary = match self.describes.get(&item.id) {
395 Some(ids) => ids.contains(&primary),
396 // A file with one Exif item and no reference is common enough to accept: there is
397 // nothing else it could describe.
398 None => true,
399 };
400 if !describes_primary {
401 continue;
402 }
403 let span = match item.extents.first() {
404 Some(e) => *e,
405 None => continue,
406 };
407 let base = match item.place {
408 Where::File => 0,
409 Where::Idat => match self.idat {
410 Some(idat) => idat.off,
411 None => continue,
412 },
413 Where::Item => continue,
414 };
415 let whole = res!(self.slice(Extent { off: base + span.off, len: span.len }));
416 if whole.len() < 4 {
417 return Err(err!(
418 "The Exif item is {} bytes, which is shorter than its own header.", whole.len();
419 Invalid, Input, Decode));
420 }
421 let skip = 4 + u32::from_be_bytes([whole[0], whole[1], whole[2], whole[3]]) as usize;
422 if skip > whole.len() {
423 return Err(err!(
424 "The Exif item's header points {} bytes into a block of {}.", skip, whole.len();
425 Invalid, Input, Decode));
426 }
427 return Ok(Some(&whole[skip..]));
428 }
429 Ok(None)
430 }
431
432 // ------------------------------------------------------------------ the reading itself
433
434 /// Reads the boxes under `meta` that need the whole file in hand.
435 fn read_meta(&mut self, whole: bool) -> Outcome<()> {
436 let bytes = self.bytes;
437 let mut items: Vec<Item> = Vec::new();
438 let mut primary: Option<u32> = None;
439 let mut props: Vec<Prop> = Vec::new();
440 let mut owned: BTreeMap<u32, Vec<u16>> = BTreeMap::new();
441 let mut derived: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
442 let mut describes: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
443 let mut places: BTreeMap<u32, (Where, Vec<Extent>)> = BTreeMap::new();
444 let mut idat: Option<Extent> = None;
445 res!(walk_from(bytes, 0, bytes.len(), 0, whole, &mut |kind, body| {
446 match &kind {
447 b"meta" => Ok(Walk::Into(4)),
448 b"iprp" => Ok(Walk::Into(0)),
449 // Read whole rather than descended into: every box inside `ipco` is a property
450 // and its index is its position, so a walker that let them fall through with the
451 // rest of the file would number them by what else it had met on the way.
452 b"ipco" => {
453 props = res!(read_ipco(bytes, body));
454 Ok(Walk::Over)
455 },
456 b"pitm" => {
457 primary = Some(res!(read_pitm(bytes, body)));
458 Ok(Walk::Over)
459 },
460 b"iinf" => {
461 items = res!(read_iinf(bytes, body));
462 Ok(Walk::Over)
463 },
464 b"iref" => {
465 res!(read_iref(bytes, body, &mut derived, &mut describes));
466 Ok(Walk::Over)
467 },
468 b"ipma" => {
469 res!(read_ipma(bytes, body, &mut owned));
470 Ok(Walk::Over)
471 },
472 b"iloc" => {
473 places = res!(read_iloc(bytes, body));
474 Ok(Walk::Over)
475 },
476 b"idat" => {
477 idat = Some(body);
478 Ok(Walk::Over)
479 },
480 _ => Ok(Walk::Over),
481 }
482 }));
483 let primary = match primary {
484 Some(id) => id,
485 None => return Err(err!(
486 "The metadata box names no primary item, so there is no picture to show.";
487 Invalid, Input, Missing)),
488 };
489 for item in &mut items {
490 item.primary = item.id == primary;
491 if let Some((place, extents)) = places.remove(&item.id) {
492 item.place = place;
493 item.extents = extents;
494 }
495 }
496 self.items = items;
497 self.primary = primary;
498 self.props = props;
499 self.owned = owned;
500 self.derived = derived;
501 self.describes = describes;
502 self.idat = idat;
503 Ok(())
504 }
505
506 /// Refuses a file whose parts do not agree with each other.
507 ///
508 /// Each of these has been met in the wild, and each yields a picture that looks like a decoder
509 /// fault when it is really a reader that trusted the file.
510 fn check(&self) -> Outcome<()> {
511 if self.items.len() > MAX_ITEMS {
512 return Err(err!(
513 "The file describes {} items, and {} is the most this reader will read.",
514 self.items.len(), MAX_ITEMS;
515 Invalid, Input, TooBig));
516 }
517 let _ = res!(self.primary());
518 for item in &self.items {
519 let limit = match item.place {
520 Where::File => self.bytes.len() as u64,
521 Where::Idat => match self.idat {
522 Some(span) => span.len,
523 None if item.extents.is_empty() => continue,
524 None => return Err(err!(
525 "Item {} is placed in the item data box, and the file has none.", item.id;
526 Invalid, Input, Missing)),
527 },
528 Where::Item => continue,
529 };
530 for span in &item.extents {
531 let end = span.off.saturating_add(span.len);
532 if end > limit {
533 return Err(err!(
534 "Item {} claims bytes {} to {} of a {} the file ends at {}.",
535 item.id, span.off, end,
536 match item.place { Where::Idat => "region", _ => "file" }, limit;
537 Invalid, Input, Decode));
538 }
539 }
540 }
541 Ok(())
542 }
543
544 /// The properties associated with one item, in association order.
545 fn props_of(&self, item: u32) -> Vec<&Prop> {
546 let mut out = Vec::new();
547 if let Some(indices) = self.owned.get(&item) {
548 for i in indices {
549 // `ipma` indexes from one, and zero means "no property".
550 if *i > 0 {
551 if let Some(prop) = self.props.get(*i as usize - 1) {
552 out.push(prop);
553 }
554 }
555 }
556 }
557 out
558 }
559
560 /// One item's extent in pixels, out of its `ispe` property.
561 fn extent_of(&self, item: u32) -> Outcome<(u32, u32)> {
562 for prop in self.props_of(item) {
563 if let Prop::Extent { w, h } = prop {
564 return Ok((*w, *h));
565 }
566 }
567 Err(err!(
568 "Item {} carries no extent, so there is no telling how big it is.", item;
569 Invalid, Input, Missing))
570 }
571
572 /// The geometry of a `grid` item, out of the sixteen bytes it holds.
573 ///
574 /// ISO/IEC 23008-12 §6.6.2.3.2: a version, flags whose low bit chooses between sixteen- and
575 /// thirty-two-bit extents, the counts less one, and then the assembled width and height.
576 fn grid(&self, item: u32) -> Outcome<Grid> {
577 let data = res!(self.data(item));
578 if data.len() < 8 {
579 return Err(err!(
580 "A grid is described in {} bytes, and the shortest legal one is eight.", data.len();
581 Invalid, Input, Decode));
582 }
583 let wide = data[1] & 1 == 1;
584 // Rows first, then columns (ISO/IEC 23008-12 §6.6.2.3.2). The two were the other way round
585 // here until something assembled a grid rather than merely counting its tiles: a
586 // photograph 3,088 wide out of 512-sample tiles needs seven across and five down, and
587 // reading them swapped gave five across and seven down -- which counts to the same
588 // thirty-five tiles, so the check that a grid names as many tiles as it has rows times
589 // columns passed all along.
590 let rows = data[2] as u16 + 1;
591 let cols = data[3] as u16 + 1;
592 let (width, height) = if wide {
593 if data.len() < 12 {
594 return Err(err!(
595 "A grid says its extent is thirty-two bits wide and gives {} bytes for it.",
596 data.len();
597 Invalid, Input, Decode));
598 }
599 (
600 u32::from_be_bytes([data[4], data[5], data[6], data[7]]),
601 u32::from_be_bytes([data[8], data[9], data[10], data[11]]),
602 )
603 } else {
604 (
605 u16::from_be_bytes([data[4], data[5]]) as u32,
606 u16::from_be_bytes([data[6], data[7]]) as u32,
607 )
608 };
609 if width == 0 || height == 0 {
610 return Err(err!(
611 "A grid assembles to {} by {} pixels.", width, height; Invalid, Input, Decode));
612 }
613 Ok(Grid { rows, cols, width, height })
614 }
615
616 /// A span of the file, refused rather than truncated where it runs past the end.
617 fn slice(&self, span: Extent) -> Outcome<&'a [u8]> {
618 let off = span.off as usize;
619 let end = match off.checked_add(span.len as usize) {
620 Some(end) => end,
621 None => return Err(err!(
622 "A span at {} of length {} overflows.", span.off, span.len; Invalid, Input, Decode)),
623 };
624 if end > self.bytes.len() {
625 return Err(err!(
626 "A span ends at byte {} of a file of {}. A file read in part cannot give up its \
627 items, only its metadata.", end, self.bytes.len();
628 Invalid, Input, Decode));
629 }
630 Ok(&self.bytes[off..end])
631 }
632}
633
634// ---------------------------------------------------------------------------- the box walk
635
636/// What a walker wants done with the box it was just handed.
637enum Walk {
638 Over, // step over it, whatever it holds
639 Into(usize), // walk its children, after this many bytes of its own header
640}
641
642/// Walks a run of boxes, handing each one's type and the span of its body to a visitor.
643///
644/// The visitor decides what is descended into, which is what keeps the caller's reading of one box
645/// beside its own knowledge of what that box contains. Every box is length-prefixed and the lengths
646/// have to tile the parent exactly; a box that claims to end beyond its parent is a malformed file
647/// and not a box to be clamped, since clamping turns one wrong length into a plausible-looking
648/// picture.
649fn walk<F>(bytes: &[u8], from: usize, to: usize, depth: usize, visit: &mut F) -> Outcome<()>
650where
651 F: FnMut([u8; 4], Extent) -> Outcome<Walk>,
652{
653 walk_from(bytes, from, to, depth, true, visit)
654}
655
656/// The same walk, with the choice of whether the run has to be tiled exactly.
657///
658/// `whole` is false when the caller holds the front of a file rather than the file: the last box
659/// then legitimately runs past the end of what was read, and the walk stops there instead of
660/// calling the file malformed. Everything inside a box that *is* complete is still checked, so a
661/// truncated `meta` is a fault either way.
662fn walk_from<F>(bytes: &[u8], from: usize, to: usize, depth: usize, whole: bool, visit: &mut F)
663 -> Outcome<()>
664where
665 F: FnMut([u8; 4], Extent) -> Outcome<Walk>,
666{
667 if depth > MAX_DEPTH {
668 return Err(err!(
669 "The box tree nests more than {} deep, which no legal file does.", MAX_DEPTH;
670 Invalid, Input, Decode));
671 }
672 if to > bytes.len() {
673 return Err(err!(
674 "A box tree was asked for out to byte {} of a file of {}.", to, bytes.len();
675 Invalid, Input, Decode));
676 }
677 let mut at = from;
678 while at + 8 <= to {
679 let size = u32::from_be_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]]);
680 let mut kind = [0u8; 4];
681 kind.copy_from_slice(&bytes[at + 4..at + 8]);
682 let (size, head) = match size {
683 // A size of one means the real one is the next eight bytes (ISO/IEC 14496-12 §4.2).
684 1 => {
685 if at + 16 > to {
686 return Err(err!(
687 "A box says its length is sixty-four bits and the file ends inside it.";
688 Invalid, Input, Decode));
689 }
690 let mut wide = [0u8; 8];
691 wide.copy_from_slice(&bytes[at + 8..at + 16]);
692 (u64::from_be_bytes(wide), 16usize)
693 },
694 // A size of zero means the box runs to the end of its parent.
695 0 => ((to - at) as u64, 8usize),
696 n => (n as u64, 8usize),
697 };
698 let size = size as usize;
699 if !whole && depth == 0 && size >= head && at.saturating_add(size) > to {
700 // The front of a file, ending inside a box nobody asked for.
701 return Ok(());
702 }
703 if size < head || at.saturating_add(size) > to {
704 return Err(err!(
705 "A {} box at byte {} says it is {} bytes long, and its parent ends at {}.",
706 String::from_utf8_lossy(&kind), at, size, to;
707 Invalid, Input, Decode));
708 }
709 let body = Extent { off: (at + head) as u64, len: (size - head) as u64 };
710 match res!(visit(kind, body)) {
711 Walk::Over => {},
712 Walk::Into(skip) => {
713 let start = at + head + skip;
714 if start > at + size {
715 return Err(err!(
716 "A {} box is {} bytes long and its own header is {}.",
717 String::from_utf8_lossy(&kind), size, head + skip;
718 Invalid, Input, Decode));
719 }
720 res!(walk(bytes, start, at + size, depth + 1, visit));
721 },
722 }
723 at += size;
724 }
725 if at != to && whole {
726 return Err(err!(
727 "The boxes between bytes {} and {} leave {} over, so they do not tile it.",
728 from, to, to - at;
729 Invalid, Input, Decode));
730 }
731 Ok(())
732}
733
734/// A reader of a box body, which refuses to run off its end rather than returning a short answer.
735struct Body<'a> {
736 buf: &'a [u8],
737 base: u64, // where the file the span came from starts, for reporting spans
738 at: usize, // how far along
739}
740
741impl<'a> Body<'a> {
742
743 fn new(bytes: &'a [u8], span: Extent) -> Outcome<Self> {
744 let off = span.off as usize;
745 let end = match off.checked_add(span.len as usize) {
746 Some(end) if end <= bytes.len() => end,
747 _ => return Err(err!(
748 "A box body at {} of length {} runs past the end of a file of {}.",
749 span.off, span.len, bytes.len();
750 Invalid, Input, Decode)),
751 };
752 Ok(Self { buf: &bytes[off..end], base: span.off, at: 0 })
753 }
754
755 fn left(&self) -> usize {
756 self.buf.len().saturating_sub(self.at)
757 }
758
759 /// The next `n` bytes as an unsigned integer, most significant first, for `n` up to eight.
760 fn num(&mut self, n: usize) -> Outcome<u64> {
761 if n > 8 {
762 return Err(err!("A field of {} bytes was asked for, and eight is the widest.", n; Bug));
763 }
764 if self.left() < n {
765 return Err(err!(
766 "A box body of {} bytes ends before a field of {} at offset {}.",
767 self.buf.len(), n, self.at;
768 Invalid, Input, Decode));
769 }
770 let mut v = 0u64;
771 for _ in 0..n {
772 v = (v << 8) | self.buf[self.at] as u64;
773 self.at += 1;
774 }
775 Ok(v)
776 }
777
778 /// The version and flags of a full box.
779 fn full(&mut self) -> Outcome<(u8, u32)> {
780 let v = res!(self.num(4));
781 Ok(((v >> 24) as u8, (v & 0x00ff_ffff) as u32))
782 }
783
784 fn kind(&mut self) -> Outcome<[u8; 4]> {
785 let v = res!(self.num(4));
786 Ok((v as u32).to_be_bytes())
787 }
788
789 /// Steps over a null-terminated string, which `infe` uses for a name.
790 fn skip_string(&mut self) -> Outcome<()> {
791 while self.at < self.buf.len() {
792 let b = self.buf[self.at];
793 self.at += 1;
794 if b == 0 {
795 return Ok(());
796 }
797 }
798 Ok(())
799 }
800
801 /// The span, in the file's coordinates, of the rest of the body.
802 fn rest(&self) -> Extent {
803 Extent { off: self.base + self.at as u64, len: self.left() as u64 }
804 }
805}
806
807/// `pitm`: which item is the picture (ISO/IEC 14496-12 §8.11.4).
808fn read_pitm(bytes: &[u8], span: Extent) -> Outcome<u32> {
809 let mut b = res!(Body::new(bytes, span));
810 let (version, _) = res!(b.full());
811 // Version 0 names the item in sixteen bits and version 1 in thirty-two.
812 let width = if version == 0 { 2 } else { 4 };
813 Ok(res!(b.num(width)) as u32)
814}
815
816/// `iinf` and its `infe` children: what each item is (ISO/IEC 14496-12 §8.11.6).
817fn read_iinf(bytes: &[u8], span: Extent) -> Outcome<Vec<Item>> {
818 let mut b = res!(Body::new(bytes, span));
819 let (version, _) = res!(b.full());
820 let count = res!(b.num(if version == 0 { 2 } else { 4 })) as usize;
821 if count > MAX_ITEMS {
822 return Err(err!(
823 "The item information box lists {} entries, and {} is the most this reader will read.",
824 count, MAX_ITEMS;
825 Invalid, Input, TooBig));
826 }
827 let mut out = Vec::with_capacity(count.min(1024));
828 let entries = Extent { off: span.off + b.at as u64, len: b.left() as u64 };
829 res!(walk(bytes, entries.off as usize, (entries.off + entries.len) as usize, 0,
830 &mut |kind, body| {
831 if &kind != b"infe" {
832 return Ok(Walk::Over);
833 }
834 let mut e = res!(Body::new(bytes, body));
835 let (version, _) = res!(e.full());
836 if version < 2 {
837 // Versions 0 and 1 describe a track's items, not a picture's, and carry no type.
838 return Err(err!(
839 "An item information entry is version {}, and a picture's items are version two or \
840 later.", version;
841 Invalid, Input, Unknown));
842 }
843 let id = res!(e.num(if version == 2 { 2 } else { 4 })) as u32;
844 let _protection = res!(e.num(2));
845 let kind = res!(e.kind());
846 res!(e.skip_string());
847 out.push(Item { id, kind, primary: false, place: Where::File, extents: Vec::new() });
848 Ok(Walk::Over)
849 }));
850 Ok(out)
851}
852
853/// `iref`: what refers to what (ISO/IEC 14496-12 §8.11.12).
854///
855/// Two reference types matter here. `dimg` runs from a derivation to the items it is derived from,
856/// in order, which for a grid is its tiles in raster order. `cdsc` runs from a description -- an
857/// Exif block, an XMP packet -- to the item it describes.
858fn read_iref(
859 bytes: &[u8],
860 span: Extent,
861 derived: &mut BTreeMap<u32, Vec<u32>>,
862 describes: &mut BTreeMap<u32, Vec<u32>>,
863)
864 -> Outcome<()>
865{
866 let mut b = res!(Body::new(bytes, span));
867 let (version, _) = res!(b.full());
868 let width = if version == 0 { 2 } else { 4 };
869 let entries = Extent { off: span.off + b.at as u64, len: b.left() as u64 };
870 res!(walk(bytes, entries.off as usize, (entries.off + entries.len) as usize, 0,
871 &mut |kind, body| {
872 let mut e = res!(Body::new(bytes, body));
873 let from = res!(e.num(width)) as u32;
874 let count = res!(e.num(2)) as usize;
875 let mut to = Vec::with_capacity(count.min(1024));
876 for _ in 0..count {
877 to.push(res!(e.num(width)) as u32);
878 }
879 match &kind {
880 b"dimg" => { derived.insert(from, to); },
881 b"cdsc" => { describes.insert(from, to); },
882 _ => {},
883 }
884 Ok(Walk::Over)
885 }));
886 Ok(())
887}
888
889/// `ipma`: which properties belong to which item (ISO/IEC 23008-12 §6.5.2).
890fn read_ipma(bytes: &[u8], span: Extent, owned: &mut BTreeMap<u32, Vec<u16>>) -> Outcome<()> {
891 let mut b = res!(Body::new(bytes, span));
892 let (version, flags) = res!(b.full());
893 // The low bit of the flags says the index is fifteen bits rather than seven; the rest of the
894 // byte is the "essential" flag, which a reader that keeps every property does not need.
895 let wide = flags & 1 == 1;
896 let count = res!(b.num(4)) as usize;
897 if count > MAX_ITEMS {
898 return Err(err!(
899 "The property association box covers {} items, and {} is the most this reader will \
900 read.", count, MAX_ITEMS;
901 Invalid, Input, TooBig));
902 }
903 for _ in 0..count {
904 let id = res!(b.num(if version < 1 { 2 } else { 4 })) as u32;
905 let n = res!(b.num(1)) as usize;
906 let mut indices = Vec::with_capacity(n.min(64));
907 for _ in 0..n {
908 let raw = res!(b.num(if wide { 2 } else { 1 }));
909 let index = if wide { (raw & 0x7fff) as u16 } else { (raw & 0x7f) as u16 };
910 indices.push(index);
911 }
912 owned.insert(id, indices);
913 }
914 Ok(())
915}
916
917/// `iloc`: where each item's bytes are (ISO/IEC 14496-12 §8.11.3).
918fn read_iloc(bytes: &[u8], span: Extent) -> Outcome<BTreeMap<u32, (Where, Vec<Extent>)>> {
919 let mut b = res!(Body::new(bytes, span));
920 let (version, _) = res!(b.full());
921 let sizes = res!(b.num(1));
922 let widths = res!(b.num(1));
923 let offset_size = (sizes >> 4) as usize;
924 let length_size = (sizes & 0xf) as usize;
925 let base_size = (widths >> 4) as usize;
926 let index_size = if version == 1 || version == 2 { (widths & 0xf) as usize } else { 0 };
927 let count = res!(b.num(if version < 2 { 2 } else { 4 })) as usize;
928 if count > MAX_ITEMS {
929 return Err(err!(
930 "The item location box places {} items, and {} is the most this reader will read.",
931 count, MAX_ITEMS;
932 Invalid, Input, TooBig));
933 }
934 let mut out = BTreeMap::new();
935 for _ in 0..count {
936 let id = res!(b.num(if version < 2 { 2 } else { 4 })) as u32;
937 let place = if version == 1 || version == 2 {
938 let method = res!(b.num(2)) & 0xf;
939 match method {
940 0 => Where::File,
941 1 => Where::Idat,
942 _ => Where::Item,
943 }
944 } else {
945 Where::File
946 };
947 let _data_reference = res!(b.num(2));
948 let base = res!(b.num(base_size));
949 let extents = res!(b.num(2)) as usize;
950 let mut spans = Vec::with_capacity(extents.min(1024));
951 for _ in 0..extents {
952 if index_size > 0 {
953 let _index = res!(b.num(index_size));
954 }
955 let off = res!(b.num(offset_size));
956 let len = res!(b.num(length_size));
957 spans.push(Extent { off: base.saturating_add(off), len });
958 }
959 out.insert(id, (place, spans));
960 }
961 Ok(out)
962}
963
964/// `ipco`: the properties, in the order `ipma` indexes them from one.
965fn read_ipco(bytes: &[u8], span: Extent) -> Outcome<Vec<Prop>> {
966 let mut out = Vec::new();
967 res!(walk(bytes, span.off as usize, (span.off + span.len) as usize, 0, &mut |kind, body| {
968 if out.len() >= u16::MAX as usize {
969 return Err(err!(
970 "The property container holds more than {} properties.", u16::MAX;
971 Invalid, Input, TooBig));
972 }
973 out.push(res!(read_prop(bytes, kind, body)));
974 Ok(Walk::Over)
975 }));
976 Ok(out)
977}
978
979/// One box out of `ipco`, read into the shape its type calls for.
980fn read_prop(bytes: &[u8], kind: [u8; 4], span: Extent) -> Outcome<Prop> {
981 match &kind {
982 b"ispe" => {
983 // A full box, then the width and height (ISO/IEC 23008-12 §6.5.3).
984 let mut b = res!(Body::new(bytes, span));
985 let _ = res!(b.full());
986 let w = res!(b.num(4)) as u32;
987 let h = res!(b.num(4)) as u32;
988 Ok(Prop::Extent { w, h })
989 },
990 b"hvcC" => Ok(Prop::Config(span)),
991 b"irot" => {
992 // One byte, of which the low two bits are the quarter turns (§6.5.10).
993 let mut b = res!(Body::new(bytes, span));
994 Ok(Prop::Rotation((res!(b.num(1)) & 3) as u8))
995 },
996 b"pixi" => {
997 // A full box, a count, then one byte a channel (§6.5.6).
998 let mut b = res!(Body::new(bytes, span));
999 let _ = res!(b.full());
1000 let channels = res!(b.num(1)) as usize;
1001 let mut depths = Vec::with_capacity(channels.min(8));
1002 for _ in 0..channels {
1003 depths.push(res!(b.num(1)) as u8);
1004 }
1005 Ok(Prop::Depth(depths))
1006 },
1007 other => {
1008 let mut kept = [0u8; 4];
1009 kept.copy_from_slice(other);
1010 Ok(Prop::Other(kept, span))
1011 },
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018
1019 /// Builds a box: a big-endian length, a four-character type, and a body.
1020 fn bx(kind: &[u8; 4], body: &[u8]) -> Vec<u8> {
1021 let mut out = ((body.len() + 8) as u32).to_be_bytes().to_vec();
1022 out.extend_from_slice(kind);
1023 out.extend_from_slice(body);
1024 out
1025 }
1026
1027 /// A file holding one coded picture of a known extent, and a thumbnail written first.
1028 ///
1029 /// The thumbnail comes first on purpose: it is the shape a phone writes and the shape that
1030 /// makes a reader taking the first `ispe` it meets report the wrong size.
1031 fn one_picture() -> Vec<u8> {
1032 let mut ipco = Vec::new();
1033 // Property 1: the thumbnail's extent. Property 2: the picture's.
1034 let mut ispe_small = vec![0u8; 4];
1035 ispe_small.extend_from_slice(&512u32.to_be_bytes());
1036 ispe_small.extend_from_slice(&512u32.to_be_bytes());
1037 ipco.extend_from_slice(&bx(b"ispe", &ispe_small));
1038 let mut ispe_big = vec![0u8; 4];
1039 ispe_big.extend_from_slice(&4032u32.to_be_bytes());
1040 ispe_big.extend_from_slice(&3024u32.to_be_bytes());
1041 ipco.extend_from_slice(&bx(b"ispe", &ispe_big));
1042 ipco.extend_from_slice(&bx(b"hvcC", &[1, 2, 3, 4]));
1043 let iprp = bx(b"iprp", &bx(b"ipco", &ipco));
1044
1045 // Item 1 is the thumbnail and item 2 the picture.
1046 let mut ipma = vec![0u8; 4];
1047 ipma.extend_from_slice(&2u32.to_be_bytes());
1048 ipma.extend_from_slice(&1u16.to_be_bytes());
1049 ipma.push(1);
1050 ipma.push(1);
1051 ipma.extend_from_slice(&2u16.to_be_bytes());
1052 ipma.push(2);
1053 ipma.push(2);
1054 ipma.push(3);
1055 let ipma = bx(b"ipma", &ipma);
1056
1057 let mut infe1 = vec![2u8, 0, 0, 0];
1058 infe1.extend_from_slice(&1u16.to_be_bytes());
1059 infe1.extend_from_slice(&0u16.to_be_bytes());
1060 infe1.extend_from_slice(b"hvc1");
1061 infe1.push(0);
1062 let mut infe2 = vec![2u8, 0, 0, 0];
1063 infe2.extend_from_slice(&2u16.to_be_bytes());
1064 infe2.extend_from_slice(&0u16.to_be_bytes());
1065 infe2.extend_from_slice(b"hvc1");
1066 infe2.push(0);
1067 let mut iinf = vec![0u8; 4];
1068 iinf.extend_from_slice(&2u16.to_be_bytes());
1069 iinf.extend_from_slice(&bx(b"infe", &infe1));
1070 iinf.extend_from_slice(&bx(b"infe", &infe2));
1071 let iinf = bx(b"iinf", &iinf);
1072
1073 let mut pitm = vec![0u8; 4];
1074 pitm.extend_from_slice(&2u16.to_be_bytes());
1075 let pitm = bx(b"pitm", &pitm);
1076
1077 // Both items are placed at the front of the file, which is legal and enough for a reader
1078 // that is being checked on its metadata rather than on its pixels.
1079 let mut iloc = vec![0u8; 4];
1080 iloc.push(0x44);
1081 iloc.push(0x00);
1082 iloc.extend_from_slice(&2u16.to_be_bytes());
1083 for id in [1u16, 2u16] {
1084 iloc.extend_from_slice(&id.to_be_bytes());
1085 iloc.extend_from_slice(&0u16.to_be_bytes());
1086 iloc.extend_from_slice(&1u16.to_be_bytes());
1087 iloc.extend_from_slice(&0u32.to_be_bytes());
1088 iloc.extend_from_slice(&8u32.to_be_bytes());
1089 }
1090 let iloc = bx(b"iloc", &iloc);
1091
1092 let mut meta = vec![0u8; 4];
1093 meta.extend_from_slice(&bx(b"hdlr", &[0u8; 20]));
1094 meta.extend_from_slice(&pitm);
1095 meta.extend_from_slice(&iinf);
1096 meta.extend_from_slice(&iprp);
1097 meta.extend_from_slice(&ipma);
1098 meta.extend_from_slice(&iloc);
1099
1100 let mut out = bx(b"ftyp", b"heic\0\0\0\0mif1heic");
1101 out.extend_from_slice(&bx(b"meta", &meta));
1102 out.extend_from_slice(&bx(b"mdat", &[0u8; 64]));
1103 out
1104 }
1105
1106 #[test]
1107 fn test_the_size_is_the_primary_item_s_and_not_the_first_ispe_00() -> Outcome<()> {
1108 let file = one_picture();
1109 let heif = res!(Heif::read(&file));
1110 req!(heif.brand(), *b"heic");
1111 req!(res!(heif.size()), (4032, 3024),
1112 "The thumbnail's extent was taken for the picture's.");
1113 req!(res!(heif.primary()).id, 2);
1114 Ok(())
1115 }
1116
1117 #[test]
1118 fn test_the_configuration_record_comes_back_whole_01() -> Outcome<()> {
1119 let file = one_picture();
1120 let heif = res!(Heif::read(&file));
1121 req!(res!(heif.config(2)), &[1u8, 2, 3, 4][..]);
1122 Ok(())
1123 }
1124
1125 #[test]
1126 fn test_a_box_that_ends_past_its_parent_is_refused_02() -> Outcome<()> {
1127 let mut file = one_picture();
1128 // The `meta` box's length, made longer than the file allows.
1129 let at = match (0..file.len() - 8).find(|i| &file[i + 4..i + 8] == b"meta") {
1130 Some(at) => at,
1131 None => return Err(err!("The fixture holds no metadata box."; Test, Missing)),
1132 };
1133 let was = u32::from_be_bytes([file[at], file[at + 1], file[at + 2], file[at + 3]]);
1134 file[at..at + 4].copy_from_slice(&(was + 4096).to_be_bytes());
1135 req!(Heif::read(&file).is_err(), true, "A box overrunning the file was read as if it fit.");
1136 Ok(())
1137 }
1138
1139 #[test]
1140 fn test_a_grid_gives_the_assembled_extent_and_its_tiles_03() -> Outcome<()> {
1141 // Six tiles of 512 square assembling to 1280 by 960, which is what a phone writes and is
1142 // the case a reader that adds up its tiles gets wrong in both directions.
1143 //
1144 // **Rows before columns** (ISO/IEC 23008-12 §6.6.2.3.2). This fixture used to be written
1145 // the other way about, to match a reader that had them swapped, and the two wrongs made a
1146 // passing test. What settled it is a real photograph: 3,088 samples wide out of 512-sample
1147 // tiles needs seven across, and only one reading of the box gives seven.
1148 let mut grid = vec![0u8, 0, 1, 2];
1149 grid.extend_from_slice(&1280u16.to_be_bytes());
1150 grid.extend_from_slice(&960u16.to_be_bytes());
1151 let geometry = Grid { rows: 2, cols: 3, width: 1280, height: 960 };
1152 req!(grid.len(), 8);
1153
1154 let mut ipco = Vec::new();
1155 let mut ispe = vec![0u8; 4];
1156 ispe.extend_from_slice(&512u32.to_be_bytes());
1157 ispe.extend_from_slice(&512u32.to_be_bytes());
1158 ipco.extend_from_slice(&bx(b"ispe", &ispe));
1159 let iprp = bx(b"iprp", &bx(b"ipco", &ipco));
1160
1161 let mut iinf = vec![0u8; 4];
1162 iinf.extend_from_slice(&7u16.to_be_bytes());
1163 for id in 1u16..=6 {
1164 let mut infe = vec![2u8, 0, 0, 0];
1165 infe.extend_from_slice(&id.to_be_bytes());
1166 infe.extend_from_slice(&0u16.to_be_bytes());
1167 infe.extend_from_slice(b"hvc1");
1168 infe.push(0);
1169 iinf.extend_from_slice(&bx(b"infe", &infe));
1170 }
1171 let mut infe = vec![2u8, 0, 0, 0];
1172 infe.extend_from_slice(&7u16.to_be_bytes());
1173 infe.extend_from_slice(&0u16.to_be_bytes());
1174 infe.extend_from_slice(b"grid");
1175 infe.push(0);
1176 iinf.extend_from_slice(&bx(b"infe", &infe));
1177 let iinf = bx(b"iinf", &iinf);
1178
1179 let mut pitm = vec![0u8; 4];
1180 pitm.extend_from_slice(&7u16.to_be_bytes());
1181 let pitm = bx(b"pitm", &pitm);
1182
1183 // The grid item is derived from its six tiles, in raster order.
1184 let mut dimg = 7u16.to_be_bytes().to_vec();
1185 dimg.extend_from_slice(&6u16.to_be_bytes());
1186 for id in 1u16..=6 {
1187 dimg.extend_from_slice(&id.to_be_bytes());
1188 }
1189 let iref = bx(b"iref", &{
1190 let mut b = vec![0u8; 4];
1191 b.extend_from_slice(&bx(b"dimg", &dimg));
1192 b
1193 });
1194
1195 // The grid's own bytes live in `idat`, which is where a derivation's do.
1196 let idat = bx(b"idat", &grid);
1197 let mut iloc = vec![1u8, 0, 0, 0];
1198 iloc.push(0x44);
1199 iloc.push(0x00);
1200 iloc.extend_from_slice(&1u16.to_be_bytes());
1201 iloc.extend_from_slice(&7u16.to_be_bytes());
1202 iloc.extend_from_slice(&1u16.to_be_bytes());
1203 iloc.extend_from_slice(&0u16.to_be_bytes());
1204 iloc.extend_from_slice(&1u16.to_be_bytes());
1205 iloc.extend_from_slice(&0u32.to_be_bytes());
1206 iloc.extend_from_slice(&(grid.len() as u32).to_be_bytes());
1207 let iloc = bx(b"iloc", &iloc);
1208
1209 let mut meta = vec![0u8; 4];
1210 meta.extend_from_slice(&pitm);
1211 meta.extend_from_slice(&iinf);
1212 meta.extend_from_slice(&iref);
1213 meta.extend_from_slice(&iprp);
1214 meta.extend_from_slice(&idat);
1215 meta.extend_from_slice(&iloc);
1216 let mut file = bx(b"ftyp", b"heic\0\0\0\0mif1heic");
1217 file.extend_from_slice(&bx(b"meta", &meta));
1218
1219 let heif = res!(Heif::read(&file));
1220 req!(res!(heif.size()), (1280, 960), "A grid was measured by one of its tiles.");
1221 match res!(heif.picture()) {
1222 Picture::Tiled { grid, tiles } => {
1223 req!(grid, geometry);
1224 req!(tiles, vec![1u32, 2, 3, 4, 5, 6]);
1225 },
1226 other => return Err(err!(
1227 "A grid was read as {:?}.", other; Test, Invalid)),
1228 }
1229 Ok(())
1230 }
1231
1232 #[test]
1233 fn test_a_grid_naming_the_wrong_number_of_tiles_is_refused_04() -> Outcome<()> {
1234 // The same file as above with one tile taken out of the reference, which is the shape a
1235 // truncated copy takes and the shape that makes a decoder read past the end of its tiles.
1236 let mut grid = vec![0u8, 0, 2, 1];
1237 grid.extend_from_slice(&1280u16.to_be_bytes());
1238 grid.extend_from_slice(&960u16.to_be_bytes());
1239 let mut iinf = vec![0u8; 4];
1240 iinf.extend_from_slice(&2u16.to_be_bytes());
1241 let mut infe = vec![2u8, 0, 0, 0];
1242 infe.extend_from_slice(&1u16.to_be_bytes());
1243 infe.extend_from_slice(&0u16.to_be_bytes());
1244 infe.extend_from_slice(b"hvc1");
1245 infe.push(0);
1246 iinf.extend_from_slice(&bx(b"infe", &infe));
1247 let mut infe = vec![2u8, 0, 0, 0];
1248 infe.extend_from_slice(&7u16.to_be_bytes());
1249 infe.extend_from_slice(&0u16.to_be_bytes());
1250 infe.extend_from_slice(b"grid");
1251 infe.push(0);
1252 iinf.extend_from_slice(&bx(b"infe", &infe));
1253 let iinf = bx(b"iinf", &iinf);
1254 let mut pitm = vec![0u8; 4];
1255 pitm.extend_from_slice(&7u16.to_be_bytes());
1256 let pitm = bx(b"pitm", &pitm);
1257 let mut dimg = 7u16.to_be_bytes().to_vec();
1258 dimg.extend_from_slice(&1u16.to_be_bytes());
1259 dimg.extend_from_slice(&1u16.to_be_bytes());
1260 let iref = bx(b"iref", &{
1261 let mut b = vec![0u8; 4];
1262 b.extend_from_slice(&bx(b"dimg", &dimg));
1263 b
1264 });
1265 let idat = bx(b"idat", &grid);
1266 let mut iloc = vec![1u8, 0, 0, 0];
1267 iloc.push(0x44);
1268 iloc.push(0x00);
1269 iloc.extend_from_slice(&1u16.to_be_bytes());
1270 iloc.extend_from_slice(&7u16.to_be_bytes());
1271 iloc.extend_from_slice(&1u16.to_be_bytes());
1272 iloc.extend_from_slice(&0u16.to_be_bytes());
1273 iloc.extend_from_slice(&1u16.to_be_bytes());
1274 iloc.extend_from_slice(&0u32.to_be_bytes());
1275 iloc.extend_from_slice(&(grid.len() as u32).to_be_bytes());
1276 let iloc = bx(b"iloc", &iloc);
1277 let mut meta = vec![0u8; 4];
1278 meta.extend_from_slice(&pitm);
1279 meta.extend_from_slice(&iinf);
1280 meta.extend_from_slice(&iref);
1281 meta.extend_from_slice(&idat);
1282 meta.extend_from_slice(&iloc);
1283 let mut file = bx(b"ftyp", b"heic\0\0\0\0mif1heic");
1284 file.extend_from_slice(&bx(b"meta", &meta));
1285
1286 let heif = res!(Heif::read(&file));
1287 req!(heif.picture().is_err(), true, "A grid of six tiles was read as a grid of one.");
1288 Ok(())
1289 }
1290
1291 #[test]
1292 fn test_a_quarter_turn_exchanges_the_two_measurements_05() -> Outcome<()> {
1293 let mut file = one_picture();
1294 // An `irot` of one quarter turn, associated with the primary item. Rebuilding the whole
1295 // file is what it takes: the property has to go into `ipco` and its index into `ipma`.
1296 let mut ipco = Vec::new();
1297 let mut ispe_small = vec![0u8; 4];
1298 ispe_small.extend_from_slice(&512u32.to_be_bytes());
1299 ispe_small.extend_from_slice(&512u32.to_be_bytes());
1300 ipco.extend_from_slice(&bx(b"ispe", &ispe_small));
1301 let mut ispe_big = vec![0u8; 4];
1302 ispe_big.extend_from_slice(&4032u32.to_be_bytes());
1303 ispe_big.extend_from_slice(&3024u32.to_be_bytes());
1304 ipco.extend_from_slice(&bx(b"ispe", &ispe_big));
1305 ipco.extend_from_slice(&bx(b"hvcC", &[1, 2, 3, 4]));
1306 ipco.extend_from_slice(&bx(b"irot", &[1]));
1307 let iprp = bx(b"iprp", &bx(b"ipco", &ipco));
1308 let mut ipma = vec![0u8; 4];
1309 ipma.extend_from_slice(&1u32.to_be_bytes());
1310 ipma.extend_from_slice(&2u16.to_be_bytes());
1311 ipma.push(3);
1312 ipma.push(2);
1313 ipma.push(3);
1314 ipma.push(4);
1315 let ipma = bx(b"ipma", &ipma);
1316 let mut infe2 = vec![2u8, 0, 0, 0];
1317 infe2.extend_from_slice(&2u16.to_be_bytes());
1318 infe2.extend_from_slice(&0u16.to_be_bytes());
1319 infe2.extend_from_slice(b"hvc1");
1320 infe2.push(0);
1321 let mut iinf = vec![0u8; 4];
1322 iinf.extend_from_slice(&1u16.to_be_bytes());
1323 iinf.extend_from_slice(&bx(b"infe", &infe2));
1324 let iinf = bx(b"iinf", &iinf);
1325 let mut pitm = vec![0u8; 4];
1326 pitm.extend_from_slice(&2u16.to_be_bytes());
1327 let pitm = bx(b"pitm", &pitm);
1328 let mut meta = vec![0u8; 4];
1329 meta.extend_from_slice(&pitm);
1330 meta.extend_from_slice(&iinf);
1331 meta.extend_from_slice(&iprp);
1332 meta.extend_from_slice(&ipma);
1333 file = bx(b"ftyp", b"heic\0\0\0\0mif1heic");
1334 file.extend_from_slice(&bx(b"meta", &meta));
1335
1336 let heif = res!(Heif::read(&file));
1337 req!(res!(heif.rotation()), 1);
1338 req!(res!(heif.size()), (3024, 4032), "A quarter turn left the measurements as they were.");
1339 Ok(())
1340 }
1341}
1342
1343// ------------------------------------------------------------------- the whole photograph
1344
1345/// Decodes a HEIC file into a picture.
1346///
1347/// The whole way: the container's boxes, the coded tiles, the HEVC decoder, the assembly of the
1348/// grid and the conversion out of colour difference into red, green and blue.
1349///
1350/// **The grid is larger than the photograph.** Tiles are whole coding units and a picture is not,
1351/// so the assembled grid is rounded up to the tile size and the photograph is cropped out of its
1352/// **top left**; the `ispe` property on the grid says how big the photograph is, and that is what
1353/// comes back here.
1354///
1355/// The camera's rotation is *not* applied. A caller that shows photographs turns them by the Exif
1356/// orientation, and a phone writes both -- applying both turns a portrait twice.
1357pub fn decode(bytes: &[u8]) -> Outcome<Pixmap> {
1358 let (assembled, want) = res!(planes(bytes));
1359 let full = res!(hevc::colour::rgb(&assembled, hevc::colour::Matrix::Hd, false));
1360 // Cropped out of the top left, which is where the photograph sits in its grid.
1361 if want.0 >= full.width() && want.1 >= full.height() {
1362 return Ok(full);
1363 }
1364 crop(&full, want.0.min(full.width()), want.1.min(full.height()))
1365}
1366
1367/// The same, stopping at the coded planes: brightness and colour difference, the grid assembled but
1368/// **not** cropped, and the size the photograph says it is.
1369///
1370/// Handed out so that a caller checking the assembly against another decoder can compare what came
1371/// out of the codec rather than what came out of a colour conversion neither of them is specified
1372/// to agree about.
1373pub fn planes(bytes: &[u8]) -> Outcome<(hevc::decode::Picture, (usize, usize))> {
1374 let heif = res!(Heif::read(bytes));
1375 let picture = res!(heif.picture());
1376 Ok(match picture {
1377 Picture::One { item, size } => {
1378 let config = res!(heif.config(item));
1379 let data = res!(heif.data(item));
1380 (res!(hevc::picture(config, &data)), (size.0 as usize, size.1 as usize))
1381 },
1382 Picture::Tiled { grid, tiles } => {
1383 let first = match tiles.first() {
1384 Some(t) => *t,
1385 None => return Err(err!("A grid with no tiles in it."; Invalid, Input, Decode)),
1386 };
1387 let config = res!(heif.config(first));
1388 // One tile decoded first, to learn how big a tile is; every tile of a grid shares one
1389 // decoder configuration, so they are all this size.
1390 let data = res!(heif.data(first));
1391 let one = res!(hevc::picture(config, &data));
1392 let (tw, th) = (one.y.w, one.y.h);
1393 let (gw, gh) = (tw * grid.cols as usize, th * grid.rows as usize);
1394 let mut out = hevc::decode::Picture {
1395 y: hevc::decode::Plane::empty(gw, gh),
1396 cb: hevc::decode::Plane::empty(gw / 2, gh / 2),
1397 cr: hevc::decode::Plane::empty(gw / 2, gh / 2),
1398 depth: one.depth,
1399 };
1400 for (i, tile) in tiles.iter().enumerate() {
1401 let (col, row) = (i % grid.cols as usize, i / grid.cols as usize);
1402 if row >= grid.rows as usize {
1403 break;
1404 }
1405 let piece = if i == 0 {
1406 one.clone()
1407 } else {
1408 let data = res!(heif.data(*tile));
1409 res!(hevc::picture(res!(heif.config(*tile)), &data))
1410 };
1411 out.paste(&piece, col * tw, row * th);
1412 }
1413 (out, (grid.width as usize, grid.height as usize))
1414 },
1415 Picture::Foreign { kind, .. } => return Err(err!(
1416 "This file holds {:?}, which is not HEVC.",
1417 String::from_utf8_lossy(&kind); Unimplemented)),
1418 })
1419}
1420
1421/// The top left of a picture, at the size a photograph says it is.
1422fn crop(src: &Pixmap, w: usize, h: usize) -> Outcome<Pixmap> {
1423 let mut out = Vec::with_capacity(w * h * 4);
1424 let px = src.data();
1425 for y in 0..h {
1426 let row = y * src.width() * 4;
1427 out.extend_from_slice(&px[row..row + w * 4]);
1428 }
1429 Pixmap::from_data(w, h, out)
1430}