From a6a2752a1791e84aa61c6b49c799a5111d6c82ab Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Wed, 7 Aug 2024 15:59:50 +0200 Subject: [PATCH 1/3] AVIF_ENABLE_EXPERIMENTAL_MINI instead of METAV1 Update the experimental prototype implementation to the latest specification draft. --- .../workflows/ci-linux-static-old-local.yml | 2 +- .github/workflows/ci-unix-shared-local.yml | 2 +- .github/workflows/ci-unix-static.yml | 2 +- .github/workflows/ci-windows-installed.yml | 2 +- .github/workflows/ci-windows-shared-local.yml | 2 +- .github/workflows/ci-windows.yml | 2 +- CHANGELOG.md | 3 + CMakeLists.txt | 6 +- apps/avifenc.c | 10 +- include/avif/avif.h | 6 +- include/avif/internal.h | 29 +- src/exif.c | 4 +- src/read.c | 553 ++++++++++-------- src/write.c | 356 ++++++----- tests/CMakeLists.txt | 8 +- .../{avifmetav1test.cc => avifminitest.cc} | 36 +- 16 files changed, 542 insertions(+), 481 deletions(-) rename tests/gtest/{avifmetav1test.cc => avifminitest.cc} (86%) diff --git a/.github/workflows/ci-linux-static-old-local.yml b/.github/workflows/ci-linux-static-old-local.yml index 4d25be6044..005f0a7087 100644 --- a/.github/workflows/ci-linux-static-old-local.yml +++ b/.github/workflows/ci-linux-static-old-local.yml @@ -53,7 +53,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_LOCAL_GTEST=ON -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_WERROR=ON - name: Build libavif (ninja) working-directory: ./build diff --git a/.github/workflows/ci-unix-shared-local.yml b/.github/workflows/ci-unix-shared-local.yml index f9aaf84ac9..9afdfd1788 100644 --- a/.github/workflows/ci-unix-shared-local.yml +++ b/.github/workflows/ci-unix-shared-local.yml @@ -57,7 +57,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_GTEST=LOCAL -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM=ON -DAVIF_ENABLE_WERROR=ON - name: Build libavif (ninja) diff --git a/.github/workflows/ci-unix-static.yml b/.github/workflows/ci-unix-static.yml index 1ced56fb3f..be93a71954 100644 --- a/.github/workflows/ci-unix-static.yml +++ b/.github/workflows/ci-unix-static.yml @@ -61,7 +61,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_GTEST=LOCAL -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM=ON -DAVIF_ENABLE_WERROR=ON - name: Build libavif (ninja) diff --git a/.github/workflows/ci-windows-installed.yml b/.github/workflows/ci-windows-installed.yml index 0c6200bf77..f3a9b9a098 100644 --- a/.github/workflows/ci-windows-installed.yml +++ b/.github/workflows/ci-windows-installed.yml @@ -75,7 +75,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_GTEST=LOCAL -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM=ON -DAVIF_ENABLE_WERROR=ON $env:AVIF_CMAKE_C_COMPILER $env:AVIF_CMAKE_CXX_COMPILER - name: Build libavif (ninja) diff --git a/.github/workflows/ci-windows-shared-local.yml b/.github/workflows/ci-windows-shared-local.yml index 85b18fe4f4..f7c6ea3eeb 100644 --- a/.github/workflows/ci-windows-shared-local.yml +++ b/.github/workflows/ci-windows-shared-local.yml @@ -55,7 +55,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_GTEST=LOCAL -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM=ON -DAVIF_ENABLE_WERROR=ON $env:AVIF_CMAKE_C_COMPILER $env:AVIF_CMAKE_CXX_COMPILER - name: Build libavif (ninja) diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 173a0fee53..fc360067e0 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -68,7 +68,7 @@ jobs: -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GTEST=ON -DAVIF_GTEST=LOCAL -DAVIF_ENABLE_EXPERIMENTAL_YCGCO_R=ON -DAVIF_ENABLE_EXPERIMENTAL_GAIN_MAP=ON - -DAVIF_ENABLE_EXPERIMENTAL_METAV1=ON + -DAVIF_ENABLE_EXPERIMENTAL_MINI=ON -DAVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM=ON -DAVIF_ENABLE_WERROR=ON - name: Build libavif (ninja) diff --git a/CHANGELOG.md b/CHANGELOG.md index c270db2aa6..8281e47656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ The changes are relative to the previous release, unless the baseline is specifi * avifenc: Allow large images to be encoded. * Fix empty CMAKE_CXX_FLAGS_RELEASE if -DAVIF_CODEC_AOM=LOCAL -DAVIF_LIBYUV=OFF is specified. https://github.com/AOMediaCodec/libavif/issues/2365. +* Renamed AVIF_ENABLE_EXPERIMENTAL_METAV1 to AVIF_ENABLE_EXPERIMENTAL_MINI and + updated the experimental reduced header feature to the latest specification + draft. ## [1.1.1] - 2024-07-30 diff --git a/CMakeLists.txt b/CMakeLists.txt index 65d11e6be0..101edcb3b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ option(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R "Enable experimental YCgCo-R matrix code option(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP "Enable experimental gain map code (for HDR images that look good both on HDR and SDR displays)" OFF ) -option(AVIF_ENABLE_EXPERIMENTAL_METAV1 "Enable experimental reduced header" OFF) +option(AVIF_ENABLE_EXPERIMENTAL_MINI "Enable experimental reduced header" OFF) option(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM "Enable experimental sample transform code" OFF) set(AVIF_PKG_CONFIG_EXTRA_LIBS_PRIVATE "") @@ -365,8 +365,8 @@ if(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) endif() -if(AVIF_ENABLE_EXPERIMENTAL_METAV1) - add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_METAV1) +if(AVIF_ENABLE_EXPERIMENTAL_MINI) + add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_MINI) endif() if(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM) diff --git a/apps/avifenc.c b/apps/avifenc.c index fde5edeab1..da5b659528 100644 --- a/apps/avifenc.c +++ b/apps/avifenc.c @@ -207,8 +207,8 @@ static void syntaxLong(void) printf(" -j,--jobs J : Number of jobs (worker threads). Use \"all\" to potentially use as many cores as possible (default: all)\n"); printf(" --no-overwrite : Never overwrite existing output file\n"); printf(" -o,--output FILENAME : Instead of using the last filename given as output, use this filename\n"); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - printf(" --metav1 : Use reduced header if possible\n"); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + printf(" --mini : Use reduced header if possible (experimental, backward-incompatible)\n"); #endif printf(" -l,--lossless : Set all defaults to encode losslessly, and emit warnings when settings/input don't allow for it\n"); printf(" -d,--depth D : Output depth [8,10,12]. (JPEG/PNG only; For y4m or stdin, depth is retained)\n"); @@ -1477,10 +1477,10 @@ int main(int argc, char * argv[]) } else if (!strcmp(arg, "-o") || !strcmp(arg, "--output")) { NEXTARG(); outputFilename = arg; -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - } else if (!strcmp(arg, "--metav1")) { +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + } else if (!strcmp(arg, "--mini")) { settings.headerFormat = AVIF_HEADER_REDUCED; -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI } else if (!strcmp(arg, "-d") || !strcmp(arg, "--depth")) { NEXTARG(); input.requestedDepth = atoi(arg); diff --git a/include/avif/avif.h b/include/avif/avif.h index 3f27a7ceb4..73055c885f 100644 --- a/include/avif/avif.h +++ b/include/avif/avif.h @@ -216,9 +216,9 @@ typedef enum avifHeaderFormat { // AVIF file with an "avif" brand, a MetaBox and all its required boxes for maximum compatibility. AVIF_HEADER_FULL, -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - // AVIF file with a "mif3" brand and a MetaBox with version 1 to reduce the encoded file size. - // This is based on the w23988 "Low-overhead image file format" MPEG proposal for HEIF. +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // AVIF file with a "mif3" brand and a MinimizedImageBox to reduce the encoded file size. + // This is based on the w24144 "Low-overhead image file format" MPEG proposal for HEIF. // WARNING: Experimental feature. Produces files that are incompatible with older decoders. AVIF_HEADER_REDUCED, #endif diff --git a/include/avif/internal.h b/include/avif/internal.h index d81e698b3b..faab398483 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h @@ -418,31 +418,6 @@ avifBool avifIsAlpha(avifItemCategory itemCategory); (AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA + AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS - 1) #endif -// --------------------------------------------------------------------------- - -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) -// HEIF pixel_format field meaning in MetaBox with version 1 -typedef enum avifMetaV1PixelFormat -{ - AVIF_METAV1_PIXEL_FORMAT_FLOAT16 = 0, // binary16 as defined by IEEE 754-2008 - AVIF_METAV1_PIXEL_FORMAT_FLOAT32 = 1, // binary32 as defined by IEEE 754-2008 - AVIF_METAV1_PIXEL_FORMAT_FLOAT64 = 2, // binary64 as defined by IEEE 754-2008 - AVIF_METAV1_PIXEL_FORMAT_UINT4 = 3, - AVIF_METAV1_PIXEL_FORMAT_UINT5 = 4, - AVIF_METAV1_PIXEL_FORMAT_UINT6 = 5, - AVIF_METAV1_PIXEL_FORMAT_UINT7 = 6, - AVIF_METAV1_PIXEL_FORMAT_UINT8 = 7, - AVIF_METAV1_PIXEL_FORMAT_UINT9 = 8, - AVIF_METAV1_PIXEL_FORMAT_UINT10 = 9, - AVIF_METAV1_PIXEL_FORMAT_UINT11 = 10, - AVIF_METAV1_PIXEL_FORMAT_UINT12 = 11, - AVIF_METAV1_PIXEL_FORMAT_UINT13 = 12, - AVIF_METAV1_PIXEL_FORMAT_UINT14 = 13, - AVIF_METAV1_PIXEL_FORMAT_UINT15 = 14, - AVIF_METAV1_PIXEL_FORMAT_UINT16 = 15, -} avifMetaV1PixelFormat; -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 - // --------------------------------------------------------------------------- // Grid AVIF images @@ -462,11 +437,11 @@ AVIF_NODISCARD avifBool avifAreGridDimensionsValid(avifPixelFormat yuvFormat, // image->imir on success. Returns AVIF_RESULT_INVALID_EXIF_PAYLOAD on failure. avifResult avifImageExtractExifOrientationToIrotImir(avifImage * image); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) // Returns the Exif orientation in [1-8] as defined in JEITA CP-3451C section 4.6.4.A Orientation // corresponding to image->irot and image->imir. uint8_t avifImageIrotImirToExifOrientation(const avifImage * image); -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI // --------------------------------------------------------------------------- // avifCodecDecodeInput diff --git a/src/exif.c b/src/exif.c index 6c386618bf..dc890602f4 100644 --- a/src/exif.c +++ b/src/exif.c @@ -142,7 +142,7 @@ avifResult avifImageExtractExifOrientationToIrotImir(avifImage * image) return AVIF_RESULT_OK; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) uint8_t avifImageIrotImirToExifOrientation(const avifImage * image) { if (!(image->transformFlags & AVIF_TRANSFORM_IROT) || image->irot.angle == 0) { @@ -188,7 +188,7 @@ uint8_t avifImageIrotImirToExifOrientation(const avifImage * image) // image->imir.axis == 1 return 5; // The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize) { diff --git a/src/read.c b/src/read.c index 04db847736..e6e79809e2 100644 --- a/src/read.c +++ b/src/read.c @@ -204,9 +204,9 @@ typedef struct avifDecoderItem avifBool hasUnsupportedEssentialProperty; // If true, this item cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so. avifBool ipmaSeen; // if true, this item already received a property association avifBool progressive; // if true, this item has progressive layers (a1lx), but does not select a specific layer (the layer_id value in lsel is set to 0xFFFF) -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - avifPixelFormat metaV1PixelFormat; // Set from the MetaBox with version 1, if present (AVIF_PIXEL_FORMAT_NONE otherwise) - avifChromaSamplePosition metaV1ChromaSamplePosition; // Set from the MetaBox with version 1, if present (AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN otherwise) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + avifPixelFormat miniBoxPixelFormat; // Set from the MinimizedImageBox, if present (AVIF_PIXEL_FORMAT_NONE otherwise) + avifChromaSamplePosition miniBoxChromaSamplePosition; // Set from the MinimizedImageBox, if present (AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN otherwise) #endif } avifDecoderItem; AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem *, item); @@ -733,9 +733,9 @@ typedef struct avifMeta // are ignored unless they refer to this item in some way (alpha plane, EXIF/XMP metadata). uint32_t primaryItemID; -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - // If true, the fields above were extracted from a MetaBox with version 1. - avifBool fromMetaV1; +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // If true, the fields above were extracted from a MinimizedImageBox. + avifBool fromMiniBox; #endif #if defined(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM) @@ -1204,15 +1204,15 @@ static avifResult avifDecoderItemValidateProperties(const avifDecoderItem * item } } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (item->metaV1PixelFormat != AVIF_PIXEL_FORMAT_NONE) { - // This is a 'meta' box with version 1. +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (item->miniBoxPixelFormat != AVIF_PIXEL_FORMAT_NONE) { + // This is a MinimizedImageBox ('mini'). - if (item->metaV1PixelFormat != avifCodecConfigurationBoxGetFormat(&configProp->u.av1C)) { + if (item->miniBoxPixelFormat != avifCodecConfigurationBoxGetFormat(&configProp->u.av1C)) { avifDiagnosticsPrintf(diag, - "Item ID %u format [%s] specified by meta box with version 1 does not match %s property format [%s]", + "Item ID %u format [%s] specified by MinimizedImageBox does not match %s property format [%s]", item->id, - avifPixelFormatToString(item->metaV1PixelFormat), + avifPixelFormatToString(item->miniBoxPixelFormat), configPropName, avifPixelFormatToString(avifCodecConfigurationBoxGetFormat(&configProp->u.av1C))); return AVIF_RESULT_BMFF_PARSE_FAILED; @@ -1223,18 +1223,18 @@ static avifResult avifDecoderItemValidateProperties(const avifDecoderItem * item // CSP_UNKNOWN - the source video transfer function must be signaled outside the AV1 bitstream // See https://aomediacodec.github.io/av1-spec/#color-config-semantics - // So item->metaV1ChromaSamplePosition can differ and will override the AV1 value. - } else if ((uint8_t)item->metaV1ChromaSamplePosition != configProp->u.av1C.chromaSamplePosition) { + // So item->miniBoxChromaSamplePosition can differ and will override the AV1 value. + } else if ((uint8_t)item->miniBoxChromaSamplePosition != configProp->u.av1C.chromaSamplePosition) { avifDiagnosticsPrintf(diag, - "Item ID %u chroma sample position [%u] specified by meta box with version 1 does not match %s property chroma sample position [%u]", + "Item ID %u chroma sample position [%u] specified by MinimizedImageBox does not match %s property chroma sample position [%u]", item->id, - (uint32_t)item->metaV1ChromaSamplePosition, + (uint32_t)item->miniBoxChromaSamplePosition, configPropName, configProp->u.av1C.chromaSamplePosition); return AVIF_RESULT_BMFF_PARSE_FAILED; } } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI if (strictFlags & AVIF_STRICT_CLAP_VALID) { const avifProperty * clapProp = avifPropertyArrayFind(&item->properties, "clap"); @@ -1703,9 +1703,9 @@ static avifResult avifDecoderFindMetadata(avifDecoder * decoder, avifMeta * meta // Advance past Annex A.2.1's header BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header"); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - // The MetaBox with version 1 does not signal the exifTiffHeaderOffset. - if (!meta->fromMetaV1) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // The MinimizedImageBox does not signal the exifTiffHeaderOffset. + if (!meta->fromMiniBox) #endif { uint32_t exifTiffHeaderOffset; @@ -2927,10 +2927,6 @@ static avifResult avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw return AVIF_RESULT_OK; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) -static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t rawOffset, uint32_t flags, avifDiagnostics * diag); -#endif - static avifResult avifParseMetaBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) { BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]"); @@ -2939,14 +2935,6 @@ static avifResult avifParseMetaBox(avifMeta * meta, uint64_t rawOffset, const ui uint32_t flags; AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, &flags), AVIF_RESULT_BMFF_PARSE_FAILED); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (version == 1) { - // Experimental box. - AVIF_CHECKRES(avifParseMetaBoxV1(&s, meta, rawOffset, flags, diag)); - return AVIF_RESULT_OK; - } -#endif - if (version != 0) { avifDiagnosticsPrintf(diag, "Box[meta]: Expecting box version 0, got version %u", version); return AVIF_RESULT_BMFF_PARSE_FAILED; @@ -3548,7 +3536,7 @@ static avifResult avifParseMovieBox(avifDecoderData * data, return AVIF_RESULT_OK; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) static avifProperty * avifMetaCreateProperty(avifMeta * meta, const char * propertyType) { avifProperty * metaProperty = avifArrayPush(&meta->properties); @@ -3565,88 +3553,104 @@ static avifProperty * avifDecoderItemAddProperty(avifDecoderItem * item, const a return itemProperty; } -static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t rawOffset, uint32_t flags, avifDiagnostics * diag) -{ - meta->fromMetaV1 = AVIF_TRUE; - - const avifBool hasAlpha = (flags & 0x000001) != 0; // has_alpha - const avifBool alphaIsPremultiplied = (flags & 0x000002) != 0; // alpha_is_premultiplied - const avifBool hasHdr = (flags & 0x000004) != 0; // has_hdr - const avifBool hasExplicitCicp = (flags & 0x000008) != 0; // has_explicit_cicp - const avifBool hasIcc = (flags & 0x000010) != 0; // has_icc - const avifBool hasExif = (flags & 0x000020) != 0; // has_exif - const avifBool hasXmp = (flags & 0x000040) != 0; // has_xmp - const avifBool fullRange = (flags & 0x000080) != 0; // full_range - const uint32_t pixelFormat = (flags & 0x000F00) >> 8; // pixel_format (4 bits) - const uint32_t chromaSubsampling = (flags & 0x003000) >> 12; // chroma_subsampling (2 bits) - const avifBool isHorizontallyCentered = (flags & 0x004000) != 0; // is_horizontally_centered - const avifBool isVerticallyCentered = (flags & 0x008000) != 0; // is_vertically_centered - const uint32_t orientation = (flags & 0x070000) >> 16; // orientation (3 bits) - const avifBool hasExplicitCodecTypes = (flags & 0x080000) != 0; // has_explicit_codec_types - const uint32_t dimensionSize = ((flags & 0x100000) != 0) ? 7 : 15; // dimension_size - const uint32_t codecConfigSizeSize = ((flags & 0x200000) != 0) ? 3 : 12; // codec_config_size_size - const uint32_t itemDataSizeSize = ((flags & 0x400000) != 0) ? 15 : 28; // item_data_size_size - const uint32_t metadataSizeSize = ((flags & 0x800000) != 0) ? 10 : 20; // metadata_size_size - - uint8_t bitDepth; - if (pixelFormat == AVIF_METAV1_PIXEL_FORMAT_UINT8) { - bitDepth = 8; - } else if (pixelFormat == AVIF_METAV1_PIXEL_FORMAT_UINT10) { - bitDepth = 10; - } else { - AVIF_CHECKERR(pixelFormat == AVIF_METAV1_PIXEL_FORMAT_UINT12, AVIF_RESULT_BMFF_PARSE_FAILED); - bitDepth = 12; - } +static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[mini]"); - uint32_t width, height; - AVIF_CHECKERR(avifROStreamReadBits(s, &width, dimensionSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(dimension_size) width_minus_one; + meta->fromMiniBox = AVIF_TRUE; + + uint32_t version; + AVIF_CHECKERR(avifROStreamReadBits(&s, &version, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(2) version; + AVIF_CHECKERR(version == 0, AVIF_RESULT_BMFF_PARSE_FAILED); + + // flags + uint32_t hasExplicitCodecTypes, floatFlag, fullRange, hasAlpha, hasExplicitCicp, hasHdr, hasIcc, hasExif, hasXmp; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasExplicitCodecTypes, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) explicit_codec_types_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &floatFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) float_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &fullRange, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) full_range_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasAlpha, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) alpha_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasExplicitCicp, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) explicit_cicp_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasHdr, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) hdr_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasIcc, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) icc_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasExif, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) exif_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &hasXmp, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) xmp_flag; + + uint32_t chromaSubsampling, orientation; + AVIF_CHECKERR(avifROStreamReadBits(&s, &chromaSubsampling, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(2) chroma_subsampling; + AVIF_CHECKERR(avifROStreamReadBits(&s, &orientation, 3), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(3) orientation_minus1; + ++orientation; + + // Spatial extents + uint32_t smallDimensionsFlag, width, height; + AVIF_CHECKERR(avifROStreamReadBits(&s, &smallDimensionsFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) small_dimensions_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &width, smallDimensionsFlag ? 7 : 15), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(small_dimensions_flag ? 7 : 15) width_minus1; ++width; - AVIF_CHECKERR(avifROStreamReadBits(s, &height, dimensionSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(dimension_size) height_minus_one; + AVIF_CHECKERR(avifROStreamReadBits(&s, &height, smallDimensionsFlag ? 7 : 15), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(small_dimensions_flag ? 7 : 15) height_minus1; ++height; - uint32_t iccDataSize = 0; - if (hasIcc) { - AVIF_CHECKERR(avifROStreamReadBits(s, &iccDataSize, metadataSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(metadata_size_size) icc_data_size_minus_one; - ++iccDataSize; + // Pixel information + uint32_t chromaIsHorizontallyCentered = 0, chromaIsVerticallyCentered = 0; + if (chromaSubsampling == 1 || chromaSubsampling == 2) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &chromaIsHorizontallyCentered, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) chroma_is_horizontally_centered; + } + if (chromaSubsampling == 1) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &chromaIsVerticallyCentered, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) chroma_is_vertically_centered; + } + + uint32_t bitDepth; + if (floatFlag) { + // bit(2) bit_depth_log2_minus4; + return AVIF_RESULT_NOT_IMPLEMENTED; + } else { + uint32_t highBitDepthFlag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &highBitDepthFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) high_bit_depth_flag; + if (highBitDepthFlag) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &bitDepth, 3), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(3) bit_depth_minus9; + bitDepth += 9; + } else { + bitDepth = 8; + } + } + + uint32_t alphaIsPremultiplied = 0; + if (hasAlpha) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &alphaIsPremultiplied, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) alpha_is_premultiplied; } + // Colour properties uint8_t colorPrimaries; uint8_t transferCharacteristics; uint8_t matrixCoefficients; if (hasExplicitCicp) { - AVIF_CHECKERR(avifROStreamReadBits8(s, &colorPrimaries, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) colour_primaries; - } else if (hasIcc) { - colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; // 2 - } else { - colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; // 1 - } - if (hasExplicitCicp) { - AVIF_CHECKERR(avifROStreamReadBits8(s, &transferCharacteristics, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) transfer_characteristics; - } else if (hasIcc) { - transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; // 2 - } else { - transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 - } - if (chromaSubsampling == 0) { - // Monochrome - matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; // 2 - } else if (hasExplicitCicp) { - AVIF_CHECKERR(avifROStreamReadBits8(s, &matrixCoefficients, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) matrix_coefficients; + AVIF_CHECKERR(avifROStreamReadBits8(&s, &colorPrimaries, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) colour_primaries; + AVIF_CHECKERR(avifROStreamReadBits8(&s, &transferCharacteristics, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) transfer_characteristics; + if (chromaSubsampling != 0) { + AVIF_CHECKERR(avifROStreamReadBits8(&s, &matrixCoefficients, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) matrix_coefficients; + } else { + matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; // 2 + } } else { - matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601; // 6 + colorPrimaries = hasIcc ? AVIF_COLOR_PRIMARIES_UNSPECIFIED // 2 + : AVIF_COLOR_PRIMARIES_BT709; // 1 + transferCharacteristics = hasIcc ? AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED // 2 + : AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 + matrixCoefficients = chromaSubsampling == 0 ? AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED // 2 + : AVIF_MATRIX_COEFFICIENTS_BT601; // 6 } + // Optional unless minor_version of FileTypeBox is a brand defining these uint8_t infeType[4]; uint8_t codecConfigType[4]; if (hasExplicitCodecTypes) { // bit(32) infe_type; for (int i = 0; i < 4; ++i) { - AVIF_CHECKERR(avifROStreamReadBits8(s, &infeType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamReadBits8(&s, &infeType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); } // bit(32) codec_config_type; for (int i = 0; i < 4; ++i) { - AVIF_CHECKERR(avifROStreamReadBits8(s, &codecConfigType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamReadBits8(&s, &codecConfigType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); } #if defined(AVIF_CODEC_AVM) if (!memcmp(infeType, "av02", 4) && !memcmp(codecConfigType, "av2C", 4)) { @@ -3660,141 +3664,167 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t memcpy(codecConfigType, "av1C", 4); } - uint32_t mainItemCodecConfigSize, mainItemDataSize; - AVIF_CHECKERR(avifROStreamReadBits(s, &mainItemCodecConfigSize, codecConfigSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(codec_config_size_size) main_item_codec_config_size; - AVIF_CHECKERR(avifROStreamReadBits(s, &mainItemDataSize, itemDataSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(item_data_size_size) main_item_data_size_minus_one; - ++mainItemDataSize; - - uint32_t alphaItemCodecConfigSize = 0, alphaItemDataSize = 0; - if (hasAlpha) { - AVIF_CHECKERR(avifROStreamReadBits(s, &alphaItemDataSize, itemDataSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(item_data_size_size) alpha_item_data_size; - if (alphaItemDataSize != 0) { - AVIF_CHECKERR(avifROStreamReadBits(s, &alphaItemCodecConfigSize, codecConfigSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(codec_config_size_size) alpha_item_codec_config_size; - } - } - + // High Dynamic Range properties + uint32_t hasGainmap = AVIF_FALSE; if (hasHdr) { - // bit(1) has_gainmap; - // bit(1) has_clli; - // bit(1) has_mdcv; - // bit(1) has_cclv; - // bit(1) has_amve; - // bit(1) has_reve; - // bit(1) has_ndwt; - // if (has_clli) + // bit(1) gainmap_flag; + // if (gainmap_flag) { + // unsigned int(small_dimensions_flag ? 7 : 15) gainmap_width_minus1; + // unsigned int(small_dimensions_flag ? 7 : 15) gainmap_height_minus1; + // bit(8) gainmap_matrix_coefficients; + // bit(1) gainmap_full_range_flag; + // bit(2) gainmap_chroma_subsampling; + // if (gainmap_chroma_subsampling == 1 || gainmap_chroma_subsampling == 2) + // bit(1) gainmap_chroma_is_horizontally_centered; + // if (gainmap_chroma_subsampling == 1) + // bit(1) gainmap_chroma_is_vertically_centered; + // bit(1) gainmap_float_flag; + // if (gainmap_float_flag) + // bit(2) gainmap_bit_depth_log2_minus4; + // else { + // bit(1) gainmap_high_bit_depth_flag; + // if (gainmap_high_bit_depth_flag) + // bit(3) gainmap_bit_depth_minus9; + // } + // bit(1) tmap_icc_flag; + // bit(1) tmap_explicit_cicp_flag; + // if (tmap_explicit_cicp_flag) { + // bit(8) tmap_colour_primaries; + // bit(8) tmap_transfer_characteristics; + // bit(8) tmap_matrix_coefficients; + // bit(1) tmap_full_range_flag; + // } + // else { + // tmap_colour_primaries = 1; + // tmap_transfer_characteristics = 13; + // tmap_matrix_coefficients = 6; + // tmap_full_range_flag = 1; + // } + // } + // bit(1) clli_flag; + // bit(1) mdcv_flag; + // bit(1) cclv_flag; + // bit(1) amve_flag; + // bit(1) reve_flag; + // bit(1) ndwt_flag; + // if (clli_flag) // ContentLightLevel clli; - // if (has_mdcv) + // if (mdcv_flag) // MasteringDisplayColourVolume mdcv; - // if (has_cclv) + // if (cclv_flag) // ContentColourVolume cclv; - // if (has_amve) + // if (amve_flag) // AmbientViewingEnvironment amve; - // if (has_reve) + // if (reve_flag) // ReferenceViewingEnvironment reve; - // if (has_ndwt) + // if (ndwt_flag) // NominalDiffuseWhite ndwt; - - // if (has_gainmap) { - // unsigned int(metadata_size_size) gainmap_metadata_size; - // unsigned int(item_data_size_size) gainmap_item_data_size; - // unsigned int(codec_config_size_size) gainmap_item_codec_config_size; - // unsigned int(dimension_size) gainmap_width_minus_one; - // unsigned int(dimension_size) gainmap_height_minus_one; - // bit(8) gainmap_matrix_coefficients; - // bit(1) gainmap_full_range; - // bit(2) gainmap_chroma_subsampling; - // bit(1) gainmap_is_horizontally_centered; - // bit(1) gainmap_is_vertically_centered; - // bit(4) gainmap_pixel_format; - - // bit(1) tmap_has_clli; - // bit(1) tmap_has_mdcv; - // bit(1) tmap_has_cclv; - // bit(1) tmap_has_amve; - // bit(1) tmap_has_reve; - // bit(1) tmap_has_ndwt; - // if (tmap_has_clli) + // if (gainmap_flag) { + // bit(1) tmap_clli_flag; + // bit(1) tmap_mdcv_flag; + // bit(1) tmap_cclv_flag; + // bit(1) tmap_amve_flag; + // bit(1) tmap_reve_flag; + // bit(1) tmap_ndwt_flag; + // if (tmap_clli_flag) // ContentLightLevel tmap_clli; - // if (tmap_has_mdcv) + // if (tmap_mdcv_flag) // MasteringDisplayColourVolume tmap_mdcv; - // if (tmap_has_cclv) + // if (tmap_cclv_flag) // ContentColourVolume tmap_cclv; - // if (tmap_has_amve) + // if (tmap_amve_flag) // AmbientViewingEnvironment tmap_amve; - // if (tmap_has_reve) + // if (tmap_reve_flag) // ReferenceViewingEnvironment tmap_reve; - // if (tmap_has_ndwt) + // if (tmap_ndwt_flag) // NominalDiffuseWhite tmap_ndwt; - - // bit(1) tmap_has_icc; - // if (tmap_has_icc) - // unsigned int(metadata_size_size) tmap_icc_data_size_minus_one; - // bit(1) tmap_has_explicit_cicp; - // if (tmap_has_explicit_cicp) { - // bit(8) tmap_colour_primaries; - // bit(8) tmap_transfer_characteristics; - // bit(8) tmap_matrix_coefficients; - // bit(1) tmap_full_range; - // } else { - // tmap_colour_primaries = 1; - // tmap_transfer_characteristics = 13; - // tmap_matrix_coefficients = 6; - // tmap_full_range = 1; - // } // } return AVIF_RESULT_NOT_IMPLEMENTED; } + // Chunk sizes + uint32_t fewMetadataBytesFlag, fewCodecConfigBytesFlag, fewItemDataBytesFlag; + if (hasIcc || hasExif || hasXmp || (hasHdr && hasGainmap)) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &fewMetadataBytesFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) few_metadata_bytes_flag; + } + AVIF_CHECKERR(avifROStreamReadBits(&s, &fewCodecConfigBytesFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) few_codec_config_bytes_flag; + AVIF_CHECKERR(avifROStreamReadBits(&s, &fewItemDataBytesFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) few_item_data_bytes_flag; + + uint32_t iccDataSize = 0; + if (hasIcc) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &iccDataSize, fewMetadataBytesFlag ? 10 : 20), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_metadata_bytes_flag ? 10 : 20) icc_data_size_minus1; + ++iccDataSize; + } + // if (hdr_flag && gainmap_flag && tmap_icc_flag) + // unsigned int(few_metadata_bytes_flag ? 10 : 20) tmap_icc_data_size_minus1; + + // if (hdr_flag && gainmap_flag) + // unsigned int(few_metadata_bytes_flag ? 10 : 20) gainmap_metadata_size; + // if (hdr_flag && gainmap_flag) + // unsigned int(few_item_data_bytes_flag ? 15 : 28) gainmap_item_data_size; + // if (hdr_flag && gainmap_flag && gainmap_item_data_size > 0) + // unsigned int(few_codec_config_bytes_flag ? 3 : 12) gainmap_item_codec_config_size; + + uint32_t mainItemCodecConfigSize, mainItemDataSize; + AVIF_CHECKERR(avifROStreamReadBits(&s, &mainItemCodecConfigSize, fewCodecConfigBytesFlag ? 3 : 12), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_codec_config_bytes_flag ? 3 : 12) main_item_codec_config_size; + AVIF_CHECKERR(avifROStreamReadBits(&s, &mainItemDataSize, fewItemDataBytesFlag ? 15 : 28), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_item_data_bytes_flag ? 15 : 28) main_item_data_size_minus1; + ++mainItemDataSize; + + uint32_t alphaItemCodecConfigSize = 0, alphaItemDataSize = 0; + if (hasAlpha) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &alphaItemDataSize, fewItemDataBytesFlag ? 15 : 28), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_item_data_bytes_flag ? 15 : 28) alpha_item_data_size; + } + if (hasAlpha && alphaItemDataSize != 0) { + AVIF_CHECKERR(avifROStreamReadBits(&s, &alphaItemCodecConfigSize, fewCodecConfigBytesFlag ? 3 : 12), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_codec_config_bytes_flag ? 3 : 12) alpha_item_codec_config_size; + } + uint32_t exifDataSize = 0; if (hasExif) { - AVIF_CHECKERR(avifROStreamReadBits(s, &exifDataSize, metadataSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(metadata_size_size) exif_data_size_minus_one; + AVIF_CHECKERR(avifROStreamReadBits(&s, &exifDataSize, fewMetadataBytesFlag ? 10 : 20), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_metadata_bytes_flag ? 10 : 20) exif_data_size_minus_one; ++exifDataSize; } uint32_t xmpDataSize = 0; if (hasXmp) { - AVIF_CHECKERR(avifROStreamReadBits(s, &xmpDataSize, metadataSizeSize), - AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(metadata_size_size) xmp_data_size_minus_one; + AVIF_CHECKERR(avifROStreamReadBits(&s, &xmpDataSize, fewMetadataBytesFlag ? 10 : 20), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(few_metadata_bytes_flag ? 10 : 20) xmp_data_size_minus_one; ++xmpDataSize; } - // Padding to align with whole bytes if necessary. - if (s->numUsedBitsInPartialByte) { + // trailing_bits(); // bit padding till byte alignment + if (s.numUsedBitsInPartialByte) { uint32_t padding; - AVIF_CHECKERR(avifROStreamReadBits(s, &padding, 8 - s->numUsedBitsInPartialByte), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamReadBits(&s, &padding, 8 - s.numUsedBitsInPartialByte), AVIF_RESULT_BMFF_PARSE_FAILED); AVIF_CHECKERR(!padding, AVIF_RESULT_BMFF_PARSE_FAILED); // Only accept zeros as padding. } - // Codec configs - + // Chunks avifCodecConfigurationBox alphaItemCodecConfig = { 0 }; - if (hasAlpha) { - AVIF_CHECKERR(alphaItemDataSize > 0, AVIF_RESULT_BMFF_PARSE_FAILED); + if (hasAlpha && alphaItemDataSize != 0 && alphaItemCodecConfigSize != 0) { AVIF_CHECKERR(alphaItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED); - AVIF_CHECKERR(avifParseCodecConfiguration(s, &alphaItemCodecConfig, (const char *)codecConfigType, diag), + AVIF_CHECKERR(avifParseCodecConfiguration(&s, &alphaItemCodecConfig, (const char *)codecConfigType, diag), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) alpha_item_codec_config[alpha_item_codec_config_size]; } - - // if (has_hdr && has_gainmap && gainmap_item_codec_config_size > 0) + // if (hdr_flag && gainmap_flag && gainmap_item_codec_config_size > 0) // unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size]; - avifCodecConfigurationBox mainItemCodecConfig = { 0 }; AVIF_CHECKERR(mainItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED); - AVIF_CHECKERR(avifParseCodecConfiguration(s, &mainItemCodecConfig, (const char *)codecConfigType, diag), + AVIF_CHECKERR(avifParseCodecConfiguration(&s, &mainItemCodecConfig, (const char *)codecConfigType, diag), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) main_item_codec_config[main_item_codec_config_size]; // Make sure all metadata and coded chunks fit into the 'meta' box whose size is rawLen. // There should be no missing nor unused byte. - AVIF_CHECKERR(avifROStreamRemainingBytes(s) == (uint64_t)iccDataSize + alphaItemDataSize + mainItemDataSize + exifDataSize + xmpDataSize, + AVIF_CHECKERR(avifROStreamRemainingBytes(&s) == (uint64_t)iccDataSize + alphaItemDataSize + mainItemDataSize + exifDataSize + xmpDataSize, AVIF_RESULT_BMFF_PARSE_FAILED); - // Create the items and properties generated by the MetaBox with version 1. - // The MetaBox with version 1 always creates 8 properties for specification easiness. + // Create the items and properties generated by the MinimizedImageBox. + // The MinimizedImageBox always creates 8 properties for specification easiness. // Use FreeSpaceBoxes as no-op placeholder properties when necessary. // There is no need to use placeholder items because item IDs do not have to // be contiguous, whereas property indices shall be 1, 2, 3, 4, 5 etc. @@ -3805,29 +3835,29 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t memcpy(colorItem->type, infeType, 4); colorItem->width = width; colorItem->height = height; - colorItem->metaV1PixelFormat = chromaSubsampling == 0 ? AVIF_PIXEL_FORMAT_YUV400 - : chromaSubsampling == 1 ? AVIF_PIXEL_FORMAT_YUV420 - : chromaSubsampling == 2 ? AVIF_PIXEL_FORMAT_YUV422 - : AVIF_PIXEL_FORMAT_YUV444; - if (colorItem->metaV1PixelFormat == AVIF_PIXEL_FORMAT_YUV422) { + colorItem->miniBoxPixelFormat = chromaSubsampling == 0 ? AVIF_PIXEL_FORMAT_YUV400 + : chromaSubsampling == 1 ? AVIF_PIXEL_FORMAT_YUV420 + : chromaSubsampling == 2 ? AVIF_PIXEL_FORMAT_YUV422 + : AVIF_PIXEL_FORMAT_YUV444; + if (colorItem->miniBoxPixelFormat == AVIF_PIXEL_FORMAT_YUV422) { // In AV1, the chroma_sample_position syntax element is not present for the YUV 4:2:2 format. // Assume that AV1 uses the same 4:2:2 chroma sample location as HEVC and VVC (colocated). - AVIF_CHECKERR(!isHorizontallyCentered, AVIF_RESULT_BMFF_PARSE_FAILED); - // isVerticallyCentered: Ignored unless chroma_subsampling is 1. - colorItem->metaV1ChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; - } else if (colorItem->metaV1PixelFormat == AVIF_PIXEL_FORMAT_YUV420) { - if (isHorizontallyCentered) { + AVIF_CHECKERR(!chromaIsHorizontallyCentered, AVIF_RESULT_BMFF_PARSE_FAILED); + // chromaIsVerticallyCentered: Ignored unless chroma_subsampling is 1. + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } else if (colorItem->miniBoxPixelFormat == AVIF_PIXEL_FORMAT_YUV420) { + if (chromaIsHorizontallyCentered) { // There is no way to describe this with AV1's chroma_sample_position enum besides CSP_UNKNOWN. // There is a proposal to assign the reserved value 3 (CSP_RESERVED) to the center chroma sample position. - colorItem->metaV1ChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; } else { - colorItem->metaV1ChromaSamplePosition = isVerticallyCentered ? AVIF_CHROMA_SAMPLE_POSITION_VERTICAL - : AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + colorItem->miniBoxChromaSamplePosition = chromaIsVerticallyCentered ? AVIF_CHROMA_SAMPLE_POSITION_VERTICAL + : AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; } } else { - // isHorizontallyCentered: Ignored unless chroma_subsampling is 1 or 2. - // isVerticallyCentered: Ignored unless chroma_subsampling is 1. - colorItem->metaV1ChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + // chromaIsHorizontallyCentered: Ignored unless chroma_subsampling is 1 or 2. + // chromaIsVerticallyCentered: Ignored unless chroma_subsampling is 1. + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; } avifDecoderItem * alphaItem = NULL; @@ -3836,8 +3866,8 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t memcpy(alphaItem->type, infeType, 4); alphaItem->width = width; alphaItem->height = height; - alphaItem->metaV1PixelFormat = AVIF_PIXEL_FORMAT_YUV400; - alphaItem->metaV1ChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + alphaItem->miniBoxPixelFormat = AVIF_PIXEL_FORMAT_YUV400; + alphaItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; } // Property with fixed index 1. @@ -3877,17 +3907,17 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t avifProperty * colrPropICC = avifMetaCreateProperty(meta, "colr"); AVIF_CHECKERR(colrPropICC, AVIF_RESULT_OUT_OF_MEMORY); colrPropICC->u.colr.hasICC = AVIF_TRUE; // colour_type "rICC" or "prof" - colrPropICC->u.colr.iccOffset = rawOffset + avifROStreamOffset(s); + colrPropICC->u.colr.iccOffset = rawOffset + avifROStreamOffset(&s); colrPropICC->u.colr.iccSize = (size_t)iccDataSize; - AVIF_CHECKERR(avifROStreamSkip(s, colrPropICC->u.colr.iccSize), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamSkip(&s, colrPropICC->u.colr.iccSize), AVIF_RESULT_BMFF_PARSE_FAILED); AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, colrPropICC), AVIF_RESULT_OUT_OF_MEMORY); } else { AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. } - // if (has_hdr && has_gainmap && tmap_has_icc) - // unsigned int(8) tmap_icc_data[tmap_icc_data_size_minus_one + 1]; - // if (has_hdr && has_gainmap && gainmap_metadata_size > 0) + // if (hdr_flag && gainmap_flag && tmap_icc_flag) + // unsigned int(8) tmap_icc_data[tmap_icc_data_size_minus1 + 1]; + // if (hdr_flag && gainmap_flag && gainmap_metadata_size > 0) // unsigned int(8) gainmap_metadata[gainmap_metadata_size]; if (hasAlpha) { @@ -3922,21 +3952,21 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); } - // Same behavior as avifImageExtractExifOrientationToIrotImir() for orientation+1. - if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 6 || orientation == 7) { + // Same behavior as avifImageExtractExifOrientationToIrotImir(). + if (orientation == 3 || orientation == 5 || orientation == 6 || orientation == 7 || orientation == 8) { // Property with fixed index 9. avifProperty * irotProp = avifMetaCreateProperty(meta, "irot"); AVIF_CHECKERR(irotProp, AVIF_RESULT_OUT_OF_MEMORY); - irotProp->u.irot.angle = orientation == 2 ? 2 : (orientation == 4 || orientation == 7) ? 1 : 3; + irotProp->u.irot.angle = orientation == 3 ? 2 : (orientation == 5 || orientation == 8) ? 1 : 3; AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, irotProp), AVIF_RESULT_OUT_OF_MEMORY); } else { AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. } - if (orientation == 1 || orientation == 3 || orientation == 4 || orientation == 6) { + if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) { // Property with fixed index 10. avifProperty * imirProp = avifMetaCreateProperty(meta, "imir"); AVIF_CHECKERR(imirProp, AVIF_RESULT_OUT_OF_MEMORY); - imirProp->u.imir.axis = orientation == 1 ? 1 : 0; + imirProp->u.imir.axis = orientation == 2 ? 1 : 0; AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, imirProp), AVIF_RESULT_OUT_OF_MEMORY); } else { AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. @@ -3952,20 +3982,20 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t if (hasAlpha) { avifExtent * alphaExtent = (avifExtent *)avifArrayPush(&alphaItem->extents); AVIF_CHECKERR(alphaExtent, AVIF_RESULT_OUT_OF_MEMORY); - alphaExtent->offset = rawOffset + avifROStreamOffset(s); + alphaExtent->offset = rawOffset + avifROStreamOffset(&s); alphaExtent->size = (size_t)alphaItemDataSize; - AVIF_CHECKERR(avifROStreamSkip(s, alphaExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamSkip(&s, alphaExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); alphaItem->size = alphaExtent->size; } - // if (has_hdr && has_gainmap && gainmap_item_data_size > 0) + // if (hdr_flag && gainmap_flag && gainmap_item_data_size > 0) // unsigned int(8) gainmap_item_data[gainmap_item_data_size]; avifExtent * colorExtent = (avifExtent *)avifArrayPush(&colorItem->extents); AVIF_CHECKERR(colorExtent, AVIF_RESULT_OUT_OF_MEMORY); - colorExtent->offset = rawOffset + avifROStreamOffset(s); + colorExtent->offset = rawOffset + avifROStreamOffset(&s); colorExtent->size = (size_t)mainItemDataSize; - AVIF_CHECKERR(avifROStreamSkip(s, colorExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamSkip(&s, colorExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); colorItem->size = colorExtent->size; if (hasExif) { @@ -3977,9 +4007,9 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t avifExtent * exifExtent = (avifExtent *)avifArrayPush(&exifItem->extents); AVIF_CHECKERR(exifExtent, AVIF_RESULT_OUT_OF_MEMORY); - exifExtent->offset = rawOffset + avifROStreamOffset(s); + exifExtent->offset = rawOffset + avifROStreamOffset(&s); exifExtent->size = (size_t)exifDataSize; // Does not include unsigned int(32) exif_tiff_header_offset; - AVIF_CHECKERR(avifROStreamSkip(s, exifExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamSkip(&s, exifExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); exifItem->size = exifExtent->size; } @@ -3993,14 +4023,14 @@ static avifResult avifParseMetaBoxV1(avifROStream * s, avifMeta * meta, uint64_t avifExtent * xmpExtent = (avifExtent *)avifArrayPush(&xmpItem->extents); AVIF_CHECKERR(xmpExtent, AVIF_RESULT_OUT_OF_MEMORY); - xmpExtent->offset = rawOffset + avifROStreamOffset(s); + xmpExtent->offset = rawOffset + avifROStreamOffset(&s); xmpExtent->size = (size_t)xmpDataSize; - AVIF_CHECKERR(avifROStreamSkip(s, xmpExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifROStreamSkip(&s, xmpExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); xmpItem->size = xmpExtent->size; } return AVIF_RESULT_OK; } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) { @@ -4033,13 +4063,13 @@ static avifResult avifParse(avifDecoder * decoder) uint64_t parseOffset = 0; avifDecoderData * data = decoder->data; avifBool ftypSeen = AVIF_FALSE; - avifBool metaV0Seen = AVIF_FALSE; + avifBool metaSeen = AVIF_FALSE; avifBool moovSeen = AVIF_FALSE; - avifBool needsMetaV0 = AVIF_FALSE; + avifBool needsMeta = AVIF_FALSE; avifBool needsMoov = AVIF_FALSE; -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - avifBool metaV1Seen = AVIF_FALSE; - avifBool needsMetaV1 = AVIF_FALSE; +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + avifBool miniSeen = AVIF_FALSE; + avifBool needsMini = AVIF_FALSE; #endif for (;;) { @@ -4070,7 +4100,12 @@ static avifResult avifParse(avifDecoder * decoder) avifROData boxContents = AVIF_DATA_EMPTY; // TODO: reorg this code to only do these memcmps once each +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (!memcmp(header.type, "ftyp", 4) || !memcmp(header.type, "meta", 4) || !memcmp(header.type, "moov", 4) || + !memcmp(header.type, "mini", 4)) { +#else if (!memcmp(header.type, "ftyp", 4) || !memcmp(header.type, "meta", 4) || !memcmp(header.type, "moov", 4)) { +#endif boxOffset = parseOffset; size_t sizeToRead; if (header.isSizeZeroBox) { @@ -4111,28 +4146,27 @@ static avifResult avifParse(avifDecoder * decoder) } ftypSeen = AVIF_TRUE; memcpy(data->majorBrand, ftyp.majorBrand, 4); // Remember the major brand for future AVIF_DECODER_SOURCE_AUTO decisions - needsMetaV0 = avifFileTypeHasBrand(&ftyp, "avif"); + needsMeta = avifFileTypeHasBrand(&ftyp, "avif"); needsMoov = avifFileTypeHasBrand(&ftyp, "avis"); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - needsMetaV1 = avifFileTypeHasBrand(&ftyp, "mif3"); - if (needsMetaV1 && (needsMetaV0 || needsMoov)) { +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + needsMini = avifFileTypeHasBrand(&ftyp, "mif3"); + if (needsMini && needsMeta) { return AVIF_RESULT_INVALID_FTYP; } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI } else if (!memcmp(header.type, "meta", 4)) { - AVIF_CHECKERR(!metaV0Seen, AVIF_RESULT_BMFF_PARSE_FAILED); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - AVIF_CHECKERR(!metaV1Seen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); #endif AVIF_CHECKRES(avifParseMetaBox(data->meta, boxOffset, boxContents.data, boxContents.size, data->diag)); -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (data->meta->fromMetaV1) { - metaV1Seen = AVIF_TRUE; - } else { - metaV0Seen = AVIF_TRUE; - } -#else - metaV0Seen = AVIF_TRUE; + metaSeen = AVIF_TRUE; +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + } else if (!memcmp(header.type, "mini", 4)) { + AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseMinimizedImageBox(data->meta, boxOffset, boxContents.data, boxContents.size, data->diag)); + miniSeen = AVIF_TRUE; #endif } else if (!memcmp(header.type, "moov", 4)) { AVIF_CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED); @@ -4142,21 +4176,22 @@ static avifResult avifParse(avifDecoder * decoder) decoder->imageSequenceTrackPresent = AVIF_TRUE; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (ftypSeen && !needsMetaV1 && metaV1Seen) { - // The 'meta' box with version 1 should be ignored if there is no 'mif3' brand, but libavif allows reading them in any order. - return AVIF_RESULT_NOT_IMPLEMENTED; // TODO(yguyon): Implement +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (ftypSeen && !needsMini) { + // When MinimizedImageBox is present in a file, the 'mif3' brand or a derived brand that implies the 'mif3' + // brand shall be the major brand or present among the compatible brands in the FileTypeBox. + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI // See if there is enough information to consider Parse() a success and early-out: - // * If the brand 'avif' is present, require a meta box with version 0 + // * If the brand 'avif' is present, require a meta box // * If the brand 'avis' is present, require a moov box - // * If AVIF_ENABLE_EXPERIMENTAL_METAV1 is defined and the brand 'mif3' is present, require a meta box with version 1 -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (ftypSeen && (!needsMetaV0 || metaV0Seen) && (!needsMoov || moovSeen) && (!needsMetaV1 || metaV1Seen)) { + // * If AVIF_ENABLE_EXPERIMENTAL_MINI is defined and the brand 'mif3' is present, require a mini box +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen) && (!needsMini || miniSeen)) { #else - if (ftypSeen && (!needsMetaV0 || metaV0Seen) && (!needsMoov || moovSeen)) { + if (ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen)) { #endif return AVIF_RESULT_OK; } @@ -4164,11 +4199,11 @@ static avifResult avifParse(avifDecoder * decoder) if (!ftypSeen) { return AVIF_RESULT_INVALID_FTYP; } - if ((needsMetaV0 && !metaV0Seen) || (needsMoov && !moovSeen)) { + if ((needsMeta && !metaSeen) || (needsMoov && !moovSeen)) { return AVIF_RESULT_TRUNCATED_DATA; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - if (needsMetaV1 && !metaV1Seen) { +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (needsMini && !miniSeen) { return AVIF_RESULT_TRUNCATED_DATA; } #endif @@ -4195,9 +4230,9 @@ static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand) static avifBool avifFileTypeIsCompatible(avifFileType * ftyp) { return avifFileTypeHasBrand(ftyp, "avif") || avifFileTypeHasBrand(ftyp, "avis") -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) || avifFileTypeHasBrand(ftyp, "mif3") -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI ; } diff --git a/src/write.c b/src/write.c index b53a7258fa..d13f7b3ce3 100644 --- a/src/write.c +++ b/src/write.c @@ -2297,12 +2297,11 @@ static avifResult avifWriteAltrGroup(avifRWStream * s, const avifEncoderItemIdAr return AVIF_RESULT_OK; } -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) -// Returns true if the image can be encoded with a slim MetaBox with version 1 -// instead of a full regular MetaBox with version 0. -static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +// Returns true if the image can be encoded with a MinimizedImageBox instead of a full regular MetaBox. +static avifBool avifEncoderIsMiniCompatible(const avifEncoder * encoder) { - // The MetaBox with version 1 ("mif3" brand) only supports non-layered, still images. + // The MinimizedImageBox ("mif3" brand) only supports non-layered, still images. if (encoder->extraLayerCount || (encoder->data->frames.count != 1)) { return AVIF_FALSE; } @@ -2321,8 +2320,11 @@ static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) return AVIF_FALSE; } - // 4:4:4, 4:2:2, 4:2:0 and 4:0:0 are supported by a MetaBox with version 1. - if (encoder->data->imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_NONE) { + // 4:4:4, 4:2:2, 4:2:0 and 4:0:0 are supported by a MinimizedImageBox. + if (encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV444 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV422 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV420 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) { return AVIF_FALSE; } @@ -2335,7 +2337,7 @@ static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; - // Grids are not supported by a MetaBox with version 1. + // Grids are not supported by a MinimizedImageBox. if (item->gridCols || item->gridRows) { return AVIF_FALSE; } @@ -2347,18 +2349,18 @@ static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) if (item->encodeOutput->samples.count != 1 || item->encodeOutput->samples.sample[0].data.size > (1 << 28)) { return AVIF_FALSE; } - continue; // The primary item can be stored in the MetaBox with version 1. + continue; // The primary item can be stored in the MinimizedImageBox. } if (item->itemCategory == AVIF_ITEM_ALPHA && item->irefToID == encoder->data->primaryItemID) { // alpha_item_data_size so 2^28 exclusive. if (item->encodeOutput->samples.count != 1 || item->encodeOutput->samples.sample[0].data.size >= (1 << 28)) { return AVIF_FALSE; } - continue; // The alpha auxiliary item can be stored in the MetaBox with version 1. + continue; // The alpha auxiliary item can be stored in the MinimizedImageBox. } if (!memcmp(item->type, "mime", 4) && !memcmp(item->infeName, "XMP", item->infeNameSize)) { assert(item->metadataPayload.size == encoder->data->imageMetadata->xmp.size); - continue; // XMP metadata can be stored in the MetaBox with version 1. + continue; // XMP metadata can be stored in the MinimizedImageBox. } if (!memcmp(item->type, "Exif", 4) && !memcmp(item->infeName, "Exif", item->infeNameSize)) { assert(item->metadataPayload.size == encoder->data->imageMetadata->exif.size + 4); @@ -2366,11 +2368,11 @@ static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) if (exif_tiff_header_offset != 0) { return AVIF_FALSE; } - continue; // Exif metadata can be stored in the MetaBox with version 1 if exif_tiff_header_offset is 0. + continue; // Exif metadata can be stored in the MinimizedImageBox if exif_tiff_header_offset is 0. } // Items besides the colorItem, the alphaItem and Exif/XMP/ICC - // metadata are not directly supported by the MetaBox with version 1. + // metadata are not directly supported by the MinimizedImageBox. return AVIF_FALSE; } // A primary item is necessary. @@ -2380,7 +2382,7 @@ static avifBool avifEncoderIsMetaBoxV1Compatible(const avifEncoder * encoder) return AVIF_TRUE; } -static avifResult avifEncoderWriteMetaBoxV1(avifEncoder * encoder, avifRWStream * s); +static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream * s); static avifResult avifEncoderWriteFileTypeBoxAndMetaBoxV1(avifEncoder * encoder, avifRWData * output) { @@ -2394,13 +2396,13 @@ static avifResult avifEncoderWriteFileTypeBoxAndMetaBoxV1(avifEncoder * encoder, // unsigned int(32) compatible_brands[]; avifRWStreamFinishBox(&s, ftyp); - AVIF_CHECKRES(avifEncoderWriteMetaBoxV1(encoder, &s)); + AVIF_CHECKRES(avifEncoderWriteMiniBox(encoder, &s)); avifRWStreamFinishWrite(&s); return AVIF_RESULT_OK; } -static avifResult avifEncoderWriteMetaBoxV1(avifEncoder * encoder, avifRWStream * s) +static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream * s) { const avifEncoderItem * colorItem = NULL; const avifEncoderItem * alphaItem = NULL; @@ -2423,7 +2425,8 @@ static avifResult avifEncoderWriteMetaBoxV1(avifEncoder * encoder, avifRWStream const avifBool hasAlpha = alphaItem != NULL; const avifBool alphaIsPremultiplied = encoder->data->imageMetadata->alphaPremultiplied; - const avifBool hasHdr = AVIF_FALSE; // Not implemented. + const avifBool hasHdr = AVIF_FALSE; // Not implemented. + const avifBool hasGainmap = AVIF_FALSE; // Not implemented. const avifBool hasIcc = image->icc.size != 0; const uint32_t chromaSubsampling = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400 ? 0 : image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 ? 1 @@ -2439,10 +2442,8 @@ static avifResult avifEncoderWriteMetaBoxV1(avifEncoder * encoder, avifRWStream image->transferCharacteristics != defaultTransferCharacteristics || image->matrixCoefficients != defaultMatrixCoefficients; + const avifBool floatFlag = AVIF_FALSE; const avifBool fullRange = image->yuvRange == AVIF_RANGE_FULL; - const uint32_t pixelFormat = image->depth == 8 ? AVIF_METAV1_PIXEL_FORMAT_UINT8 - : image->depth == 10 ? AVIF_METAV1_PIXEL_FORMAT_UINT10 - : AVIF_METAV1_PIXEL_FORMAT_UINT12; // In AV1, the chroma_sample_position syntax element is not present for the YUV 4:2:2 format. // Assume that AV1 uses the same 4:2:2 chroma sample location as HEVC and VVC (colocated). @@ -2454,198 +2455,245 @@ static avifResult avifEncoderWriteMetaBoxV1(avifEncoder * encoder, avifRWStream } // For the YUV 4:2:0 format, assume centered sample position unless specified otherwise. // This is consistent with the behavior in read.c. - const avifBool isHorizontallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && - image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_VERTICAL && - image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; - const avifBool isVerticallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && - image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + const avifBool chromaIsHorizontallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_VERTICAL && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + const avifBool chromaIsVerticallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; - const uint32_t orientation = avifImageIrotImirToExifOrientation(image) - 1; + const uint32_t orientationMinus1 = avifImageIrotImirToExifOrientation(image) - 1; const avifBool hasExplicitCodecTypes = AVIF_FALSE; // 'av01' and 'av1C' known from 'avif' minor_version field of FileTypeBox. - const uint32_t dimensionSize = image->width <= (1 << 7) && image->height <= (1 << 7) ? 7 : 15; - const uint32_t codecConfigSize = 4; // 'av1C' always uses 4 bytes. - const uint32_t codecConfigSizeSize = codecConfigSize < (1 << 3) ? 3 : 12; - const uint32_t itemDataSizeSize = colorData->size <= (1 << 15) && (!alphaData || alphaData->size < (1 << 15)) ? 15 : 28; - const uint32_t metadataSizeSize = - image->icc.size <= (1 << 10) && image->exif.size <= (1 << 10) && image->xmp.size <= (1 << 10) ? 10 : 20; - - uint32_t flags = 0; - flags |= alphaItem ? 0x000001 : 0; // has_alpha - flags |= alphaIsPremultiplied ? 0x000002 : 0; // alpha_is_premultiplied - flags |= hasHdr ? 0x000004 : 0; // has_hdr - flags |= hasExplicitCicp ? 0x000008 : 0; // has_explicit_cicp - flags |= hasIcc ? 0x000010 : 0; // has_icc - flags |= image->exif.size ? 0x000020 : 0; // has_exif - flags |= image->xmp.size ? 0x000040 : 0; // has_xmp - flags |= fullRange ? 0x000080 : 0; // full_range - flags |= pixelFormat << 8; // pixel_format (4 bits) - flags |= chromaSubsampling << 12; // chroma_subsampling (2 bits) - flags |= isHorizontallyCentered ? 0x004000 : 0; // is_horizontally_centered - flags |= isVerticallyCentered ? 0x008000 : 0; // is_vertically_centered - flags |= orientation << 16; // orientation (3 bits) - flags |= hasExplicitCodecTypes ? 0x080000 : 0; // has_explicit_codec_types - flags |= dimensionSize == 7 ? 0x100000 : 0; // dimension_size - flags |= codecConfigSizeSize == 3 ? 0x200000 : 0; // codec_config_size_size - flags |= itemDataSizeSize == 15 ? 0x400000 : 0; // item_data_size_size - flags |= metadataSizeSize == 10 ? 0x800000 : 0; // metadata_size_size - - avifBoxMarker meta; - AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, /*version=*/1, flags, &meta)); - - AVIF_CHECKRES(avifRWStreamWriteBits(s, image->width - 1, dimensionSize)); // unsigned int(dimension_size) width_minus_one; - AVIF_CHECKRES(avifRWStreamWriteBits(s, image->height - 1, dimensionSize)); // unsigned int(dimension_size) height_minus_one; + const uint32_t smallDimensionsFlag = image->width <= (1 << 7) && image->height <= (1 << 7); + const uint32_t codecConfigSize = 4; // 'av1C' always uses 4 bytes. + const uint32_t fewCodecConfigBytesFlag = codecConfigSize < (1 << 3); // 'av1C' always uses 4 bytes. + const uint32_t fewItemDataBytesFlag = colorData->size <= (1 << 15) && (!alphaData || alphaData->size < (1 << 15)); + const uint32_t fewMetadataBytesFlag = image->icc.size <= (1 << 10) && image->exif.size <= (1 << 10) && image->xmp.size <= (1 << 10); + + avifBoxMarker mini; + AVIF_CHECKRES(avifRWStreamWriteBox(s, "mini", AVIF_BOX_SIZE_TBD, &mini)); + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 2)); // bit(2) version; + + // flags + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem ? 1 : 0, 1)); // bit(1) alpha_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size ? 1 : 0, 1)); // bit(1) exif_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size ? 1 : 0, 1)); // bit(1) xmp_flag; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaSubsampling, 2)); // bit(2) chroma_subsampling; + AVIF_CHECKRES(avifRWStreamWriteBits(s, orientationMinus1, 3)); // bit(3) orientation_minus1; + + // Spatial extents + AVIF_CHECKRES(avifRWStreamWriteBits(s, smallDimensionsFlag, 1)); // bit(1) small_dimensions_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->width - 1, smallDimensionsFlag ? 7 : 15)); // unsigned int(small_dimensions_flag ? 7 : 15) width_minus1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->height - 1, smallDimensionsFlag ? 7 : 15)); // unsigned int(small_dimensions_flag ? 7 : 15) height_minus1; + + // Pixel information + if (chromaSubsampling == 1 || chromaSubsampling == 2) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaIsHorizontallyCentered, 1)); // bit(1) chroma_is_horizontally_centered; + } + if (chromaSubsampling == 1) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaIsVerticallyCentered, 1)); // bit(1) chroma_is_vertically_centered; + } + + if (floatFlag) { + // bit(2) bit_depth_log2_minus4; + return AVIF_RESULT_NOT_IMPLEMENTED; + } else { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth > 8, 1)); // bit(1) high_bit_depth_flag; + if (image->depth > 8) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth - 9, 3)); // bit(3) bit_depth_minus9; + } + } - if (hasIcc) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->icc.size - 1, metadataSizeSize)); // unsigned int(metadata_size_size) icc_data_size_minus_one; + if (alphaItem) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaIsPremultiplied, 1)); // bit(1) alpha_is_premultiplied; } + // Colour properties if (hasExplicitCicp) { AVIF_CHECKRES(avifRWStreamWriteBits(s, image->colorPrimaries, 8)); // bit(8) colour_primaries; AVIF_CHECKRES(avifRWStreamWriteBits(s, image->transferCharacteristics, 8)); // bit(8) transfer_characteristics; - } - if (chromaSubsampling == 0) { - AVIF_CHECKERR(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED, AVIF_RESULT_ENCODE_COLOR_FAILED); - } else if (hasExplicitCicp) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, image->matrixCoefficients, 8)); // bit(8) matrix_coefficients; + if (chromaSubsampling != 0) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->matrixCoefficients, 8)); // bit(8) matrix_coefficients; + } else { + AVIF_CHECKERR(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED, AVIF_RESULT_ENCODE_COLOR_FAILED); + } } - AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigSize, codecConfigSizeSize)); // unsigned int(codec_config_size_size) main_item_codec_config_size; - AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)colorData->size - 1, itemDataSizeSize)); // unsigned int(item_data_size_size) main_item_data_size_minus_one; - - if (hasAlpha) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)alphaData->size, itemDataSizeSize)); // unsigned int(item_data_size_size) alpha_item_data_size; - if (alphaData->size != 0) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigSize, codecConfigSizeSize)); // unsigned int(codec_config_size_size) alpha_item_codec_config_size; - } + // Optional unless minor_version of FileTypeBox is a brand defining these + if (hasExplicitCodecTypes) { + // bit(32) infe_type; + // bit(32) codec_config_type; + return AVIF_RESULT_NOT_IMPLEMENTED; } + // High Dynamic Range properties if (hasHdr) { - // bit(1) has_gainmap; - // bit(1) has_clli; - // bit(1) has_mdcv; - // bit(1) has_cclv; - // bit(1) has_amve; - // bit(1) has_reve; - // bit(1) has_ndwt; - // if (has_clli) + // bit(1) gainmap_flag; + // if (gainmap_flag) { + // unsigned int(small_dimensions_flag ? 7 : 15) gainmap_width_minus1; + // unsigned int(small_dimensions_flag ? 7 : 15) gainmap_height_minus1; + // bit(8) gainmap_matrix_coefficients; + // bit(1) gainmap_full_range_flag; + // bit(2) gainmap_chroma_subsampling; + // if (gainmap_chroma_subsampling == 1 || gainmap_chroma_subsampling == 2) + // bit(1) gainmap_chroma_is_horizontally_centered; + // if (gainmap_chroma_subsampling == 1) + // bit(1) gainmap_chroma_is_vertically_centered; + // bit(1) gainmap_float_flag; + // if (gainmap_float_flag) + // bit(2) gainmap_bit_depth_log2_minus4; + // else { + // bit(1) gainmap_high_bit_depth_flag; + // if (gainmap_high_bit_depth_flag) + // bit(3) gainmap_bit_depth_minus9; + // } + // bit(1) tmap_icc_flag; + // bit(1) tmap_explicit_cicp_flag; + // if (tmap_explicit_cicp_flag) { + // bit(8) tmap_colour_primaries; + // bit(8) tmap_transfer_characteristics; + // bit(8) tmap_matrix_coefficients; + // bit(1) tmap_full_range_flag; + // } + // else { + // tmap_colour_primaries = 1; + // tmap_transfer_characteristics = 13; + // tmap_matrix_coefficients = 6; + // tmap_full_range_flag = 1; + // } + // } + // bit(1) clli_flag; + // bit(1) mdcv_flag; + // bit(1) cclv_flag; + // bit(1) amve_flag; + // bit(1) reve_flag; + // bit(1) ndwt_flag; + // if (clli_flag) // ContentLightLevel clli; - // if (has_mdcv) + // if (mdcv_flag) // MasteringDisplayColourVolume mdcv; - // if (has_cclv) + // if (cclv_flag) // ContentColourVolume cclv; - // if (has_amve) + // if (amve_flag) // AmbientViewingEnvironment amve; - // if (has_reve) + // if (reve_flag) // ReferenceViewingEnvironment reve; - // if (has_ndwt) + // if (ndwt_flag) // NominalDiffuseWhite ndwt; - - // if (has_gainmap) { - // unsigned int(metadata_size_size) gainmap_metadata_size; - // unsigned int(item_data_size_size) gainmap_item_data_size; - // unsigned int(codec_config_size_size) gainmap_item_codec_config_size; - // unsigned int(dimension_size) gainmap_width_minus_one; - // unsigned int(dimension_size) gainmap_height_minus_one; - // bit(8) gainmap_matrix_coefficients; - // bit(1) gainmap_full_range; - // bit(2) gainmap_chroma_subsampling; - // bit(1) gainmap_is_horizontally_centered; - // bit(1) gainmap_is_vertically_centered; - // bit(4) gainmap_pixel_format; - - // bit(1) tmap_has_clli; - // bit(1) tmap_has_mdcv; - // bit(1) tmap_has_cclv; - // bit(1) tmap_has_amve; - // bit(1) tmap_has_reve; - // bit(1) tmap_has_ndwt; - // if (tmap_has_clli) + // if (gainmap_flag) { + // bit(1) tmap_clli_flag; + // bit(1) tmap_mdcv_flag; + // bit(1) tmap_cclv_flag; + // bit(1) tmap_amve_flag; + // bit(1) tmap_reve_flag; + // bit(1) tmap_ndwt_flag; + // if (tmap_clli_flag) // ContentLightLevel tmap_clli; - // if (tmap_has_mdcv) + // if (tmap_mdcv_flag) // MasteringDisplayColourVolume tmap_mdcv; - // if (tmap_has_cclv) + // if (tmap_cclv_flag) // ContentColourVolume tmap_cclv; - // if (tmap_has_amve) + // if (tmap_amve_flag) // AmbientViewingEnvironment tmap_amve; - // if (tmap_has_reve) + // if (tmap_reve_flag) // ReferenceViewingEnvironment tmap_reve; - // if (tmap_has_ndwt) + // if (tmap_ndwt_flag) // NominalDiffuseWhite tmap_ndwt; - - // bit(1) tmap_has_icc; - // if (tmap_has_icc) - // unsigned int(metadata_size_size) tmap_icc_data_size_minus_one; - // bit(1) tmap_has_explicit_cicp; - // if (tmap_has_explicit_cicp) { - // bit(8) tmap_colour_primaries; - // bit(8) tmap_transfer_characteristics; - // bit(8) tmap_matrix_coefficients; - // bit(1) tmap_full_range; - // } else { - // tmap_colour_primaries = 1; - // tmap_transfer_characteristics = 13; - // tmap_matrix_coefficients = 6; - // tmap_full_range = 1; - // } // } return AVIF_RESULT_NOT_IMPLEMENTED; } + // Chunk sizes + if (hasIcc || image->exif.size || image->xmp.size || (hasHdr && hasGainmap)) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, fewMetadataBytesFlag, 1)); // bit(1) few_metadata_bytes_flag; + } + AVIF_CHECKRES(avifRWStreamWriteBits(s, fewCodecConfigBytesFlag, 1)); // bit(1) few_codec_config_bytes_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, fewItemDataBytesFlag, 1)); // bit(1) few_item_data_bytes_flag; + + if (hasIcc) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->icc.size - 1, fewMetadataBytesFlag ? 10 : 20)); // unsigned int(few_metadata_bytes_flag ? 10 : 20) icc_data_size_minus1; + } + // if (hdr_flag && gainmap_flag && tmap_icc_flag) + // unsigned int(few_metadata_bytes_flag ? 10 : 20) tmap_icc_data_size_minus1; + + // if (hdr_flag && gainmap_flag) + // unsigned int(few_metadata_bytes_flag ? 10 : 20) gainmap_metadata_size; + // if (hdr_flag && gainmap_flag) + // unsigned int(few_item_data_bytes_flag ? 15 : 28) gainmap_item_data_size; + // if (hdr_flag && gainmap_flag && gainmap_item_data_size > 0) + // unsigned int(few_codec_config_bytes_flag ? 3 : 12) gainmap_item_codec_config_size; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigSize, fewCodecConfigBytesFlag ? 3 : 12)); // unsigned int(few_codec_config_bytes_flag ? 3 : 12) main_item_codec_config_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)colorData->size - 1, fewItemDataBytesFlag ? 15 : 28)); // unsigned int(few_item_data_bytes_flag ? 15 : 28) main_item_data_size_minus1; + + if (hasAlpha) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)alphaData->size, fewItemDataBytesFlag ? 15 : 28)); // unsigned int(few_item_data_bytes_flag ? 15 : 28) alpha_item_data_size; + } + if (hasAlpha && alphaData->size != 0) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigSize, fewCodecConfigBytesFlag ? 3 : 12)); // unsigned int(few_codec_config_bytes_flag ? 3 : 12) alpha_item_codec_config_size; + } + if (image->exif.size) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->exif.size - 1, metadataSizeSize)); // unsigned int(metadata_size_size) exif_data_size_minus_one; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->exif.size - 1, fewMetadataBytesFlag ? 10 : 20)); // unsigned int(few_metadata_bytes_flag ? 10 : 20) exif_data_size_minus_one; } if (image->xmp.size) { - AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->xmp.size - 1, metadataSizeSize)); // unsigned int(metadata_size_size) xmp_data_size_minus_one; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->xmp.size - 1, fewMetadataBytesFlag ? 10 : 20)); // unsigned int(few_metadata_bytes_flag ? 10 : 20) xmp_data_size_minus_one; } - // Padding to align with whole bytes if necessary. + // trailing_bits(); // bit padding till byte alignment if (s->numUsedBitsInPartialByte != 0) { AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 8 - s->numUsedBitsInPartialByte)); } const size_t headerSize = avifRWStreamOffset(s); - // Codec configs - if (hasAlpha) { + // Chunks + if (hasAlpha && alphaData->size != 0 && codecConfigSize != 0) { AVIF_CHECKRES(writeCodecConfig(s, &alphaItem->av1C)); // unsigned int(8) alpha_item_codec_config[alpha_item_codec_config_size]; } - // if (has_hdr && has_gainmap && gainmap_item_codec_config_size > 0) + // if (hdr_flag && gainmap_flag && gainmap_item_codec_config_size > 0) // unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size]; - AVIF_CHECKRES(writeCodecConfig(s, &colorItem->av1C)); // unsigned int(8) main_item_codec_config[main_item_codec_config_size]; + if (codecConfigSize > 0) { + AVIF_CHECKRES(writeCodecConfig(s, &colorItem->av1C)); // unsigned int(8) main_item_codec_config[main_item_codec_config_size]; + } - // Color profiles - if (image->icc.size) { - AVIF_CHECKRES(avifRWStreamWrite(s, image->icc.data, image->icc.size)); // unsigned int(8) icc_data[icc_data_size_minus_one + 1]; + if (hasIcc) { + AVIF_CHECKRES(avifRWStreamWrite(s, image->icc.data, image->icc.size)); // unsigned int(8) icc_data[icc_data_size_minus1 + 1]; } - // if (has_hdr && has_gainmap && tmap_has_icc) - // unsigned int(8) tmap_icc_data[tmap_icc_data_size_minus_one + 1]; - // if (has_hdr && has_gainmap && gainmap_metadata_size > 0) + // if (hdr_flag && gainmap_flag && tmap_icc_flag) + // unsigned int(8) tmap_icc_data[tmap_icc_data_size_minus1 + 1]; + // if (hdr_flag && gainmap_flag && gainmap_metadata_size > 0) // unsigned int(8) gainmap_metadata[gainmap_metadata_size]; - // Encoded samples - if (hasAlpha) { + if (hasAlpha && alphaData->size != 0) { AVIF_CHECKRES(avifRWStreamWrite(s, alphaData->data, alphaData->size)); // unsigned int(8) alpha_item_data[alpha_item_data_size]; } - // if (has_hdr && has_gainmap && gainmap_item_data_size > 0) + // if (hdr_flag && gainmap_flag && gainmap_item_data_size > 0) // unsigned int(8) gainmap_item_data[gainmap_item_data_size]; - AVIF_CHECKRES(avifRWStreamWrite(s, colorData->data, colorData->size)); // unsigned int(8) main_item_data[main_item_data_size_minus_one + 1]; - // Metadata + AVIF_CHECKRES(avifRWStreamWrite(s, colorData->data, colorData->size)); // unsigned int(8) main_item_data[main_item_data_size_minus1 + 1]; + if (image->exif.size) { - AVIF_CHECKRES(avifRWStreamWrite(s, image->exif.data, image->exif.size)); // unsigned int(8) exif_data[exif_data_size_minus_one + 1]; + AVIF_CHECKRES(avifRWStreamWrite(s, image->exif.data, image->exif.size)); // unsigned int(8) exif_data[exif_data_size_minus1 + 1]; } if (image->xmp.size) { - AVIF_CHECKRES(avifRWStreamWrite(s, image->xmp.data, image->xmp.size)); // unsigned int(8) xmp_data[xmp_data_size_minus_one + 1]; + AVIF_CHECKRES(avifRWStreamWrite(s, image->xmp.data, image->xmp.size)); // unsigned int(8) xmp_data[xmp_data_size_minus1 + 1]; } AVIF_ASSERT_OR_RETURN(avifRWStreamOffset(s) - headerSize == (hasAlpha ? codecConfigSize : 0) + codecConfigSize + image->icc.size + (hasAlpha ? alphaData->size : 0) + colorData->size + image->exif.size + image->xmp.size); - avifRWStreamFinishBox(s, meta); + avifRWStreamFinishBox(s, mini); return AVIF_RESULT_OK; } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI static avifResult avifRWStreamWriteProperties(avifItemPropertyDedup * const dedup, avifRWStream * const s, @@ -2931,13 +2979,13 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ----------------------------------------------------------------------- // Begin write stream -#if defined(AVIF_ENABLE_EXPERIMENTAL_METAV1) - // Decide whether to go for a reduced MetaBox with version 1 or a full regular MetaBox with version 0. - if ((encoder->headerFormat == AVIF_HEADER_REDUCED) && avifEncoderIsMetaBoxV1Compatible(encoder)) { +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // Decide whether to go for a reduced MinimizedImageBox or a full regular MetaBox. + if ((encoder->headerFormat == AVIF_HEADER_REDUCED) && avifEncoderIsMiniCompatible(encoder)) { AVIF_CHECKRES(avifEncoderWriteFileTypeBoxAndMetaBoxV1(encoder, output)); return AVIF_RESULT_OK; } -#endif // AVIF_ENABLE_EXPERIMENTAL_METAV1 +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI const avifImage * imageMetadata = encoder->data->imageMetadata; // The epoch for creation_time and modification_time is midnight, Jan. 1, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec972dff6f..9908b1cc07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -138,8 +138,8 @@ if(AVIF_ENABLE_GTEST) add_avif_gtest_with_data(aviflosslesstest) add_avif_gtest_with_data(avifmetadatatest) - if(AVIF_ENABLE_EXPERIMENTAL_METAV1) - add_avif_gtest(avifmetav1test) + if(AVIF_ENABLE_EXPERIMENTAL_MINI) + add_avif_gtest(avifminitest) endif() add_avif_gtest(avifopaquetest) @@ -365,8 +365,8 @@ if(AVIF_CODEC_AVM_ENABLED) PROPERTIES DISABLED True ) - if(AVIF_ENABLE_EXPERIMENTAL_METAV1) - set_tests_properties(avifmetav1test PROPERTIES DISABLED True) + if(AVIF_ENABLE_EXPERIMENTAL_MINI) + set_tests_properties(avifminitest PROPERTIES DISABLED True) endif() if(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) set_tests_properties(avifgainmaptest PROPERTIES DISABLED True) diff --git a/tests/gtest/avifmetav1test.cc b/tests/gtest/avifminitest.cc similarity index 86% rename from tests/gtest/avifmetav1test.cc rename to tests/gtest/avifminitest.cc index 4ddbcdae3e..1bddea3e72 100644 --- a/tests/gtest/avifmetav1test.cc +++ b/tests/gtest/avifminitest.cc @@ -13,13 +13,13 @@ namespace { //------------------------------------------------------------------------------ -class AvifMetaBoxV1Test +class AvifMinimizedImageBoxTest : public testing::TestWithParam> {}; -TEST_P(AvifMetaBoxV1Test, SimpleOpaque) { +TEST_P(AvifMinimizedImageBoxTest, SimpleOpaque) { const int width = std::get<0>(GetParam()); const int height = std::get<1>(GetParam()); const int depth = std::get<2>(GetParam()); @@ -66,37 +66,37 @@ TEST_P(AvifMetaBoxV1Test, SimpleOpaque) { } // Encode. - testutil::AvifRwData encoded_metav1; + testutil::AvifRwData encoded_mini; EncoderPtr encoder(avifEncoderCreate()); ASSERT_NE(encoder, nullptr); encoder->speed = AVIF_SPEED_FASTEST; encoder->headerFormat = AVIF_HEADER_REDUCED; - ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded_metav1), + ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded_mini), AVIF_RESULT_OK); // Decode. - const ImagePtr decoded_metav1 = - testutil::Decode(encoded_metav1.data, encoded_metav1.size); - ASSERT_NE(decoded_metav1, nullptr); + const ImagePtr decoded_mini = + testutil::Decode(encoded_mini.data, encoded_mini.size); + ASSERT_NE(decoded_mini, nullptr); // Compare. - testutil::AvifRwData encoded_metav0 = + testutil::AvifRwData encoded_meta = testutil::Encode(image.get(), encoder->speed); - ASSERT_NE(encoded_metav0.data, nullptr); + ASSERT_NE(encoded_meta.data, nullptr); // At least 200 bytes should be saved. - EXPECT_LT(encoded_metav1.size, encoded_metav0.size - 200); + EXPECT_LT(encoded_mini.size, encoded_meta.size - 200); - const ImagePtr decoded_metav0 = - testutil::Decode(encoded_metav0.data, encoded_metav0.size); - ASSERT_NE(decoded_metav0, nullptr); + const ImagePtr decoded_meta = + testutil::Decode(encoded_meta.data, encoded_meta.size); + ASSERT_NE(decoded_meta, nullptr); // Only the container changed. The pixels, features and metadata should be // identical. EXPECT_TRUE( - testutil::AreImagesEqual(*decoded_metav0.get(), *decoded_metav1.get())); + testutil::AreImagesEqual(*decoded_meta.get(), *decoded_mini.get())); } -INSTANTIATE_TEST_SUITE_P(OnePixel, AvifMetaBoxV1Test, +INSTANTIATE_TEST_SUITE_P(OnePixel, AvifMinimizedImageBoxTest, Combine(/*width=*/Values(1), /*height=*/Values(1), /*depth=*/Values(8), Values(AVIF_PIXEL_FORMAT_YUV444), @@ -108,7 +108,7 @@ INSTANTIATE_TEST_SUITE_P(OnePixel, AvifMetaBoxV1Test, Values(AVIF_TRANSFORM_NONE))); INSTANTIATE_TEST_SUITE_P( - DepthsSubsamplings, AvifMetaBoxV1Test, + DepthsSubsamplings, AvifMinimizedImageBoxTest, Combine(/*width=*/Values(12), /*height=*/Values(34), /*depth=*/Values(8, 10, 12), Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, @@ -118,7 +118,7 @@ INSTANTIATE_TEST_SUITE_P( /*create_xmp=*/Values(false), Values(AVIF_TRANSFORM_NONE))); INSTANTIATE_TEST_SUITE_P( - Dimensions, AvifMetaBoxV1Test, + Dimensions, AvifMinimizedImageBoxTest, Combine(/*width=*/Values(127), /*height=*/Values(200), /*depth=*/Values(8), Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_PLANES_ALL), Values(AVIF_RANGE_FULL), /*create_icc=*/Values(true), @@ -126,7 +126,7 @@ INSTANTIATE_TEST_SUITE_P( Values(AVIF_TRANSFORM_NONE))); INSTANTIATE_TEST_SUITE_P( - Orientation, AvifMetaBoxV1Test, + Orientation, AvifMinimizedImageBoxTest, Combine(/*width=*/Values(16), /*height=*/Values(24), /*depth=*/Values(8), Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_PLANES_ALL), Values(AVIF_RANGE_FULL), /*create_icc=*/Values(true), From 0f8e1f5cc344e9ef9efc05f0250cd9229b9c51f3 Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Wed, 7 Aug 2024 16:36:02 +0200 Subject: [PATCH 2/3] Add explicit casts for depth --- src/read.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/read.c b/src/read.c index e6e79809e2..829e38dee6 100644 --- a/src/read.c +++ b/src/read.c @@ -3888,7 +3888,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset AVIF_CHECKERR(pixiProp, AVIF_RESULT_OUT_OF_MEMORY); pixiProp->u.pixi.planeCount = chromaSubsampling == 0 ? 1 : 3; for (uint8_t plane = 0; plane < pixiProp->u.pixi.planeCount; ++plane) { - pixiProp->u.pixi.planeDepths[plane] = bitDepth; + pixiProp->u.pixi.planeDepths[plane] = (uint8_t)bitDepth; } AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, pixiProp), AVIF_RESULT_OUT_OF_MEMORY); @@ -3943,7 +3943,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset AVIF_CHECKERR(alphaPixiProp, AVIF_RESULT_OUT_OF_MEMORY); memcpy(alphaPixiProp->type, "pixi", 4); alphaPixiProp->u.pixi.planeCount = 1; - alphaPixiProp->u.pixi.planeDepths[0] = bitDepth; + alphaPixiProp->u.pixi.planeDepths[0] = (uint8_t)bitDepth; AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, alphaPixiProp), AVIF_RESULT_OUT_OF_MEMORY); } else { // Placeholders 6, 7 and 8. From 225207c57c54bdd52976e1c7983ba5fd9ab2c9af Mon Sep 17 00:00:00 2001 From: Yannis Guyon Date: Wed, 7 Aug 2024 18:16:13 +0200 Subject: [PATCH 3/3] Initialize conditional fields To avoid Windows MSVC warning C4701: potentially uninitialized local variable used --- src/read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/read.c b/src/read.c index 829e38dee6..e3f4e85820 100644 --- a/src/read.c +++ b/src/read.c @@ -3743,7 +3743,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset } // Chunk sizes - uint32_t fewMetadataBytesFlag, fewCodecConfigBytesFlag, fewItemDataBytesFlag; + uint32_t fewMetadataBytesFlag = 0, fewCodecConfigBytesFlag = 0, fewItemDataBytesFlag = 0; if (hasIcc || hasExif || hasXmp || (hasHdr && hasGainmap)) { AVIF_CHECKERR(avifROStreamReadBits(&s, &fewMetadataBytesFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) few_metadata_bytes_flag; }