Skip to content

Commit 72eb355

Browse files
authored
Merge pull request #338 from mozilla/permit-multiple-colr
Add support for multiple `colr` box values per image item
2 parents fb18088 + f605b3b commit 72eb355

13 files changed

+81
-22
lines changed

mp4parse/src/boxes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl fmt::Debug for BoxType {
5151
}
5252
}
5353

54-
#[derive(Default, PartialEq, Clone)]
54+
#[derive(Default, Eq, Hash, PartialEq, Clone)]
5555
pub struct FourCC {
5656
pub value: [u8; 4],
5757
}

mp4parse/src/lib.rs

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -994,24 +994,40 @@ impl AvifContext {
994994
}
995995

996996
pub fn nclx_colour_information_ptr(&self) -> Result<*const NclxColourInformation> {
997-
match self
997+
let nclx_colr_boxes = self
998998
.item_properties
999-
.get(self.primary_item.id, BoxType::ColourInformationBox)?
1000-
{
1001-
Some(ItemProperty::Colour(ColourInformation::Nclx(nclx))) => Ok(nclx),
1002-
None | Some(ItemProperty::Colour(ColourInformation::Icc(_))) => Ok(std::ptr::null()),
1003-
Some(other_property) => panic!("property key mismatch: {:?}", other_property),
999+
.get_multiple(self.primary_item.id, |prop| {
1000+
matches!(prop, ItemProperty::Colour(ColourInformation::Nclx(_)))
1001+
})?;
1002+
1003+
match *nclx_colr_boxes.as_slice() {
1004+
[] => Ok(std::ptr::null()),
1005+
[ItemProperty::Colour(ColourInformation::Nclx(nclx)), ..] => {
1006+
if nclx_colr_boxes.len() > 1 {
1007+
warn!("Multiple nclx colr boxes, using first");
1008+
}
1009+
Ok(nclx)
1010+
}
1011+
_ => unreachable!("Expect only ColourInformation::Nclx(_) matches"),
10041012
}
10051013
}
10061014

10071015
pub fn icc_colour_information(&self) -> Result<&[u8]> {
1008-
match self
1016+
let icc_colr_boxes = self
10091017
.item_properties
1010-
.get(self.primary_item.id, BoxType::ColourInformationBox)?
1011-
{
1012-
Some(ItemProperty::Colour(ColourInformation::Icc(icc))) => Ok(icc.bytes.as_slice()),
1013-
None | Some(ItemProperty::Colour(ColourInformation::Nclx(_))) => Ok(&[]),
1014-
Some(other_property) => panic!("property key mismatch: {:?}", other_property),
1018+
.get_multiple(self.primary_item.id, |prop| {
1019+
matches!(prop, ItemProperty::Colour(ColourInformation::Icc(_, _)))
1020+
})?;
1021+
1022+
match *icc_colr_boxes.as_slice() {
1023+
[] => Ok(&[]),
1024+
[ItemProperty::Colour(ColourInformation::Icc(icc, _)), ..] => {
1025+
if icc_colr_boxes.len() > 1 {
1026+
warn!("Multiple ICC profiles in colr boxes, using first");
1027+
}
1028+
Ok(icc.bytes.as_slice())
1029+
}
1030+
_ => unreachable!("Expect only ColourInformation::Icc(_) matches"),
10151031
}
10161032
}
10171033

@@ -2140,6 +2156,11 @@ fn read_iref<T: Read>(src: &mut BMFFBox<T>) -> Result<TryVec<SingleItemTypeRefer
21402156
///
21412157
/// Note: HEIF (ISO 23008-12:2017) § 9.3.1 also defines the `iprp` box and
21422158
/// related types, but lacks additional requirements specified in 14496-12:2020.
2159+
///
2160+
/// Note: Currently HEIF (ISO 23008-12:2017) § 6.5.5.1 specifies "At most one"
2161+
/// `colr` box per item, but this is being amended in [DIS 23008-12](https://www.iso.org/standard/83650.html).
2162+
/// The new text is likely to be "At most one for a given value of `colour_type`",
2163+
/// so this implementation adheres to that language for forward compatibility.
21432164
fn read_iprp<T: Read>(
21442165
src: &mut BMFFBox<T>,
21452166
brand: FourCC,
@@ -2211,6 +2232,9 @@ fn read_iprp<T: Read>(
22112232
BoxType::ImageMirror,
22122233
];
22132234
let mut prev_transform_index = None;
2235+
// Realistically, there should only ever be 1 nclx and 1 icc
2236+
let mut colour_type_indexes: TryHashMap<FourCC, PropertyIndex> =
2237+
TryHashMap::with_capacity(2)?;
22142238

22152239
for a in &association_entry.associations {
22162240
if a.property_index == PropertyIndex(0) {
@@ -2244,6 +2268,28 @@ fn read_iprp<T: Read>(
22442268
)?;
22452269
}
22462270
}
2271+
// XXX this is contrary to the published specification; see doc comment
2272+
// at the beginning of this function for more details
2273+
ItemProperty::Colour(colr) => {
2274+
let colour_type = colr.colour_type();
2275+
if let Some(prev_colr_index) = colour_type_indexes.get(&colour_type) {
2276+
warn!(
2277+
"Multiple '{}' type colr associations with {:?}: {:?} and {:?}",
2278+
colour_type,
2279+
association_entry.item_id,
2280+
a.property_index,
2281+
prev_colr_index
2282+
);
2283+
fail_if(
2284+
strictness != ParseStrictness::Permissive,
2285+
"Each item shall have at most one property association with a
2286+
ColourInformationBox (colr) for a given value of colour_type \
2287+
per HEIF (ISO/IEC DIS 23008-12) § 6.5.5.1",
2288+
)?;
2289+
} else {
2290+
colour_type_indexes.insert(colour_type, a.property_index)?;
2291+
}
2292+
}
22472293
_ => {}
22482294
}
22492295

@@ -2378,7 +2424,10 @@ impl ItemPropertiesBox {
23782424
}
23792425

23802426
fn get(&self, item_id: ItemId, property_type: BoxType) -> Result<Option<&ItemProperty>> {
2381-
match self.get_multiple(item_id, property_type)?.as_slice() {
2427+
match self
2428+
.get_multiple(item_id, |prop| prop.box_type() == property_type)?
2429+
.as_slice()
2430+
{
23822431
&[] => Ok(None),
23832432
&[single_value] => Ok(Some(single_value)),
23842433
multiple_values => {
@@ -2395,17 +2444,15 @@ impl ItemPropertiesBox {
23952444
fn get_multiple(
23962445
&self,
23972446
item_id: ItemId,
2398-
property_type: BoxType,
2447+
filter: impl Fn(&ItemProperty) -> bool,
23992448
) -> Result<TryVec<&ItemProperty>> {
24002449
let mut values = TryVec::new();
24012450
for entry in &self.association_entries {
24022451
for a in &entry.associations {
24032452
if entry.item_id == item_id {
24042453
match self.properties.get(&a.property_index) {
24052454
Some(ItemProperty::Unsupported(_)) => {}
2406-
Some(property) if property.box_type() == property_type => {
2407-
values.push(property)?
2408-
}
2455+
Some(property) if filter(property) => values.push(property)?,
24092456
_ => {}
24102457
}
24112458
}
@@ -2809,7 +2856,16 @@ impl fmt::Debug for IccColourInformation {
28092856
#[derive(Debug)]
28102857
pub enum ColourInformation {
28112858
Nclx(NclxColourInformation),
2812-
Icc(IccColourInformation),
2859+
Icc(IccColourInformation, FourCC),
2860+
}
2861+
2862+
impl ColourInformation {
2863+
fn colour_type(&self) -> FourCC {
2864+
match self {
2865+
Self::Nclx(_) => FourCC::from(*b"nclx"),
2866+
Self::Icc(_, colour_type) => colour_type.clone(),
2867+
}
2868+
}
28132869
}
28142870

28152871
/// Parse colour information
@@ -2853,9 +2909,12 @@ fn read_colr<T: Read>(
28532909
full_range_flag,
28542910
}))
28552911
}
2856-
b"rICC" | b"prof" => Ok(ColourInformation::Icc(IccColourInformation {
2857-
bytes: src.read_into_try_vec()?,
2858-
})),
2912+
b"rICC" | b"prof" => Ok(ColourInformation::Icc(
2913+
IccColourInformation {
2914+
bytes: src.read_into_try_vec()?,
2915+
},
2916+
FourCC::from(colour_type),
2917+
)),
28592918
_ => {
28602919
error!("read_colr colour_type: {:?}", colour_type);
28612920
Err(Error::InvalidData(
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
883 Bytes
Binary file not shown.
883 Bytes
Binary file not shown.
314 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)