Commit f7d75fa8 for libheif
commit f7d75fa8b6ba9c079537a46ba4744a1622828679
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Tue Sep 29 22:35:10 2026 +0200
Write the 'miaf' brand for HEVC images in the profiles of MIAF only
A MIAF image item has to conform to a MIAF codec profile (ISO/IEC 23000-22,
6.3). The MIAF profiles for HEVC (A.3 to A.5) list the HEVC profiles of their
images: Main, Main Still Picture, Main 10, and the format range extensions
profiles with up to 10 bits. libheif wrote 'miaf' for every HEVC image.
'miaf' is not written anymore for an HEVC image with more than 10 bits, or in
a high throughput profile or a screen content coding profile. A format range
extensions profile that MIAF does not list is accepted when it is a subset of
one that is listed, as Main 4:4:4 Intra is of Main 4:4:4 10 Intra: this is
how ITU-T H.265 defines which bitstreams a decoder of a profile can decode.
Grid and overlay images follow the images that they are made of.
MIAF also limits the images to the Main tier and to level 6. This is not
checked, since the level in 'hvcC' does not tell whether the image is within
the limit: kvazaar signals level 6.2 for every image.
diff --git a/libheif/codecs/hevc_boxes.cc b/libheif/codecs/hevc_boxes.cc
index 848b6188..f0e490ee 100644
--- a/libheif/codecs/hevc_boxes.cc
+++ b/libheif/codecs/hevc_boxes.cc
@@ -191,6 +191,81 @@ bool HEVCDecoderConfigurationRecord::is_profile_compatible(Profile profile) cons
}
+bool HEVCDecoderConfigurationRecord::is_miaf_profile() const
+{
+ // The MIAF HEVC Basic, Advanced and Extended profiles (ISO/IEC 23000-22, A.3.2, A.4.2, A.5.2)
+ // list the HEVC profiles of their images.
+ //
+ // They also limit the images to the Main tier and to level 6. This is not checked here:
+ // the encoders do not all signal the level that the image needs (kvazaar signals level 6.2
+ // for every image), so the level in 'hvcC' does not tell whether the image is within
+ // the limit.
+
+ if (general_profile_space != 0) {
+ return false;
+ }
+
+ // Main and Main Still Picture (Basic), Main 10 and Main 10 Still Picture (Advanced).
+ // A bitstream in the Main 10 Still Picture profile is also one in the Main 10 profile.
+
+ if (is_profile_compatible(Profile_Main) ||
+ is_profile_compatible(Profile_MainStillPicture) ||
+ is_profile_compatible(Profile_Main10)) {
+ return true;
+ }
+
+ if (!is_profile_compatible(Profile_RExt)) {
+ // There is no MIAF profile with the high throughput profiles, the screen content coding
+ // profiles or any other profile of HEVC.
+ return false;
+ }
+
+ // The format range extensions profiles are told apart by nine of the constraint flags.
+ // A bitstream conforms to a profile when each of its flags is greater than or equal to the
+ // flag of the profile in Table A.2 of ITU-T H.265. This way, a profile that is not listed
+ // here is accepted when it is a subset of a listed one, like Main 4:4:4 Intra, which is a
+ // subset of Main 4:4:4 10 Intra.
+
+ const int first_flag = 4; // general_max_12bit_constraint_flag
+ const int num_flags = 9;
+
+ static const bool miaf_profiles[][num_flags] = {
+ // max 12 bit, max 10 bit, max 8 bit, max 4:2:2, max 4:2:0, max monochrome,
+ // intra, one picture only, lower bit rate
+
+ // MIAF HEVC Advanced profile
+ {1, 1, 1, 1, 1, 0, 1, 0, 0}, // Main Intra
+ {1, 1, 0, 1, 1, 0, 1, 0, 0}, // Main 10 Intra
+ {1, 1, 0, 1, 0, 0, 1, 0, 0}, // Main 4:2:2 10 Intra
+
+ // MIAF HEVC Extended profile
+ {1, 1, 1, 0, 0, 0, 0, 0, 1}, // Main 4:4:4
+ {1, 1, 0, 0, 0, 0, 0, 0, 1}, // Main 4:4:4 10
+ {1, 1, 0, 0, 0, 0, 1, 0, 0}, // Main 4:4:4 10 Intra
+ {1, 1, 1, 0, 0, 0, 1, 1, 0}, // Main 4:4:4 Still Picture
+ {1, 1, 1, 1, 1, 1, 0, 0, 1}, // Monochrome
+ {1, 1, 0, 1, 1, 1, 0, 0, 1} // Monochrome 10
+ };
+
+ for (const auto& profile : miaf_profiles) {
+ bool conforms = true;
+
+ for (int i = 0; i < num_flags; i++) {
+ if (profile[i] && !general_constraint_indicator_flags[first_flag + i]) {
+ conforms = false;
+ break;
+ }
+ }
+
+ if (conforms) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
Error Box_hvcC::parse(BitstreamRange& range, const heif_security_limits* limits)
{
return m_configuration.parse(range, limits);
diff --git a/libheif/codecs/hevc_boxes.h b/libheif/codecs/hevc_boxes.h
index b77e30fb..e589ced2 100644
--- a/libheif/codecs/hevc_boxes.h
+++ b/libheif/codecs/hevc_boxes.h
@@ -84,6 +84,10 @@ struct HEVCDecoderConfigurationRecord
bool get_general_profile_compatibility_flag(int idx) const;
bool is_profile_compatible(Profile) const;
+
+ // Whether the profile is one of those that the MIAF profiles for HEVC list for their images
+ // (ISO/IEC 23000-22, A.3 to A.5). The tier and the level are not considered.
+ bool is_miaf_profile() const;
};
diff --git a/libheif/image-items/grid.cc b/libheif/image-items/grid.cc
index c7c3a7a5..01eef19e 100644
--- a/libheif/image-items/grid.cc
+++ b/libheif/image-items/grid.cc
@@ -969,6 +969,19 @@ Result<std::shared_ptr<ImageItem_Grid>> ImageItem_Grid::add_and_encode_full_grid
return griditem;
}
+bool ImageItem_Grid::is_coded_in_miaf_profile() const
+{
+ for (heif_item_id child_id : m_grid_tile_ids) {
+ auto child = get_context()->get_image(child_id, false);
+ if (child && !child->is_coded_in_miaf_profile()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
heif_brand2 ImageItem_Grid::get_compatible_brand() const
{
if (m_grid_tile_ids.empty()) { return 0; }
diff --git a/libheif/image-items/grid.h b/libheif/image-items/grid.h
index ec8bba50..39f7edf7 100644
--- a/libheif/image-items/grid.h
+++ b/libheif/image-items/grid.h
@@ -138,6 +138,8 @@ public:
heif_brand2 get_compatible_brand() const override;
+ bool is_coded_in_miaf_profile() const override;
+
protected:
Result<std::shared_ptr<Decoder>> get_decoder() const override;
diff --git a/libheif/image-items/hevc.cc b/libheif/image-items/hevc.cc
index 8789b9a0..7443ee36 100644
--- a/libheif/image-items/hevc.cc
+++ b/libheif/image-items/hevc.cc
@@ -103,6 +103,17 @@ heif_brand2 ImageItem_HEVC::get_compatible_brand() const
}
+bool ImageItem_HEVC::is_coded_in_miaf_profile() const
+{
+ auto hvcC = get_property<Box_hvcC>();
+ if (!hvcC) {
+ return false;
+ }
+
+ return hvcC->get_configuration().is_miaf_profile();
+}
+
+
Result<std::vector<uint8_t>> ImageItem_HEVC::read_bitstream_configuration_data() const
{
return m_decoder->read_bitstream_configuration_data();
diff --git a/libheif/image-items/hevc.h b/libheif/image-items/hevc.h
index 7dff5c19..b9a124e0 100644
--- a/libheif/image-items/hevc.h
+++ b/libheif/image-items/hevc.h
@@ -52,6 +52,8 @@ public:
heif_brand2 get_compatible_brand() const override;
+ bool is_coded_in_miaf_profile() const override;
+
// currently not used
void set_preencoded_hevc_image(const std::vector<uint8_t>& data);
diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h
index 4030ddde..2b3419f6 100644
--- a/libheif/image-items/image_item.h
+++ b/libheif/image-items/image_item.h
@@ -356,7 +356,12 @@ public:
// TODO: we should have a function that checks all MIAF constraints and sets the compatibility flag.
void mark_not_miaf_compatible() { m_miaf_compatible = false; }
- bool is_miaf_compatible() const { return m_miaf_compatible; }
+ bool is_miaf_compatible() const { return m_miaf_compatible && is_coded_in_miaf_profile(); }
+
+ // A MIAF image item has to conform to a MIAF codec profile (ISO/IEC 23000-22, 6.3).
+ // Whether the coded data of this image does. An image that is derived from other
+ // images answers for them.
+ virtual bool is_coded_in_miaf_profile() const { return true; }
// return 0 if we don't know the brand
virtual heif_brand2 get_compatible_brand() const { return 0; }
diff --git a/libheif/image-items/overlay.cc b/libheif/image-items/overlay.cc
index ece7bc27..92596781 100644
--- a/libheif/image-items/overlay.cc
+++ b/libheif/image-items/overlay.cc
@@ -539,6 +539,19 @@ Result<std::shared_ptr<ImageItem_Overlay>> ImageItem_Overlay::add_new_overlay_it
return iovl_image;
}
+bool ImageItem_Overlay::is_coded_in_miaf_profile() const
+{
+ for (heif_item_id child_id : m_overlay_image_ids) {
+ auto child = get_context()->get_image(child_id, false);
+ if (child && !child->is_coded_in_miaf_profile()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
heif_brand2 ImageItem_Overlay::get_compatible_brand() const
{
if (m_overlay_image_ids.empty()) { return 0; }
diff --git a/libheif/image-items/overlay.h b/libheif/image-items/overlay.h
index b48d7920..cd277385 100644
--- a/libheif/image-items/overlay.h
+++ b/libheif/image-items/overlay.h
@@ -114,6 +114,8 @@ public:
heif_brand2 get_compatible_brand() const override;
+ bool is_coded_in_miaf_profile() const override;
+
Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
heif_encoder* encoder,
const heif_encoding_options& options,
diff --git a/tests/hevc_brands.cc b/tests/hevc_brands.cc
index 26091bfe..9c72351d 100644
--- a/tests/hevc_brands.cc
+++ b/tests/hevc_brands.cc
@@ -29,6 +29,11 @@
// ('heix', 'hevx'). It has none for the other profiles of HEVC, like the high throughput
// profiles and the screen content coding profiles. libheif wrote 'heix' and 'hevx' for them.
//
+// The 'miaf' brand also depends on the profile. A MIAF image item has to conform to a MIAF
+// codec profile (ISO/IEC 23000-22, 6.3), and the MIAF profiles for HEVC (A.3 to A.5) list the
+// HEVC profiles of their images, which end at 10 bits. libheif wrote 'miaf' for all HEVC
+// images. The limits of MIAF for the tier and the level are not checked.
+//
// No encoder of libheif may be able to write these profiles, and the brands only depend
// on the profile in the 'hvcC' box. So the tests change the profile in the 'hvcC' box of a
// file and compute the brands that libheif would write for it.
@@ -36,6 +41,7 @@
#include "catch_amalgamated.hpp"
#include "libheif/heif.h"
#include "libheif/heif_sequences.h"
+#include "libheif/heif_tiling.h"
#include "brands.h"
#include "context.h"
#include "test_utils.h"
@@ -58,27 +64,72 @@ const int PROFILE_HIGH_THROUGHPUT = 5;
const int PROFILE_SCREEN_CONTENT_CODING = 9;
const int PROFILE_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING = 11;
-struct profile_brands
+struct hevc_profile
{
+ const char* name;
int profile_idc;
+
+ // The constraint flags that tell the format range extensions profiles apart (Table A.2 of
+ // ITU-T H.265), from general_max_12bit_constraint_flag to
+ // general_lower_bit_rate_constraint_flag.
+ const char* constraint_flags;
+
heif_brand2 image_brand; // 0: there is no brand for this profile
heif_brand2 sequence_brand;
+ bool is_miaf_profile;
};
-const profile_brands all_profiles[] = {
- {PROFILE_MAIN, heif_brand2_heic, heif_brand2_hevc},
- {PROFILE_MAIN_STILL_PICTURE, heif_brand2_heic, heif_brand2_hevc},
- {PROFILE_MAIN_10, heif_brand2_heix, heif_brand2_hevx},
- {PROFILE_FORMAT_RANGE_EXTENSIONS, heif_brand2_heix, heif_brand2_hevx},
- {PROFILE_HIGH_THROUGHPUT, 0, 0},
- {PROFILE_SCREEN_CONTENT_CODING, 0, 0},
- {PROFILE_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING, 0, 0}
+const char* const NO_FLAGS = "000000000";
+
+const heif_brand2 heic = heif_brand2_heic;
+const heif_brand2 heix = heif_brand2_heix;
+const heif_brand2 hevc = heif_brand2_hevc;
+const heif_brand2 hevx = heif_brand2_hevx;
+
+const hevc_profile all_profiles[] = {
+ // --- MIAF HEVC Basic profile
+ {"Main", PROFILE_MAIN, NO_FLAGS, heic, hevc, true},
+ {"Main Still Picture", PROFILE_MAIN_STILL_PICTURE, NO_FLAGS, heic, hevc, true},
+
+ // --- MIAF HEVC Advanced profile
+ {"Main 10", PROFILE_MAIN_10, NO_FLAGS, heix, hevx, true},
+ {"Main Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "111110100", heix, hevx, true},
+ {"Main 10 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "110110101", heix, hevx, true},
+ {"Main 4:2:2 10 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "110100100", heix, hevx, true},
+
+ // --- MIAF HEVC Extended profile
+ {"Main 4:4:4", PROFILE_FORMAT_RANGE_EXTENSIONS, "111000001", heix, hevx, true},
+ {"Main 4:4:4 10", PROFILE_FORMAT_RANGE_EXTENSIONS, "110000001", heix, hevx, true},
+ {"Main 4:4:4 10 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "110000100", heix, hevx, true},
+ {"Main 4:4:4 Still Picture", PROFILE_FORMAT_RANGE_EXTENSIONS, "111000110", heix, hevx, true},
+ {"Monochrome", PROFILE_FORMAT_RANGE_EXTENSIONS, "111111001", heix, hevx, true},
+ {"Monochrome 10", PROFILE_FORMAT_RANGE_EXTENSIONS, "110111001", heix, hevx, true},
+
+ // --- not listed by MIAF, but subsets of a profile that is
+ {"Main 4:4:4 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "111000101", heix, hevx, true},
+ {"Main 4:2:2 10", PROFILE_FORMAT_RANGE_EXTENSIONS, "110100001", heix, hevx, true},
+
+ // --- no MIAF profile: more than 10 bits
+ {"Main 12", PROFILE_FORMAT_RANGE_EXTENSIONS, "100110001", heix, hevx, false},
+ {"Main 12 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "100110100", heix, hevx, false},
+ {"Main 4:2:2 12", PROFILE_FORMAT_RANGE_EXTENSIONS, "100100001", heix, hevx, false},
+ {"Main 4:4:4 12 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "100000100", heix, hevx, false},
+ {"Main 4:4:4 16 Intra", PROFILE_FORMAT_RANGE_EXTENSIONS, "000000100", heix, hevx, false},
+ {"Main 4:4:4 16 Still Picture", PROFILE_FORMAT_RANGE_EXTENSIONS, "000000110", heix, hevx, false},
+ {"Monochrome 12", PROFILE_FORMAT_RANGE_EXTENSIONS, "100111001", heix, hevx, false},
+ {"Monochrome 16", PROFILE_FORMAT_RANGE_EXTENSIONS, "000111001", heix, hevx, false},
+
+ // --- profiles without a brand of their own and without a MIAF profile
+ {"High Throughput 4:4:4 16 Intra", PROFILE_HIGH_THROUGHPUT, "000000100", 0, 0, false},
+ {"Screen-Extended Main", PROFILE_SCREEN_CONTENT_CODING, "111110001", 0, 0, false},
+ {"Screen-Extended Main 4:4:4 10", PROFILE_SCREEN_CONTENT_CODING, "110000001", 0, 0, false},
+ {"Screen-Extended High Throughput 4:4:4", PROFILE_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING, "111000001", 0, 0, false}
};
-// Sets general_profile_idc in all 'hvcC' boxes of the file, and the compatibility flag
-// of this profile as the only one.
-void set_hevc_profile(std::vector<uint8_t>& file, int profile_idc)
+// Overwrites the profile in all 'hvcC' boxes of the file. The compatibility flag of the
+// profile is the only one that is set.
+void set_hevc_profile(std::vector<uint8_t>& file, const hevc_profile& profile)
{
static const uint8_t fourcc[4] = {'h', 'v', 'c', 'C'};
@@ -86,17 +137,31 @@ void set_hevc_profile(std::vector<uint8_t>& file, int profile_idc)
auto box = std::search(file.begin(), file.end(), fourcc, fourcc + 4);
while (box != file.end()) {
- REQUIRE(file.end() - box > 10);
+ REQUIRE(file.end() - box > 17);
- // HEVCDecoderConfigurationRecord: configurationVersion, then the profile
+ // HEVCDecoderConfigurationRecord: configurationVersion, then profile_tier_level
auto record = box + 4;
- record[1] = static_cast<uint8_t>((record[1] & 0xE0) | profile_idc);
+ record[1] = static_cast<uint8_t>((record[1] & 0xE0) | profile.profile_idc);
- const uint32_t compatibility_flags = uint32_t{1} << (31 - profile_idc);
+ const uint32_t compatibility_flags = uint32_t{1} << (31 - profile.profile_idc);
for (int i = 0; i < 4; i++) {
record[2 + i] = static_cast<uint8_t>(compatibility_flags >> (24 - 8 * i));
}
+ // The 48 constraint flags. The first four describe the source and are kept, the
+ // next nine are the flags of the format range extensions.
+ record[6] &= 0xF0;
+ for (int i = 7; i < 12; i++) {
+ record[i] = 0;
+ }
+
+ for (int i = 0; i < 9; i++) {
+ if (profile.constraint_flags[i] == '1') {
+ const int bit = 4 + i;
+ record[6 + bit / 8] |= static_cast<uint8_t>(0x80 >> (bit % 8));
+ }
+ }
+
num_boxes++;
box = std::search(box + 4, file.end(), fourcc, fourcc + 4);
}
@@ -137,6 +202,66 @@ heif_error write_to_vector(heif_context*, const void* data, size_t size, void* u
return heif_error{heif_error_Ok, heif_suberror_Unspecified, nullptr};
}
+std::vector<uint8_t> write_to_memory(heif_context* ctx)
+{
+ std::vector<uint8_t> file;
+ heif_writer writer{};
+ writer.writer_api_version = 1;
+ writer.write = write_to_vector;
+ heif_error err = heif_context_write(ctx, &writer, &file);
+ REQUIRE(err.code == heif_error_Ok);
+
+ return file;
+}
+
+heif_image* create_gray_image(uint32_t size, uint8_t value)
+{
+ heif_image* img = nullptr;
+ heif_error err = heif_image_create(size, size, heif_colorspace_YCbCr, heif_chroma_420, &img);
+ REQUIRE(err.code == heif_error_Ok);
+
+ for (heif_channel channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}) {
+ const uint32_t plane_size = (channel == heif_channel_Y ? size : size / 2);
+ err = heif_image_add_plane(img, channel, plane_size, plane_size, 8);
+ REQUIRE(err.code == heif_error_Ok);
+
+ size_t stride = 0;
+ uint8_t* p = heif_image_get_plane2(img, channel, &stride);
+ for (uint32_t y = 0; y < plane_size; y++) {
+ std::fill(p + y * stride, p + y * stride + plane_size, value);
+ }
+ }
+
+ return img;
+}
+
+// Two tiles next to each other.
+std::vector<uint8_t> encode_hevc_grid()
+{
+ heif_context* ctx = heif_context_alloc();
+
+ heif_encoder* encoder = nullptr;
+ heif_error err = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
+ REQUIRE(err.code == heif_error_Ok);
+
+ heif_image* tiles[2] = {create_gray_image(64, 100), create_gray_image(64, 150)};
+
+ heif_image_handle* handle = nullptr;
+ err = heif_context_encode_grid(ctx, tiles, 1, 2, encoder, nullptr, &handle);
+ INFO("encoding error: " << (err.message ? err.message : ""));
+ REQUIRE(err.code == heif_error_Ok);
+
+ std::vector<uint8_t> file = write_to_memory(ctx);
+
+ heif_image_handle_release(handle);
+ heif_image_release(tiles[0]);
+ heif_image_release(tiles[1]);
+ heif_encoder_release(encoder);
+ heif_context_free(ctx);
+
+ return file;
+}
+
std::vector<uint8_t> encode_hevc_sequence()
{
const uint16_t size = 64;
@@ -153,22 +278,7 @@ std::vector<uint8_t> encode_hevc_sequence()
REQUIRE(err.code == heif_error_Ok);
for (int frame = 0; frame < 2; frame++) {
- heif_image* img = nullptr;
- err = heif_image_create(size, size, heif_colorspace_YCbCr, heif_chroma_420, &img);
- REQUIRE(err.code == heif_error_Ok);
-
- for (heif_channel channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}) {
- const uint32_t plane_size = (channel == heif_channel_Y ? size : size / 2);
- err = heif_image_add_plane(img, channel, plane_size, plane_size, 8);
- REQUIRE(err.code == heif_error_Ok);
-
- size_t stride = 0;
- uint8_t* p = heif_image_get_plane2(img, channel, &stride);
- for (uint32_t y = 0; y < plane_size; y++) {
- std::fill(p + y * stride, p + y * stride + plane_size, static_cast<uint8_t>(100 + 50 * frame));
- }
- }
-
+ heif_image* img = create_gray_image(size, static_cast<uint8_t>(100 + 50 * frame));
heif_image_set_duration(img, 1);
err = heif_track_encode_sequence_image(track, img, encoder, nullptr);
@@ -181,12 +291,7 @@ std::vector<uint8_t> encode_hevc_sequence()
err = heif_track_encode_end_of_sequence(track, encoder);
REQUIRE(err.code == heif_error_Ok);
- std::vector<uint8_t> file;
- heif_writer writer{};
- writer.writer_api_version = 1;
- writer.write = write_to_vector;
- err = heif_context_write(ctx, &writer, &file);
- REQUIRE(err.code == heif_error_Ok);
+ std::vector<uint8_t> file = write_to_memory(ctx);
heif_track_release(track);
heif_encoder_release(encoder);
@@ -195,32 +300,49 @@ std::vector<uint8_t> encode_hevc_sequence()
return file;
}
-} // namespace
-
-TEST_CASE("brand of an HEVC image follows its profile")
+void check_image_brands(const std::vector<uint8_t>& original)
{
- std::ifstream istr(tests_data_directory + "/rainbow-451x461.heic", std::ios::binary);
- REQUIRE(istr.good());
- const std::vector<uint8_t> original{std::istreambuf_iterator<char>(istr), std::istreambuf_iterator<char>()};
-
- for (const profile_brands& profile : all_profiles) {
- INFO("general_profile_idc " << profile.profile_idc);
+ for (const hevc_profile& profile : all_profiles) {
+ INFO("profile: " << profile.name);
std::vector<uint8_t> file = original;
- set_hevc_profile(file, profile.profile_idc);
+ set_hevc_profile(file, profile);
const Brands brands = brands_for(file);
CHECK(brands.has(heif_brand2_mif1));
CHECK(brands.has(heif_brand2_heic) == (profile.image_brand == heif_brand2_heic));
CHECK(brands.has(heif_brand2_heix) == (profile.image_brand == heif_brand2_heix));
+ CHECK(brands.has(heif_brand2_miaf) == profile.is_miaf_profile);
// Without a brand for the image, what remains is the structural brand.
CHECK(brands.main_brand == (profile.image_brand ? profile.image_brand : heif_brand2_mif1));
}
}
+} // namespace
+
+
+TEST_CASE("brands of an HEVC image follow its profile")
+{
+ std::ifstream istr(tests_data_directory + "/rainbow-451x461.heic", std::ios::binary);
+ REQUIRE(istr.good());
+ const std::vector<uint8_t> file{std::istreambuf_iterator<char>(istr), std::istreambuf_iterator<char>()};
+
+ check_image_brands(file);
+}
+
+
+TEST_CASE("brands of an HEVC grid image follow the profile of its tiles")
+{
+ if (!heif_have_encoder_for_format(heif_compression_HEVC)) {
+ SKIP("no HEVC encoder available");
+ }
+
+ check_image_brands(encode_hevc_grid());
+}
+
TEST_CASE("brand of an HEVC image sequence follows its profile")
{
@@ -230,11 +352,11 @@ TEST_CASE("brand of an HEVC image sequence follows its profile")
const std::vector<uint8_t> original = encode_hevc_sequence();
- for (const profile_brands& profile : all_profiles) {
- INFO("general_profile_idc " << profile.profile_idc);
+ for (const hevc_profile& profile : all_profiles) {
+ INFO("profile: " << profile.name);
std::vector<uint8_t> file = original;
- set_hevc_profile(file, profile.profile_idc);
+ set_hevc_profile(file, profile);
const Brands brands = brands_for(file);