From 6724b15fe27b9a60ed400e9afd55968174435d84 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 13 Sep 2025 02:03:44 +0200 Subject: [PATCH 1/4] feat(heif): Read and write 10 and 12 bit images Signed-off-by: Brecht Van Lommel --- src/heif.imageio/heifinput.cpp | 49 +++++++++++++++++++++++++++------ src/heif.imageio/heifoutput.cpp | 37 +++++++++++++++++++++---- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/heif.imageio/heifinput.cpp b/src/heif.imageio/heifinput.cpp index be92acf9f4..93a9fc0956 100644 --- a/src/heif.imageio/heifinput.cpp +++ b/src/heif.imageio/heifinput.cpp @@ -53,6 +53,7 @@ class HeifInput final : public ImageInput { std::string m_filename; int m_subimage = -1; int m_num_subimages = 0; + int m_bitdepth = 0; int m_has_alpha = false; bool m_associated_alpha = true; bool m_keep_unassociated_alpha = false; @@ -203,11 +204,26 @@ HeifInput::seek_subimage(int subimage, int miplevel) return false; } - auto id = (subimage == 0) ? m_primary_id : m_item_ids[subimage - 1]; - m_ihandle = m_ctx->get_image_handle(id); + auto id = (subimage == 0) ? m_primary_id : m_item_ids[subimage - 1]; + m_ihandle = m_ctx->get_image_handle(id); + + m_bitdepth = m_ihandle.get_luma_bits_per_pixel(); + if (m_bitdepth < 0) { + errorfmt("Image has undefined bit depth"); + m_ctx.reset(); + return false; + } else if (!(m_bitdepth == 8 || m_bitdepth == 10 || m_bitdepth == 12)) { + errorfmt("Image has unsupported bit depth {}", m_bitdepth); + m_ctx.reset(); + return false; + } + m_has_alpha = m_ihandle.has_alpha_channel(); - auto chroma = m_has_alpha ? heif_chroma_interleaved_RGBA - : heif_chroma_interleaved_RGB; + auto chroma = m_has_alpha ? (m_bitdepth > 8) + ? heif_chroma_interleaved_RRGGBBAA_LE + : heif_chroma_interleaved_RGBA + : (m_bitdepth > 8) ? heif_chroma_interleaved_RRGGBB_LE + : heif_chroma_interleaved_RGB; #if 0 try { m_himage = m_ihandle.decode_image(heif_colorspace_RGB, chroma); @@ -238,11 +254,14 @@ HeifInput::seek_subimage(int subimage, int miplevel) } #endif - int bits = m_himage.get_bits_per_pixel(heif_channel_interleaved); - m_spec = ImageSpec(m_himage.get_width(heif_channel_interleaved), - m_himage.get_height(heif_channel_interleaved), bits / 8, - TypeUInt8); + m_spec = ImageSpec(m_himage.get_width(heif_channel_interleaved), + m_himage.get_height(heif_channel_interleaved), + m_has_alpha ? 4 : 3, + (m_bitdepth > 8) ? TypeUInt16 : TypeUInt8); + if (m_bitdepth > 8) { + m_spec.attribute("oiio:BitsPerSample", m_bitdepth); + } m_spec.set_colorspace("srgb_rec709_scene"); #if LIBHEIF_HAVE_VERSION(1, 12, 0) @@ -402,7 +421,19 @@ HeifInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, return false; } hdata += (y - m_spec.y) * ystride; - memcpy(data, hdata, m_spec.width * m_spec.pixel_bytes()); + if (m_spec.format == TypeUInt16) { + // Convert from 10 or 12 bits to 16 bits. Read little endian + // zero padded bits, and shift to scale up. + const uint8_t* in = hdata; + uint16_t* out = static_cast(data); + const int bitshift = 16 - m_bitdepth; + const size_t num_values = m_spec.width * m_spec.nchannels; + for (size_t i = 0; i < num_values; i++, out++, in += 2) { + *out = uint16_t((in[1] << 8) | in[0]) << bitshift; + } + } else { + memcpy(data, hdata, m_spec.width * m_spec.pixel_bytes()); + } return true; } diff --git a/src/heif.imageio/heifoutput.cpp b/src/heif.imageio/heifoutput.cpp index 2d45f50920..fb1bf0cabb 100644 --- a/src/heif.imageio/heifoutput.cpp +++ b/src/heif.imageio/heifoutput.cpp @@ -45,6 +45,7 @@ class HeifOutput final : public ImageOutput { heif::Encoder m_encoder { heif_compression_HEVC }; std::vector scratch; std::vector m_tilebuffer; + int m_bitdepth = 0; }; @@ -104,19 +105,31 @@ HeifOutput::open(const std::string& name, const ImageSpec& newspec, m_filename = name; - m_spec.set_format(TypeUInt8); // Only uint8 for now + m_bitdepth = m_spec.format.size() > TypeUInt8.size() ? 10 : 8; + m_bitdepth = m_spec.get_int_attribute("oiio:BitsPerSample", m_bitdepth); + if (m_bitdepth == 10 || m_bitdepth == 12) { + m_spec.set_format(TypeUInt16); + } else if (m_bitdepth == 8) { + m_spec.set_format(TypeUInt8); + } else { + errorfmt("Unsupported bit depth {}", m_bitdepth); + return false; + } try { m_ctx.reset(new heif::Context); m_himage = heif::Image(); static heif_chroma chromas[/*nchannels*/] = { heif_chroma_undefined, heif_chroma_monochrome, - heif_chroma_undefined, heif_chroma_interleaved_RGB, - heif_chroma_interleaved_RGBA }; + heif_chroma_undefined, + (m_bitdepth == 8) ? heif_chroma_interleaved_RGB + : heif_chroma_interleaved_RRGGBB_LE, + (m_bitdepth == 8) ? heif_chroma_interleaved_RGBA + : heif_chroma_interleaved_RRGGBBAA_LE }; m_himage.create(newspec.width, newspec.height, heif_colorspace_RGB, chromas[m_spec.nchannels]); m_himage.add_plane(heif_channel_interleaved, newspec.width, - newspec.height, 8 * m_spec.nchannels /*bit depth*/); + newspec.height, m_bitdepth); m_encoder = heif::Encoder(heif_compression_HEVC); auto compqual = m_spec.decode_compression_metadata("", 75); @@ -161,7 +174,21 @@ HeifOutput::write_scanline(int y, int /*z*/, TypeDesc format, const void* data, uint8_t* hdata = m_himage.get_plane(heif_channel_interleaved, &hystride); #endif hdata += hystride * (y - m_spec.y); - memcpy(hdata, data, hystride); + if (m_spec.format == TypeUInt16) { + // Convert from 16 bits to 10 or 12 bits. Shift to scale down and + // output zero padded little endian. + const uint16_t* in = static_cast(data); + uint8_t* out = hdata; + const int bitshift = 16 - m_bitdepth; + const size_t num_values = m_spec.width * m_spec.nchannels; + for (size_t i = 0; i < num_values; i++, out += 2, in++) { + const uint16_t v = *in >> bitshift; + out[0] = (uint8_t)(v & 0xFF); + out[1] = (uint8_t)(v >> 8); + } + } else { + memcpy(hdata, data, hystride); + } return true; } From b52c3ab15da16afb3cf73ef2dcce92ded765adee Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 12 Sep 2025 22:59:52 +0200 Subject: [PATCH 2/4] feat(heif): Read and write CICP By setting the matrix coefficients, the library will perform RGB to YUV conversion on write. The YUV to RBG conversion was already happening on read automatically. Signed-off-by: Brecht Van Lommel --- src/doc/builtinplugins.rst | 6 ++++ src/heif.imageio/heifinput.cpp | 30 +++++++++++++++++- src/heif.imageio/heifoutput.cpp | 26 +++++++++++++-- testsuite/heif/ref/out-libheif1.12-orient.txt | 28 ++++++++++++++++ testsuite/heif/ref/out-libheif1.4.txt | 23 ++++++++++++++ testsuite/heif/ref/out-libheif1.5.txt | 23 ++++++++++++++ testsuite/heif/ref/out-libheif1.9-alt2.txt | 28 ++++++++++++++++ .../heif/ref/out-libheif1.9-with-av1-alt2.txt | 23 ++++++++++++++ .../heif/ref/out-libheif1.9-with-av1.txt | 23 ++++++++++++++ testsuite/heif/ref/out-libheif1.9.txt | 28 ++++++++++++++++ testsuite/heif/ref/test-10bit.avif | Bin 0 -> 820 bytes testsuite/heif/run.py | 6 +++- 12 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 testsuite/heif/ref/test-10bit.avif diff --git a/src/doc/builtinplugins.rst b/src/doc/builtinplugins.rst index 847010c9d4..ba7ba6c0f8 100644 --- a/src/doc/builtinplugins.rst +++ b/src/doc/builtinplugins.rst @@ -688,6 +688,12 @@ preferred except when legacy file access is required. - string - Color space (see Section :ref:`sec-metadata-color`). We currently assume that any RGBE files encountered are linear with sRGB primaries. + * - ``CICP`` + - int[4] + - Coding-independent code points to describe the color profile. + * - ``oiio:BitsPerSample`` + - int + - Bits per sample in the file: 8, 10 or 12. * - ``heif:Orientation`` - int - If the configuration option ``heif:reorient`` is nonzero and diff --git a/src/heif.imageio/heifinput.cpp b/src/heif.imageio/heifinput.cpp index 93a9fc0956..2a68051051 100644 --- a/src/heif.imageio/heifinput.cpp +++ b/src/heif.imageio/heifinput.cpp @@ -36,7 +36,11 @@ class HeifInput final : public ImageInput { const char* format_name(void) const override { return "heif"; } int supports(string_view feature) const override { - return feature == "exif"; + return feature == "exif" +#if LIBHEIF_HAVE_VERSION(1, 9, 0) + || feature == "cicp" +#endif + ; } bool valid_file(const std::string& filename) const override; bool open(const std::string& name, ImageSpec& newspec) override; @@ -264,6 +268,30 @@ HeifInput::seek_subimage(int subimage, int miplevel) } m_spec.set_colorspace("srgb_rec709_scene"); +#if LIBHEIF_HAVE_VERSION(1, 9, 0) + // Read CICP. Have to use the C API to get it from the image handle, + // the one on the decoded image is not what was written in the file. + enum heif_color_profile_type profile_type + = heif_image_handle_get_color_profile_type( + m_ihandle.get_raw_image_handle()); + if (profile_type == heif_color_profile_type_nclx) { + heif_color_profile_nclx* nclx = nullptr; + const heif_error err = heif_image_handle_get_nclx_color_profile( + m_ihandle.get_raw_image_handle(), &nclx); + + if (nclx) { + if (err.code == heif_error_Ok) { + const int cicp[4] = { int(nclx->color_primaries), + int(nclx->transfer_characteristics), + int(nclx->matrix_coefficients), + int(nclx->full_range_flag ? 1 : 0) }; + m_spec.attribute("CICP", TypeDesc(TypeDesc::INT, 4), cicp); + } + heif_nclx_color_profile_free(nclx); + } + } +#endif + #if LIBHEIF_HAVE_VERSION(1, 12, 0) // Libheif >= 1.12 added API call to find out if the image is associated // alpha (i.e. colors are premultiplied). diff --git a/src/heif.imageio/heifoutput.cpp b/src/heif.imageio/heifoutput.cpp index fb1bf0cabb..0bd6c7d634 100644 --- a/src/heif.imageio/heifoutput.cpp +++ b/src/heif.imageio/heifoutput.cpp @@ -26,7 +26,11 @@ class HeifOutput final : public ImageOutput { const char* format_name(void) const override { return "heif"; } int supports(string_view feature) const override { - return feature == "alpha" || feature == "exif" || feature == "tiles"; + return feature == "alpha" || feature == "exif" || feature == "tiles" +#if LIBHEIF_HAVE_VERSION(1, 9, 0) + || feature == "cicp" +#endif + ; } bool open(const std::string& name, const ImageSpec& spec, OpenMode mode) override; @@ -234,8 +238,26 @@ HeifOutput::close() } else if (compqual.first == "none") { m_encoder.set_lossless(true); } + heif::Context::EncodingOptions options; +#if LIBHEIF_HAVE_VERSION(1, 9, 0) + // Write CICP. we can only set output_nclx_profile with the C API. + std::unique_ptr + nclx(heif_nclx_color_profile_alloc(), heif_nclx_color_profile_free); + const ParamValue* p = m_spec.find_attribute("CICP", + TypeDesc(TypeDesc::INT, 4)); + if (p) { + const int* cicp = static_cast(p->data()); + nclx->color_primaries = heif_color_primaries(cicp[0]); + nclx->transfer_characteristics = heif_transfer_characteristics( + cicp[1]); + nclx->matrix_coefficients = heif_matrix_coefficients(cicp[2]); + nclx->full_range_flag = cicp[3]; + options.output_nclx_profile = nclx.get(); + } +#endif encode_exif(m_spec, exifblob, endian::big); - m_ihandle = m_ctx->encode_image(m_himage, m_encoder); + m_ihandle = m_ctx->encode_image(m_himage, m_encoder, options); std::vector head { 'E', 'x', 'i', 'f', 0, 0 }; exifblob.insert(exifblob.begin(), head.begin(), head.end()); try { diff --git a/testsuite/heif/ref/out-libheif1.12-orient.txt b/testsuite/heif/ref/out-libheif1.12-orient.txt index a3102659c6..8c52196667 100644 --- a/testsuite/heif/ref/out-libheif1.12-orient.txt +++ b/testsuite/heif/ref/out-libheif1.12-orient.txt @@ -39,6 +39,34 @@ ref/IMG_7702_small.heic : 512 x 300, 3 channel, uint8 heif Exif:SubsecTimeOriginal: "006" Exif:WhiteBalance: 0 (auto) oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/Chimera-AV1-8bit-162.avif +ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif + SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 + channel list: R, G, B + oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8211F56BBABDC7615CCAF67CBF49741D1A292D2E diff --git a/testsuite/heif/ref/out-libheif1.4.txt b/testsuite/heif/ref/out-libheif1.4.txt index ba85f95394..70a7ee941a 100644 --- a/testsuite/heif/ref/out-libheif1.4.txt +++ b/testsuite/heif/ref/out-libheif1.4.txt @@ -44,6 +44,29 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 channel list: R, G, B oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8211F56BBABDC7615CCAF67CBF49741D1A292D2E diff --git a/testsuite/heif/ref/out-libheif1.5.txt b/testsuite/heif/ref/out-libheif1.5.txt index 8fb8e93fdf..b97da782f6 100644 --- a/testsuite/heif/ref/out-libheif1.5.txt +++ b/testsuite/heif/ref/out-libheif1.5.txt @@ -44,6 +44,29 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 channel list: R, G, B oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8211F56BBABDC7615CCAF67CBF49741D1A292D2E diff --git a/testsuite/heif/ref/out-libheif1.9-alt2.txt b/testsuite/heif/ref/out-libheif1.9-alt2.txt index e24120d453..28e67619ea 100644 --- a/testsuite/heif/ref/out-libheif1.9-alt2.txt +++ b/testsuite/heif/ref/out-libheif1.9-alt2.txt @@ -39,6 +39,34 @@ ref/IMG_7702_small.heic : 512 x 300, 3 channel, uint8 heif Exif:SubsecTimeOriginal: "006" Exif:WhiteBalance: 0 (auto) oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/Chimera-AV1-8bit-162.avif +ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif + SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 + channel list: R, G, B + oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8064B23A1A995B0D6525AFB5248EEC6C730BBB6C diff --git a/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt b/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt index 2433705c6b..28e67619ea 100644 --- a/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt +++ b/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt @@ -44,6 +44,29 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 channel list: R, G, B oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8064B23A1A995B0D6525AFB5248EEC6C730BBB6C diff --git a/testsuite/heif/ref/out-libheif1.9-with-av1.txt b/testsuite/heif/ref/out-libheif1.9-with-av1.txt index 40d4b662df..81a2e7157b 100644 --- a/testsuite/heif/ref/out-libheif1.9-with-av1.txt +++ b/testsuite/heif/ref/out-libheif1.9-with-av1.txt @@ -44,6 +44,29 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 channel list: R, G, B oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8211F56BBABDC7615CCAF67CBF49741D1A292D2E diff --git a/testsuite/heif/ref/out-libheif1.9.txt b/testsuite/heif/ref/out-libheif1.9.txt index 2ddc23c253..81a2e7157b 100644 --- a/testsuite/heif/ref/out-libheif1.9.txt +++ b/testsuite/heif/ref/out-libheif1.9.txt @@ -39,6 +39,34 @@ ref/IMG_7702_small.heic : 512 x 300, 3 channel, uint8 heif Exif:SubsecTimeOriginal: "006" Exif:WhiteBalance: 0 (auto) oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/Chimera-AV1-8bit-162.avif +ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif + SHA-1: F8FDAF1BD56A21E3AF99CF8EE7FA45434D2826C7 + channel list: R, G, B + oiio:ColorSpace: "srgb_rec709_scene" +Reading ref/test-10bit.avif +ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + channel list: R, G, B, A + Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" +Reading cicp_pq.avif +cicp_pq.avif : 16 x 16, 4 channel, uint10 heif + SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + channel list: R, G, B, A + CICP: 9, 16, 9, 1 + Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" + Exif:ExifVersion: "0230" + Exif:FlashPixVersion: "0100" + Exif:ImageHistory: "oiiotool --pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png" + heif:UnassociatedAlpha: 1 + oiio:BitsPerSample: 10 + oiio:ColorSpace: "srgb_rec709_scene" Reading ../oiio-images/heif/greyhounds-looking-for-a-table.heic ../oiio-images/heif/greyhounds-looking-for-a-table.heic : 3024 x 4032, 3 channel, uint8 heif SHA-1: 8211F56BBABDC7615CCAF67CBF49741D1A292D2E diff --git a/testsuite/heif/ref/test-10bit.avif b/testsuite/heif/ref/test-10bit.avif new file mode 100644 index 0000000000000000000000000000000000000000..9dab1f234699aa6fe1f676107597d5bec4509d92 GIT binary patch literal 820 zcmZ8fJ%|%Q6n?YGsTaZ_o(B<>+v^T7VP_MU&BDQ%KRhs8B{nweW|9o-&$v4YNh5b~ zs92pgwrRB0+QMEd5kXtI=1MyoMc>RC1!vfK@B7~OX7;@q0JuDqbKy_<5O73%NR=OP zA2E3mG3hHF$JTfdrnw6scp#Vl(yj?EBMk_yJsu{3LY-YGfMS5W%PqP!Ff{Z1FSCIv z97cJ`kZpv0&SU*!I=X>?<%t0P)S@cmABmCwNuu()v%I)hT%-aso^TN)Xv_YTwh!T! z+T|k81XB)<^UF{08COK1R4SqQqra;tEH;l36>lH4dWq%DIg+rqNM)Am4d5C*$^OWVa<8hcAV$X=~&RQ_Y0^L>Y zE6JjPFE2o8wNn1|WwEvRaA0p%V9h-_0{Z&?Y4h{o((20x;q#ks?)=!ieSH1n+g((z zDD@}1Q~~!Zke}@28ykU>36CZE)x?x6L$4;{5$p>V??wKI z?d_ZOnq9MM)__e-&uleXokr8?H0`Ea@4Ag1?R6ZRI Date: Sun, 14 Sep 2025 02:41:12 +0200 Subject: [PATCH 3/4] Fix bit range conversion Signed-off-by: Brecht Van Lommel --- src/heif.imageio/heifinput.cpp | 29 ++++++++++++------- src/heif.imageio/heifoutput.cpp | 29 +++++++++++-------- testsuite/heif/ref/out-libheif1.12-orient.txt | 4 +-- testsuite/heif/ref/out-libheif1.4.txt | 4 +-- testsuite/heif/ref/out-libheif1.5.txt | 4 +-- testsuite/heif/ref/out-libheif1.9-alt2.txt | 4 +-- .../heif/ref/out-libheif1.9-with-av1-alt2.txt | 4 +-- .../heif/ref/out-libheif1.9-with-av1.txt | 4 +-- testsuite/heif/ref/out-libheif1.9.txt | 4 +-- 9 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/heif.imageio/heifinput.cpp b/src/heif.imageio/heifinput.cpp index 2a68051051..4ba8e2fb61 100644 --- a/src/heif.imageio/heifinput.cpp +++ b/src/heif.imageio/heifinput.cpp @@ -3,7 +3,9 @@ // https://github.com/AcademySoftwareFoundation/OpenImageIO #include +#include #include +#include #include #include @@ -224,9 +226,13 @@ HeifInput::seek_subimage(int subimage, int miplevel) m_has_alpha = m_ihandle.has_alpha_channel(); auto chroma = m_has_alpha ? (m_bitdepth > 8) - ? heif_chroma_interleaved_RRGGBBAA_LE + ? littleendian() + ? heif_chroma_interleaved_RRGGBBAA_LE + : heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RGBA - : (m_bitdepth > 8) ? heif_chroma_interleaved_RRGGBB_LE + : (m_bitdepth > 8) ? littleendian() + ? heif_chroma_interleaved_RRGGBB_LE + : heif_chroma_interleaved_RRGGBB_BE : heif_chroma_interleaved_RGB; #if 0 try { @@ -449,15 +455,18 @@ HeifInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, return false; } hdata += (y - m_spec.y) * ystride; - if (m_spec.format == TypeUInt16) { - // Convert from 10 or 12 bits to 16 bits. Read little endian - // zero padded bits, and shift to scale up. - const uint8_t* in = hdata; - uint16_t* out = static_cast(data); - const int bitshift = 16 - m_bitdepth; + if (m_bitdepth == 10 || m_bitdepth == 12) { const size_t num_values = m_spec.width * m_spec.nchannels; - for (size_t i = 0; i < num_values; i++, out++, in += 2) { - *out = uint16_t((in[1] << 8) | in[0]) << bitshift; + const uint16_t* hdata16 = reinterpret_cast(hdata); + uint16_t* data16 = static_cast(data); + if (m_bitdepth == 10) { + for (size_t i = 0; i < num_values; ++i) { + data16[i] = bit_range_convert<10, 16>(hdata16[i]); + } + } else { + for (size_t i = 0; i < num_values; ++i) { + data16[i] = bit_range_convert<12, 16>(hdata16[i]); + } } } else { memcpy(data, hdata, m_spec.width * m_spec.pixel_bytes()); diff --git a/src/heif.imageio/heifoutput.cpp b/src/heif.imageio/heifoutput.cpp index 0bd6c7d634..3847b60062 100644 --- a/src/heif.imageio/heifoutput.cpp +++ b/src/heif.imageio/heifoutput.cpp @@ -4,7 +4,9 @@ #include +#include #include +#include #include #include @@ -127,9 +129,11 @@ HeifOutput::open(const std::string& name, const ImageSpec& newspec, = { heif_chroma_undefined, heif_chroma_monochrome, heif_chroma_undefined, (m_bitdepth == 8) ? heif_chroma_interleaved_RGB - : heif_chroma_interleaved_RRGGBB_LE, + : littleendian() ? heif_chroma_interleaved_RRGGBB_LE + : heif_chroma_interleaved_RRGGBB_BE, (m_bitdepth == 8) ? heif_chroma_interleaved_RGBA - : heif_chroma_interleaved_RRGGBBAA_LE }; + : littleendian() ? heif_chroma_interleaved_RRGGBBAA_LE + : heif_chroma_interleaved_RRGGBBAA_BE }; m_himage.create(newspec.width, newspec.height, heif_colorspace_RGB, chromas[m_spec.nchannels]); m_himage.add_plane(heif_channel_interleaved, newspec.width, @@ -178,17 +182,18 @@ HeifOutput::write_scanline(int y, int /*z*/, TypeDesc format, const void* data, uint8_t* hdata = m_himage.get_plane(heif_channel_interleaved, &hystride); #endif hdata += hystride * (y - m_spec.y); - if (m_spec.format == TypeUInt16) { - // Convert from 16 bits to 10 or 12 bits. Shift to scale down and - // output zero padded little endian. - const uint16_t* in = static_cast(data); - uint8_t* out = hdata; - const int bitshift = 16 - m_bitdepth; + if (m_bitdepth == 10 || m_bitdepth == 12) { + const uint16_t* data16 = static_cast(data); + uint16_t* hdata16 = reinterpret_cast(hdata); const size_t num_values = m_spec.width * m_spec.nchannels; - for (size_t i = 0; i < num_values; i++, out += 2, in++) { - const uint16_t v = *in >> bitshift; - out[0] = (uint8_t)(v & 0xFF); - out[1] = (uint8_t)(v >> 8); + if (m_bitdepth == 10) { + for (size_t i = 0; i < num_values; ++i) { + hdata16[i] = bit_range_convert<16, 10>(data16[i]); + } + } else { + for (size_t i = 0; i < num_values; ++i) { + hdata16[i] = bit_range_convert<16, 12>(data16[i]); + } } } else { memcpy(hdata, data, hystride); diff --git a/testsuite/heif/ref/out-libheif1.12-orient.txt b/testsuite/heif/ref/out-libheif1.12-orient.txt index 8c52196667..e9ef2777bb 100644 --- a/testsuite/heif/ref/out-libheif1.12-orient.txt +++ b/testsuite/heif/ref/out-libheif1.12-orient.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.4.txt b/testsuite/heif/ref/out-libheif1.4.txt index 70a7ee941a..48384b5e0d 100644 --- a/testsuite/heif/ref/out-libheif1.4.txt +++ b/testsuite/heif/ref/out-libheif1.4.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.5.txt b/testsuite/heif/ref/out-libheif1.5.txt index b97da782f6..8a371fca47 100644 --- a/testsuite/heif/ref/out-libheif1.5.txt +++ b/testsuite/heif/ref/out-libheif1.5.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.9-alt2.txt b/testsuite/heif/ref/out-libheif1.9-alt2.txt index 28e67619ea..9d92f3b2ed 100644 --- a/testsuite/heif/ref/out-libheif1.9-alt2.txt +++ b/testsuite/heif/ref/out-libheif1.9-alt2.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt b/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt index 28e67619ea..9d92f3b2ed 100644 --- a/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt +++ b/testsuite/heif/ref/out-libheif1.9-with-av1-alt2.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.9-with-av1.txt b/testsuite/heif/ref/out-libheif1.9-with-av1.txt index 81a2e7157b..5253081cdc 100644 --- a/testsuite/heif/ref/out-libheif1.9-with-av1.txt +++ b/testsuite/heif/ref/out-libheif1.9-with-av1.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" diff --git a/testsuite/heif/ref/out-libheif1.9.txt b/testsuite/heif/ref/out-libheif1.9.txt index 81a2e7157b..5253081cdc 100644 --- a/testsuite/heif/ref/out-libheif1.9.txt +++ b/testsuite/heif/ref/out-libheif1.9.txt @@ -46,7 +46,7 @@ ref/Chimera-AV1-8bit-162.avif : 480 x 270, 3 channel, uint8 heif oiio:ColorSpace: "srgb_rec709_scene" Reading ref/test-10bit.avif ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: FFDB2271C871832C052DA19F0C0B18BB8AFC99EE + SHA-1: A217653C4E10FEBF080E26F9FC78F572184B1FDA channel list: R, G, B, A Software: "OpenImageIO 3.2.0.0dev : B4BD496D92983E84F1FD621682CAB821C1E2126C" Exif:ExifVersion: "0230" @@ -57,7 +57,7 @@ ref/test-10bit.avif : 16 x 16, 4 channel, uint10 heif oiio:ColorSpace: "srgb_rec709_scene" Reading cicp_pq.avif cicp_pq.avif : 16 x 16, 4 channel, uint10 heif - SHA-1: 973A58CD6B420F205AA89967C18A60CD8ED8894B + SHA-1: 0F3CAB52D479BC23E9C981DBADDFEF1F792E5540 channel list: R, G, B, A CICP: 9, 16, 9, 1 Software: "OpenImageIO 3.2.0.0dev : A50DC799B2B4CA667217608C0F82302455E5D32A" From 311d7adf91467c61ee1b56034476be5be6833770 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 14 Sep 2025 02:44:57 +0200 Subject: [PATCH 4/4] Fix libaom error with RGB matrix coefficients and chroma subsampling Signed-off-by: Brecht Van Lommel --- src/heif.imageio/heifoutput.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/heif.imageio/heifoutput.cpp b/src/heif.imageio/heifoutput.cpp index 3847b60062..6ed1dbb439 100644 --- a/src/heif.imageio/heifoutput.cpp +++ b/src/heif.imageio/heifoutput.cpp @@ -259,6 +259,10 @@ HeifOutput::close() nclx->matrix_coefficients = heif_matrix_coefficients(cicp[2]); nclx->full_range_flag = cicp[3]; options.output_nclx_profile = nclx.get(); + // Chroma subsampling is incompatible with RGB. + if (nclx->matrix_coefficients == heif_matrix_coefficients_RGB_GBR) { + m_encoder.set_string_parameter("chroma", "444"); + } } #endif encode_exif(m_spec, exifblob, endian::big);