Extract info related to gain maps and the primary item id.
Change-Id: I53ca31a136e4ed1f78f2b70de56ca5117d3df3cb
diff --git a/README.md b/README.md
index dfb67c6..fe27e5d 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# AVIF-info
**libavifinfo** is a standalone library that can be used to extract the width,
-height, bit depth and number of channels from an AVIF payload.
+height, bit depth, number of channels and other metadata from an AVIF payload.
See `avifinfo.h` for details on the API and `avifinfo.c` for the implementation.
See `avifinfo_test.cc` for usage examples.
@@ -78,6 +78,8 @@
## PHP implementation
+The PHP implementation of libavifinfo is a subset of the C API.
+
`libavifinfo` was [implemented](https://github.com/php/php-src/pull/7711) into
**php-src** natively and is available through `getimagesize()` at head. If it is
not available in the PHP release version you use, you can fallback to
diff --git a/avifinfo.c b/avifinfo.c
index 90e02bf..977ff82 100644
--- a/avifinfo.c
+++ b/avifinfo.c
@@ -94,6 +94,12 @@
#define AVIFINFO_CHECK_NOT_FOUND(check_status) \
AVIFINFO_CHECK_STATUS_IS((check_status), kNotFound)
+#if defined(AVIFINFO_ENABLE_DEBUG_LOG)
+#define AVIF_DEBUG_LOG(...) printf(__VA_ARGS__)
+#else
+#define AVIF_DEBUG_LOG(...)
+#endif
+
//------------------------------------------------------------------------------
// Streamed input struct and helper functions.
@@ -102,6 +108,7 @@
read_stream_t read; // Used to fetch more bytes from the 'stream'.
skip_stream_t skip; // Used to advance the position in the 'stream'.
// Fallback to 'read' if 'skip' is null.
+ uint64_t num_read_bytes; // Number of bytes read or skipped.
} AvifInfoInternalStream;
// Reads 'num_bytes' from the 'stream'. They are available at '*data'.
@@ -110,6 +117,7 @@
AvifInfoInternalStream* stream, uint32_t num_bytes, const uint8_t** data) {
*data = stream->read(stream->stream, num_bytes);
AVIFINFO_CHECK(*data != NULL, kTruncated);
+ stream->num_read_bytes += num_bytes;
return kFound;
}
@@ -128,6 +136,7 @@
return AvifInfoInternalRead(stream, num_bytes, &unused);
}
stream->skip(stream->stream, num_bytes);
+ stream->num_read_bytes += num_bytes;
}
return kFound;
}
@@ -157,7 +166,14 @@
typedef struct {
uint8_t has_primary_item; // True if "pitm" was parsed.
- uint8_t has_alpha; // True if an alpha "auxC" was parsed.
+ // Start location of the primary item id in the stream, in bytes.
+ uint64_t primary_item_id_location;
+ // Number of bytes used for the primary item id.
+ uint8_t primary_item_id_bytes;
+ uint8_t has_alpha; // True if an alpha "auxC" was parsed.
+ uint8_t has_gainmap; // True if a gain map "auxC" was parsed.
+ // Index of the gain map auxC property.
+ uint8_t gainmap_property_index;
uint8_t primary_item_id;
AvifInfoFeatures primary_item_features; // Deduced from the data below.
uint8_t data_was_skipped; // True if some loops/indices were skipped.
@@ -175,6 +191,20 @@
// Generates the features of a given 'target_item_id' from internal features.
static AvifInfoInternalStatus AvifInfoInternalGetItemFeatures(
AvifInfoInternalFeatures* f, uint32_t target_item_id, uint32_t tile_depth) {
+ f->primary_item_features.has_gainmap = f->has_gainmap;
+ f->primary_item_features.primary_item_id_location =
+ f->primary_item_id_location;
+ f->primary_item_features.primary_item_id_bytes = f->primary_item_id_bytes;
+
+ // Find the gain map's item id.
+ if (f->gainmap_property_index > 0) {
+ for (uint32_t prop_item = 0; prop_item < f->num_props; ++prop_item) {
+ if (f->props[prop_item].property_index == f->gainmap_property_index) {
+ f->primary_item_features.gainmap_item_id = f->props[prop_item].item_id;
+ }
+ }
+ }
+
for (uint32_t prop_item = 0; prop_item < f->num_props; ++prop_item) {
if (f->props[prop_item].item_id != target_item_id) continue;
const uint32_t property_index = f->props[prop_item].property_index;
@@ -251,8 +281,9 @@
// 'num_remaining_bytes' is the remaining size of the container of the 'box'
// (either the file size itself or the content size of the parent of the 'box').
static AvifInfoInternalStatus AvifInfoInternalParseBox(
- AvifInfoInternalStream* stream, uint32_t num_remaining_bytes,
- uint32_t* num_parsed_boxes, AvifInfoInternalBox* box) {
+ int nesting_level, AvifInfoInternalStream* stream,
+ uint32_t num_remaining_bytes, uint32_t* num_parsed_boxes,
+ AvifInfoInternalBox* box) {
const uint8_t* data;
// See ISO/IEC 14496-12:2012(E) 4.2
uint32_t box_header_size = 8; // box 32b size + 32b type (at least)
@@ -309,6 +340,8 @@
// Instead of considering this file as invalid, skip unparsable boxes.
if (!is_parsable) memcpy(box->type, "\0skp", 4); // \0 so not a valid type
}
+ AVIF_DEBUG_LOG("%*c", nesting_level * 2, ' ');
+ AVIF_DEBUG_LOG("Box type %.4s size %d\n", box->type, box->size);
return kFound;
}
@@ -317,15 +350,16 @@
// Parses a 'stream' of an "ipco" box into 'features'.
// "ispe" is used for width and height, "pixi" and "av1C" are used for bit depth
// and number of channels, and "auxC" is used for alpha.
-static AvifInfoInternalStatus ParseIpco(AvifInfoInternalStream* stream,
+static AvifInfoInternalStatus ParseIpco(int nesting_level,
+ AvifInfoInternalStream* stream,
uint32_t num_remaining_bytes,
uint32_t* num_parsed_boxes,
AvifInfoInternalFeatures* features) {
uint32_t box_index = 1; // 1-based index. Used for iterating over properties.
do {
AvifInfoInternalBox box;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes,
- num_parsed_boxes, &box));
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ nesting_level, stream, num_remaining_bytes, num_parsed_boxes, &box));
if (!memcmp(box.type, "ispe", 4)) {
// See ISO/IEC 23008-12:2017(E) 6.5.3.2
@@ -408,21 +442,40 @@
// at https://aomediacodec.github.io/av1-avif/#auxiliary-images
const char* kAlphaStr = "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha";
const uint32_t kAlphaStrLength = 44; // Includes terminating character.
- if (box.content_size >= kAlphaStrLength) {
+ const char* kGainmapStr = "urn:com:photo:aux:hdrgainmap";
+ const uint32_t kGainmapStrLength = 29; // Includes terminating character.
+ uint32_t num_read_bytes = 0;
+ // Check for a gain map or for an alpha plane. Start with the gain map
+ // since the identifier is shorter.
+ if (box.content_size >= kGainmapStrLength) {
const uint8_t* data;
AVIFINFO_CHECK_FOUND(
- AvifInfoInternalRead(stream, kAlphaStrLength, &data));
+ AvifInfoInternalRead(stream, kGainmapStrLength, &data));
+ num_read_bytes = kGainmapStrLength;
const char* const aux_type = (const char*)data;
- if (strcmp(aux_type, kAlphaStr) == 0) {
- // Note: It is unlikely but it is possible that this alpha plane does
- // not belong to the primary item or a tile. Ignore this issue.
- features->has_alpha = 1;
+ if (strcmp(aux_type, kGainmapStr) == 0) {
+ // Note: It is unlikely but it is possible that this gain map
+ // does not belong to the primary item or a tile. Ignore this issue.
+ features->has_gainmap = 1;
+ features->gainmap_property_index = box_index;
+ } else if (box.content_size >= kAlphaStrLength &&
+ memcmp(aux_type, kAlphaStr, kGainmapStrLength) == 0) {
+ // The beginning of the aux type matches the alpha aux type string.
+ // Check the end as well.
+ const uint8_t* data2;
+ const uint32_t kEndLength = kAlphaStrLength - kGainmapStrLength;
+ AVIFINFO_CHECK_FOUND(
+ AvifInfoInternalRead(stream, kEndLength, &data2));
+ num_read_bytes = kAlphaStrLength;
+ if (strcmp((const char*)data2, &kAlphaStr[kGainmapStrLength]) == 0) {
+ // Note: It is unlikely but it is possible that this alpha plane
+ // does not belong to the primary item or a tile. Ignore this issue.
+ features->has_alpha = 1;
+ }
}
- AVIFINFO_CHECK_FOUND(
- AvifInfoInternalSkip(stream, box.content_size - kAlphaStrLength));
- } else {
- AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size));
}
+ AVIFINFO_CHECK_FOUND(
+ AvifInfoInternalSkip(stream, box.content_size - num_read_bytes));
} else {
AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size));
}
@@ -434,18 +487,20 @@
// Parses a 'stream' of an "iprp" box into 'features'. The "ipco" box contain
// the properties which are linked to items by the "ipma" box.
-static AvifInfoInternalStatus ParseIprp(AvifInfoInternalStream* stream,
+static AvifInfoInternalStatus ParseIprp(int nesting_level,
+ AvifInfoInternalStream* stream,
uint32_t num_remaining_bytes,
uint32_t* num_parsed_boxes,
AvifInfoInternalFeatures* features) {
do {
AvifInfoInternalBox box;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes,
- num_parsed_boxes, &box));
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ nesting_level, stream, num_remaining_bytes, num_parsed_boxes, &box));
if (!memcmp(box.type, "ipco", 4)) {
- AVIFINFO_CHECK_NOT_FOUND(
- ParseIpco(stream, box.content_size, num_parsed_boxes, features));
+ AVIFINFO_CHECK_NOT_FOUND(ParseIpco(nesting_level + 1, stream,
+ box.content_size, num_parsed_boxes,
+ features));
} else if (!memcmp(box.type, "ipma", 4)) {
// See ISO/IEC 23008-12:2017(E) 9.3.2
uint32_t num_read_bytes = 4;
@@ -521,14 +576,15 @@
// The "dimg" boxes contain links between tiles and their parent items, which
// can be used to infer bit depth and number of channels for the primary item
// when the latter does not have these properties.
-static AvifInfoInternalStatus ParseIref(AvifInfoInternalStream* stream,
+static AvifInfoInternalStatus ParseIref(int nesting_level,
+ AvifInfoInternalStream* stream,
uint32_t num_remaining_bytes,
uint32_t* num_parsed_boxes,
AvifInfoInternalFeatures* features) {
do {
AvifInfoInternalBox box;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes,
- num_parsed_boxes, &box));
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ nesting_level, stream, num_remaining_bytes, num_parsed_boxes, &box));
if (!memcmp(box.type, "dimg", 4)) {
// See ISO/IEC 14496-12:2015(E) 8.11.12.2
@@ -584,18 +640,19 @@
// Parses a 'stream' of a "meta" box. It looks for the primary item ID in the
// "pitm" box and recurses into other boxes to find its 'features'.
-static AvifInfoInternalStatus ParseMeta(AvifInfoInternalStream* stream,
+static AvifInfoInternalStatus ParseMeta(int nesting_level,
+ AvifInfoInternalStream* stream,
uint32_t num_remaining_bytes,
uint32_t* num_parsed_boxes,
AvifInfoInternalFeatures* features) {
do {
AvifInfoInternalBox box;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes,
- num_parsed_boxes, &box));
-
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ nesting_level, stream, num_remaining_bytes, num_parsed_boxes, &box));
if (!memcmp(box.type, "pitm", 4)) {
// See ISO/IEC 14496-12:2015(E) 8.11.4.2
const uint32_t num_bytes_per_id = (box.version == 0) ? 2 : 4;
+ const uint64_t primary_item_id_location = stream->num_read_bytes;
const uint8_t* data;
AVIFINFO_CHECK(num_bytes_per_id <= num_remaining_bytes, kInvalid);
AVIFINFO_CHECK_FOUND(
@@ -605,14 +662,18 @@
AVIFINFO_CHECK(primary_item_id <= AVIFINFO_MAX_VALUE, kAborted);
features->has_primary_item = 1;
features->primary_item_id = primary_item_id;
+ features->primary_item_id_location = primary_item_id_location;
+ features->primary_item_id_bytes = num_bytes_per_id;
AVIFINFO_CHECK_FOUND(
AvifInfoInternalSkip(stream, box.content_size - num_bytes_per_id));
} else if (!memcmp(box.type, "iprp", 4)) {
- AVIFINFO_CHECK_NOT_FOUND(
- ParseIprp(stream, box.content_size, num_parsed_boxes, features));
+ AVIFINFO_CHECK_NOT_FOUND(ParseIprp(nesting_level + 1, stream,
+ box.content_size, num_parsed_boxes,
+ features));
} else if (!memcmp(box.type, "iref", 4)) {
- AVIFINFO_CHECK_NOT_FOUND(
- ParseIref(stream, box.content_size, num_parsed_boxes, features));
+ AVIFINFO_CHECK_NOT_FOUND(ParseIref(nesting_level + 1, stream,
+ box.content_size, num_parsed_boxes,
+ features));
} else {
AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size));
}
@@ -628,8 +689,9 @@
static AvifInfoInternalStatus ParseFtyp(AvifInfoInternalStream* stream) {
AvifInfoInternalBox box;
uint32_t num_parsed_boxes = 0;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, AVIFINFO_MAX_SIZE,
- &num_parsed_boxes, &box));
+ const int nesting_level = 0;
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ nesting_level, stream, AVIFINFO_MAX_SIZE, &num_parsed_boxes, &box));
AVIFINFO_CHECK(!memcmp(box.type, "ftyp", 4), kInvalid);
// Iterate over brands. See ISO/IEC 14496-12:2012(E) 4.3.1
AVIFINFO_CHECK(box.content_size >= 8, kInvalid); // major_brand,minor_version
@@ -653,10 +715,12 @@
AvifInfoInternalFeatures* features) {
while (1) {
AvifInfoInternalBox box;
- AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, AVIFINFO_MAX_SIZE,
- num_parsed_boxes, &box));
+ AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(
+ /*nesting_level=*/0, stream, AVIFINFO_MAX_SIZE, num_parsed_boxes,
+ &box));
if (!memcmp(box.type, "meta", 4)) {
- return ParseMeta(stream, box.content_size, num_parsed_boxes, features);
+ return ParseMeta(/*nesting_level=*/1, stream, box.content_size,
+ num_parsed_boxes, features);
} else {
AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size));
}
@@ -716,19 +780,20 @@
// Streamed input API
AvifInfoStatus AvifInfoIdentifyStream(void* stream, read_stream_t read,
- skip_stream_t skip) {
+ skip_stream_t skip) {
if (read == NULL) return kAvifInfoNotEnoughData;
AvifInfoInternalStream internal_stream;
internal_stream.stream = stream;
internal_stream.read = read;
internal_stream.skip = skip; // Fallbacks to 'read' if null.
+ internal_stream.num_read_bytes = 0;
return AvifInfoInternalConvertStatus(ParseFtyp(&internal_stream));
}
AvifInfoStatus AvifInfoGetFeaturesStream(void* stream, read_stream_t read,
- skip_stream_t skip,
- AvifInfoFeatures* features) {
+ skip_stream_t skip,
+ AvifInfoFeatures* features) {
if (features != NULL) memset(features, 0, sizeof(*features));
if (read == NULL) return kAvifInfoNotEnoughData;
@@ -736,6 +801,7 @@
internal_stream.stream = stream;
internal_stream.read = read;
internal_stream.skip = skip; // Fallbacks to 'read' if null.
+ internal_stream.num_read_bytes = 0;
uint32_t num_parsed_boxes = 0;
AvifInfoInternalFeatures internal_features;
memset(&internal_features, AVIFINFO_UNDEFINED, sizeof(internal_features));
diff --git a/avifinfo.h b/avifinfo.h
index 4c898c2..266c714 100644
--- a/avifinfo.h
+++ b/avifinfo.h
@@ -39,6 +39,14 @@
uint32_t bit_depth; // Likely 8, 10 or 12 bits per channel per pixel.
uint32_t num_channels; // Likely 1, 2, 3 or 4 channels:
// (1 monochrome or 3 colors) + (0 or 1 alpha)
+ uint8_t has_gainmap; // True if a gain map was found.
+ uint8_t gainmap_item_id; // Id of the gain map item.
+ // Start location of the primary item id, in bytes.
+ // The primary item id is a big endian number stored on bytes
+ // primary_item_id_location to primary_item_id_location+primary_item_id_bytes.
+ uint64_t primary_item_id_location;
+ // Number of bytes of the primary item id.
+ uint8_t primary_item_id_bytes;
} AvifInfoFeatures;
//------------------------------------------------------------------------------
diff --git a/tests/avifinfo_test.cc b/tests/avifinfo_test.cc
index c0cb7c7..f7fcfc2 100644
--- a/tests/avifinfo_test.cc
+++ b/tests/avifinfo_test.cc
@@ -29,9 +29,32 @@
: Data();
}
-bool AreEqual(const AvifInfoFeatures& a, const AvifInfoFeatures& b) {
- return a.width == b.width && a.height == b.height &&
- a.bit_depth == b.bit_depth && a.num_channels == b.num_channels;
+void WriteBigEndian(uint32_t value, uint32_t num_bytes, uint8_t* input) {
+ for (int i = num_bytes - 1; i >= 0; --i) {
+ input[i] = value & 0xff;
+ value >>= 8;
+ }
+}
+
+bool SetPrimaryItemIdToGainmapId(Data& input) {
+ AvifInfoFeatures f;
+ if (AvifInfoGetFeatures(input.data(), input.size(), &f) != kAvifInfoOk) {
+ return false;
+ }
+ WriteBigEndian(f.gainmap_item_id, f.primary_item_id_bytes,
+ &input[f.primary_item_id_location]);
+ return true;
+}
+
+void ExpectEqual(const AvifInfoFeatures& actual, const AvifInfoFeatures& expected) {
+ EXPECT_EQ(actual.width, expected.width);
+ EXPECT_EQ(actual.height, expected.height);
+ EXPECT_EQ(actual.bit_depth, expected.bit_depth);
+ EXPECT_EQ(actual.num_channels, expected.num_channels);
+ EXPECT_EQ(actual.has_gainmap, expected.has_gainmap);
+ EXPECT_EQ(actual.gainmap_item_id, expected.gainmap_item_id);
+ EXPECT_EQ(actual.primary_item_id_location, expected.primary_item_id_location);
+ EXPECT_EQ(actual.primary_item_id_bytes, expected.primary_item_id_bytes);
}
//------------------------------------------------------------------------------
@@ -41,12 +64,70 @@
const Data input = LoadFile("avifinfo_test_1x1.avif");
ASSERT_FALSE(input.empty());
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
- EXPECT_TRUE(AreEqual(f, {1u, 1u, 8u, 3u}));
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
+ ExpectEqual(f, {.width = 1u,
+ .height = 1u,
+ .bit_depth = 8u,
+ .num_channels = 3u,
+ .has_gainmap = 0u,
+ .primary_item_id_location = 96u,
+ .primary_item_id_bytes = 2u});
}
+TEST(AvifInfoGetTest, WithAlpha) {
+ const Data input = LoadFile("avifinfo_test_2x2_alpha.avif");
+ ASSERT_FALSE(input.empty());
+
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ AvifInfoFeatures f;
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
+ ExpectEqual(f, {.width = 2u,
+ .height = 2u,
+ .bit_depth = 8u,
+ .num_channels = 4u,
+ .has_gainmap = 0u,
+ .primary_item_id_location = 96u,
+ .primary_item_id_bytes = 2u});
+
+}
+
+TEST(AvifInfoGetTest, WithGainmap) {
+ const Data input = LoadFile("avifinfo_test_20x20_gainmap.avif");
+ ASSERT_FALSE(input.empty());
+
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ AvifInfoFeatures f;
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
+ ExpectEqual(f, {.width = 20u,
+ .height = 20u,
+ .bit_depth = 8u,
+ .num_channels = 3u,
+ .has_gainmap = 1u,
+ .gainmap_item_id = 2u,
+ .primary_item_id_location = 96u,
+ .primary_item_id_bytes = 2u});
+
+ Data gainmap = input;
+ ASSERT_TRUE(SetPrimaryItemIdToGainmapId(gainmap));
+ ASSERT_EQ(AvifInfoIdentify(gainmap.data(), gainmap.size()), kAvifInfoOk);
+ AvifInfoFeatures gainmap_f;
+ ASSERT_EQ(AvifInfoGetFeatures(gainmap.data(), gainmap.size(), &gainmap_f),
+ kAvifInfoOk);
+ // TODO(maryla-uc): find a small test file with a gainmap that is smaller
+ // than the main image.
+ ExpectEqual(gainmap_f, {.width = 20u,
+ .height = 20u,
+ .bit_depth = 8u,
+ .num_channels = 1u, // the gainmap is monochrome
+ .has_gainmap = 1u,
+ .gainmap_item_id = 2u,
+ .primary_item_id_location = 96u,
+ .primary_item_id_bytes = 2u});
+}
+
+
TEST(AvifInfoGetTest, NoPixi10b) {
// Same as above but "meta" box size is stored as 64 bits, "av1C" has
// 'high_bitdepth' set to true, "pixi" was renamed to "pixy" and "mdat" size
@@ -55,10 +136,17 @@
LoadFile("avifinfo_test_1x1_10b_nopixi_metasize64b_mdatsize0.avif");
ASSERT_FALSE(input.empty());
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
- EXPECT_TRUE(AreEqual(f, {1u, 1u, 10u, 3u}));
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
+ ExpectEqual(f, {.width = 1u,
+ .height = 1u,
+ .bit_depth = 10u,
+ .num_channels = 3u,
+ .has_gainmap = 0u,
+ .primary_item_id_location = 104u,
+ .primary_item_id_bytes = 2u});
+
}
TEST(AvifInfoGetTest, EnoughBytes) {
@@ -69,17 +157,23 @@
input.resize(std::search(input.begin(), input.end(), kMdatTag, kMdatTag + 4) -
input.begin());
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
- EXPECT_TRUE(AreEqual(f, {1u, 1u, 8u, 3u}));
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f), kAvifInfoOk);
+ ExpectEqual(f, {.width = 1u,
+ .height = 1u,
+ .bit_depth = 8u,
+ .num_channels = 3u,
+ .has_gainmap = 0u,
+ .primary_item_id_location = 96u,
+ .primary_item_id_bytes = 2u});
}
TEST(AvifInfoGetTest, Null) {
const Data input = LoadFile("avifinfo_test_1x1.avif");
ASSERT_FALSE(input.empty());
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), nullptr),
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), nullptr),
kAvifInfoOk);
}
@@ -87,10 +181,10 @@
// Negative tests
TEST(AvifInfoGetTest, Empty) {
- EXPECT_EQ(AvifInfoIdentify(nullptr, 0), kAvifInfoNotEnoughData);
+ ASSERT_EQ(AvifInfoIdentify(nullptr, 0), kAvifInfoNotEnoughData);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(nullptr, 0, &f), kAvifInfoNotEnoughData);
- EXPECT_TRUE(AreEqual(f, {0}));
+ ASSERT_EQ(AvifInfoGetFeatures(nullptr, 0, &f), kAvifInfoNotEnoughData);
+ ExpectEqual(f, {0});
}
TEST(AvifInfoGetTest, NotEnoughBytes) {
@@ -101,9 +195,9 @@
input.resize(std::search(input.begin(), input.end(), kIpmaTag, kIpmaTag + 4) -
input.begin());
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
kAvifInfoNotEnoughData);
}
@@ -114,11 +208,11 @@
const uint8_t kIspeTag[] = {'i', 's', 'p', 'e'};
std::search(input.begin(), input.end(), kIspeTag, kIspeTag + 4)[0] = 'a';
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
kAvifInfoInvalidFile);
- EXPECT_TRUE(AreEqual(f, {0}));
+ ExpectEqual(f, {0});
}
TEST(AvifInfoGetTest, MetaBoxIsTooBig) {
@@ -132,11 +226,11 @@
meta_tag[-1] = 1; // 32-bit "1" then 4-char "meta" then 64-bit size.
input.insert(meta_tag + 4, {255, 255, 255, 255, 255, 255, 255, 255});
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
+ ASSERT_EQ(AvifInfoGetFeatures(input.data(), input.size(), &f),
kAvifInfoTooComplex);
- EXPECT_TRUE(AreEqual(f, {0}));
+ ExpectEqual(f, {0});
}
TEST(AvifInfoGetTest, TooManyBoxes) {
@@ -150,22 +244,22 @@
input.insert(input.end(), kBox, kBox + kBox[3]);
}
- EXPECT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
+ ASSERT_EQ(AvifInfoIdentify(input.data(), input.size()), kAvifInfoOk);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeatures(reinterpret_cast<uint8_t*>(input.data()),
+ ASSERT_EQ(AvifInfoGetFeatures(reinterpret_cast<uint8_t*>(input.data()),
input.size() * 4, &f),
kAvifInfoTooComplex);
}
TEST(AvifInfoReadTest, Null) {
- EXPECT_EQ(AvifInfoIdentifyStream(/*stream=*/nullptr, /*read=*/nullptr,
+ ASSERT_EQ(AvifInfoIdentifyStream(/*stream=*/nullptr, /*read=*/nullptr,
/*skip=*/nullptr),
kAvifInfoNotEnoughData);
AvifInfoFeatures f;
- EXPECT_EQ(AvifInfoGetFeaturesStream(/*stream=*/nullptr, /*read=*/nullptr,
+ ASSERT_EQ(AvifInfoGetFeaturesStream(/*stream=*/nullptr, /*read=*/nullptr,
/*skip=*/nullptr, &f),
kAvifInfoNotEnoughData);
- EXPECT_TRUE(AreEqual(f, {0}));
+ ExpectEqual(f, {0});
}
//------------------------------------------------------------------------------
diff --git a/tests/avifinfo_test_20x20_gainmap.avif b/tests/avifinfo_test_20x20_gainmap.avif
new file mode 100644
index 0000000..e012b7a
--- /dev/null
+++ b/tests/avifinfo_test_20x20_gainmap.avif
Binary files differ
diff --git a/tests/avifinfo_test_2x2_alpha.avif b/tests/avifinfo_test_2x2_alpha.avif
new file mode 100644
index 0000000..c009fa9
--- /dev/null
+++ b/tests/avifinfo_test_2x2_alpha.avif
Binary files differ