diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index a27d400240..ffe49bc14e 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -40,6 +40,8 @@ Checkboxes may be marked complete only when the implementation and the verificat
- [x] Implement the H.273 V4 IPT-C2 and reversible YCgCo-Re/YCgCo-Ro transforms in both directions as closed SIMD operators, including their reduced RGB precision, signed integer lifting, full- and limited-range behavior, and 4:4:4 constraint.
- [x] Verify IPT-C2 against independently calculated matrix values and verify YCgCo-Re/YCgCo-Ro against exact 8/10/12-bit code values, limited-range black/white values, and SIMD-lane/scalar-tail parity.
- [x] Implement SIMD 4:2:0 and 4:2:2 encoder downsampling with odd-width and odd-height tails, and verify the stored 8/12-bit chroma samples against independently encoded full-resolution planes.
+ - [x] Move the H.273 parameter resolver, closed color operators, transfer functions, sample loading/storage, chroma filtering, and RGB packing into one shared HEIF color pipeline used by both AV1 and HEVC rather than maintaining codec-specific arithmetic copies.
+ - [x] Implement sequential pooled HEVC conversion in both directions for monochrome, 4:2:0, 4:2:2, and 4:4:4; independent luma/chroma precision; 8/10/12-bit full/limited ranges; and all six progressive 4:2:0 chroma sample locations. Eight-bit paths use JPEG's optimized RGB plane contracts, while high-bit-depth paths retain 16-bit packed pixels.
- [ ] Complete the SIMD YUV/CICP paths for every supported AV1 bit depth, chroma format, range, matrix, transfer function, color primary, and chroma position, with scalar fallback only when hardware vectorization is unavailable or the operation is inherently non-vectorizable.
- [x] Apply ICC conversion only after the SIMD YUV/CICP stage, alpha composition, grid assembly, and presentation transforms have produced the presented RGB image; retain ImageSharp's shared ICC converter and optimize reusable bulk kernels rather than creating a HEIF-specific color-management implementation.
- [ ] Verify ICC preservation, conversion, compaction, and metadata skipping for grids, alpha-composited images, every presented sequence frame, and the completed HEVC path.
@@ -48,6 +50,7 @@ Checkboxes may be marked complete only when the implementation and the verificat
- [ ] Benchmark representative 8/10/12-bit end-to-end AVIF and HEIC color pipelines with and without ICC conversion, recording absolute timings and allocations before marking Phase 5 complete.
- [x] Add permanent frame-wide `Av1ColorConversionBenchmarks` coverage for sequential 1920x1080 YUV 4:2:0 conversion in both directions at 8, 10, and 12 bits, including managed-allocation reporting.
- [x] Record the .NET 10 short-run AV1 baseline on 2026-08-25. SIMD decode measured 4.025/2.803/2.742 ms and encode measured 3.750/2.555/2.450 ms at 8/10/12 bits with 488-744 B allocated per frame. Forced-scalar decode measured 31.15/29.47/28.40 ms and encode measured 19.19/15.77/15.14 ms, making the normal SIMD pipeline 7.7-10.5 times faster for decode and 5.1-6.2 times faster for encode on this machine.
+ - [x] Add permanent frame-wide `HevcColorConversionBenchmarks` coverage and record the .NET 10 short-run plane-conversion baseline on 2026-08-26. SIMD decode measured 1.141/2.640/2.668 ms and encode measured 2.089/2.481/2.412 ms at 8/10/12 bits with 504-712 B allocated per frame. Forced-scalar decode measured 13.42/28.35/27.60 ms and encode measured 14.82/14.67/14.45 ms, making the normal SIMD pipeline 10.3-11.8 times faster for decode and 5.9-7.1 times faster for encode on this machine.
- [ ] Add representative ICC-enabled AVIF cases and the equivalent HEVC cases after the HEVC reconstruction path is complete.
- [x] **Completed:** remove production `ToArray()` materializations from the HEIF implementation and make every retained copy an explicit ownership decision.
- [x] Store AV1 palette colors and HEVC short-term reference-picture state inline at their normative fixed bounds.
@@ -201,8 +204,8 @@ This snapshot pins or classifies the available references and failures; it does
| Managed implementation | Normative behavior | Reviewed implementation reference | Use |
| --- | --- | --- | --- |
-| `Av1YuvConverter.ConvertToRgb`, `ConvertFromRgb`, scalar row conversion, chroma reconstruction, and chroma downsampling | ITU-T H.273 (V4) equations 14-16 and 27-84, including limited/full-range scaling, chromaticity-derived equations 39-47, YCgCo equations 51-57, constant-luminance equations 66-75, IPT-C2, YCgCo-Re/YCgCo-Ro, and the PQ/HLG ICtCp matrices; AV1 section 6.4.2 chroma sample positions | Official ITU-T H.273 (V4) (07/2024); libavif `src/reformat.c` and `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826`; libaom `aom/aom_image.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Scalar behavioral oracle for full/limited-range conversion at 8, 10, and 12 bits. Decode and encode cover every H.273 V4 matrix code point through 17: identity, YCgCo, coefficient-based NCL, both fixed and chromaticity-derived constant/non-constant-luminance systems, SMPTE ST 2085, ICtCp, IPT-C2, YCgCo-Re, and YCgCo-Ro. The common paths cover monochrome, YUV 4:2:0, 4:2:2, and 4:4:4 with AV1 chroma sample positioning; identity and the reversible lifting transforms enforce their full-chroma constraint. Limited-range YCgCo retains the 219-code scale inherited from its R/G/B inputs instead of applying YCbCr's unrelated 224-code chroma range. YCgCo-Re and YCgCo-Ro apply range adjustment at their reduced-precision RGB code values before signed integer lifting. Chromaticity derivation uses every defined H.273 primary and matches libavif's BT.709 fallback for unspecified or reserved primaries. The ICtCp and IPT-C2 inverses are derived from their exact H.273 matrices rather than unrelated display conversions. The closed operators provide `Vector512`, `Vector256`, and `Vector128` implementations with one scalar tail. |
-| `Av1TransferFunctions` | ITU-T H.273 (V4) Table 3 transfer characteristics 1-18 | Official ITU-T H.273 (V4) (07/2024); libavif `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826` | Apply every AV1-signallable transfer function required by constant-luminance and ICtCp color conversion. Retain the H.273 normalized PQ and HLG definitions; do not import libavif's display-oriented 203-nit scaling or HLG OOTF into codec sample interpretation. Use libavif's midpoint convention only for the non-bijective zero code of the two logarithmic curves. |
+| `HeifColorConversionParameters`, `HeifColorConverter`, `Av1YuvConverter`, and `HevcYuvConverter` | ITU-T H.273 (V4) equations 14-16 and 27-84, including limited/full-range scaling, chromaticity-derived equations 39-47, YCgCo equations 51-57, constant-luminance equations 66-75, IPT-C2, YCgCo-Re/YCgCo-Ro, and the PQ/HLG ICtCp matrices; AV1 section 6.4.2 and HEVC Annex E chroma sample positions | Official ITU-T H.273 (V4) (07/2024); libavif `src/reformat.c` and `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826`; libaom `aom/aom_image.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Scalar behavioral oracle for full/limited-range conversion at 8, 10, and 12 bits. Decode and encode cover every H.273 V4 matrix code point through 17: identity, YCgCo, coefficient-based NCL, both fixed and chromaticity-derived constant/non-constant-luminance systems, SMPTE ST 2085, ICtCp, IPT-C2, YCgCo-Re, and YCgCo-Ro. Shared paths cover monochrome, YUV 4:2:0, 4:2:2, and 4:4:4; AV1 centered/co-sited placement; and all six HEVC progressive 4:2:0 sample locations. Identity and reversible lifting enforce their full-chroma constraint. Limited-range YCgCo retains the 219-code scale inherited from its R/G/B inputs instead of applying YCbCr's unrelated 224-code chroma range. YCgCo-Re and YCgCo-Ro apply range adjustment at reduced-precision RGB code values before signed integer lifting. Chromaticity derivation uses every defined H.273 primary and matches libavif's BT.709 fallback for unspecified or reserved primaries. ICtCp and IPT-C2 inverses come from the exact H.273 matrices rather than unrelated display conversions. Closed operators provide `Vector512`, `Vector256`, and `Vector128` implementations with one scalar tail. Eight-bit codec wrappers use JPEG's RGB plane pack/unpack contracts; high-bit-depth wrappers retain native 16-bit packed pixels. |
+| `HeifTransferFunctions` | ITU-T H.273 (V4) Table 3 transfer characteristics 1-18 | Official ITU-T H.273 (V4) (07/2024); libavif `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826` | Apply every HEVC/AV1-signallable transfer function required by constant-luminance, ICtCp, and IPT-C2 conversion. Retain the H.273 normalized PQ and HLG definitions; do not import libavif's display-oriented 203-nit scaling or HLG OOTF into codec sample interpretation. Use libavif's midpoint convention only for the non-bijective zero code of the two logarithmic curves. |
| `ObuReader.ReadSequenceHeader`, `ReadUncompressedFrameHeader`, decoder-model parsing, and operating-parameter consumption | AV1 sections 5.5.2 through 5.5.4 sequence timing and decoder-model syntax, section 5.9.2 uncompressed frame-header syntax, and section 5.9.31 temporal-point syntax | libaom `av1/decoder/decodeframe.c` functions `av1_read_decoder_model_info`, `av1_read_op_parameters_info`, `read_temporal_point_info`, and `read_uncompressed_header`, plus `common/av1_config.c`, at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Read the normative 32-bit decoding-tick field; consume operating-point buffer delays and the layer-applicable frame removal times needed to keep a non-reduced still-image sequence aligned; and read presentation time only under the normative decoder-model condition. Retain no scheduling or playback behavior from those values and introduce no ISO BMFF timing, track, sample-table, or sequence surface. |
| `Av1FrameBuffer` high-bit-depth sample layout and `Av1YuvConverter` 10/12-bit packed-pixel conversion | AV1 section 6.4.1 bit depth and H.273 sample-range scaling | libaom `aom_scale/yv12config.h`, `av1/common/idct.c`, and `av1/common/reconintra.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f`; libavif `src/avif.c` and `src/reformat.c` at `092276ce89098ead06db80975173191e5fee1826` | Establish two-byte native sample storage with sample-unit strides for 10/12-bit reconstruction and use ImageSharp's existing `Rgb48` pixel-operation paths in both directions so packed-pixel staging does not reduce high-bit-depth samples to eight bits. |
| `Av1PredictionDecoder`, `Av1HighBitDepthPredictor`, `Av1ChromaFromLumaContext`, `Av1PartitionInfo`, and the scalar DC, directional, Paeth, smooth, filter-intra, and chroma-from-luma predictors | AV1 sections 7.11.2 and 7.11.2.3 intra prediction | libaom `aom_dsp/intrapred.c`, `av1/common/reconintra.c`, `av1/common/av1_common_int.h`, `av1/common/blockd.h`, `av1/common/cfl.c`, and `av1/common/cfl.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Behavioral oracle for luma/chroma mode-neighbor addressing, directional upsampling, Paeth selection, smooth normalization, filter-intra taps, high-bit-depth clipping, chroma-from-luma storage/subsampling, and chroma-from-luma row strides. Existing managed scalar tables and predictors remain the implementation base. The WIP rectangular byte-pipeline smooth digest expectations encode width/height-swapped weights and must be replaced only from an independently generated oracle, not regenerated from this implementation. |
@@ -472,16 +475,19 @@ Tasks:
- [ ] Implement monochrome, 4:2:0, 4:2:2, and 4:4:4 plane access with every valid signaled chroma sample position.
- [x] Implement SIMD AV1 4:2:0 and 4:2:2 chroma reconstruction and downsampling, including centered/colocated reconstruction and odd image edges.
- - [ ] Complete the HEVC plane paths and the exhaustive AV1/HEVC chroma-position matrix before closing this item.
+ - [x] Implement pooled HEVC plane conversion for monochrome, 4:2:0, 4:2:2, and 4:4:4, including exact encode-coordinate and decode-interpolation coverage for all six progressive 4:2:0 sample locations.
+ - [ ] Connect reconstructed HEVC pictures and complete the independently verified AV1/HEVC chroma-position matrix before closing this item.
- [ ] Implement full- and limited-range expansion for 8, 10, and 12-bit samples across every supported plane layout.
- [x] Implement AV1 range expansion and quantization with SIMD-first byte/ushort loaders and storers and pooled planar rows.
- - [ ] Complete the equivalent HEVC paths and independently verify the full cross-product.
+ - [x] Implement the equivalent HEVC plane paths with independent luma/chroma precision, native 16-bit high-depth staging, and exact limited-range endpoint coverage.
+ - [ ] Independently verify the complete HEVC range/precision/plane-layout cross-product after reconstruction is connected.
- [ ] Implement every non-reserved HEVC/AV1 color-primary, transfer-characteristic, and matrix-coefficient signaling path, including identity conversion, with correct fixed-point rounding and clipping.
- - [x] Implement the AV1 H.273 matrix and transfer-function operators in both directions, including identity, derived coefficients, constant luminance, SMPTE ST 2085, and PQ/HLG ICtCp.
- - [ ] Add independent conformance vectors for signaling combinations that libavif deliberately rejects, then complete and verify the HEVC operator set.
+ - [x] Implement one shared H.273 matrix and transfer-function operator set in both directions for AV1 and HEVC, including identity, derived coefficients, constant luminance, SMPTE ST 2085, PQ/HLG ICtCp, IPT-C2, YCgCo-Re, and YCgCo-Ro.
+ - [ ] Add independent conformance vectors for signaling combinations that libavif deliberately rejects, then verify the operator set through completed HEVC reconstruction.
- [ ] Make SIMD the default decode path for YUV range expansion, chroma reconstruction, H.273 matrix and transfer conversion, clamping, and 8/10/12-bit RGB packing. Use the established static operator pattern with `Vector512`, `Vector256`, and `Vector128` dispatch followed by one scalar tail that remains the behavioral oracle.
- [x] AV1 uses the JPEG-style static operator contract and descending SIMD-width traversal for decode and encode, with scalar execution limited to the remaining lanes or hardware without vector support.
- - [ ] Complete the same default SIMD pipeline for HEVC and record end-to-end measurements for both codecs.
+ - [x] HEVC plane conversion uses the same shared static operators, descending SIMD-width traversal, JPEG byte-plane pack/unpack, pooled high-depth staging, and scalar fallback; full-HD SIMD/scalar timings are recorded above.
+ - [ ] Connect the HEVC reconstruction path and record complete HEIC item and sequence measurements before closing this item.
- [ ] Apply embedded ICC profiles after codec YUV/CICP conversion and image composition through ImageSharp's existing color-profile converter. Cover preserve, convert, compact, and ignore behavior for primary items, grids, alpha-composited images, and every presented sequence frame; never substitute CICP or an assumed sRGB profile for an embedded ICC profile.
- Primary AVIF verification passes four independently scoped cases: exact profile preservation, non-sRGB conversion with changed pixels and paired-source comparison, canonical-sRGB compaction without pixel changes, and metadata skipping. Grid, auxiliary-alpha, sequence, and HEVC cases remain open.
- [ ] Decode alpha auxiliary items as monochrome planes, validate dimensions and bit depth, and compose them without losing precision. Define premultiplication behavior from AVIF signaling and ImageSharp's pixel contract.
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConversionParameters.cs b/src/ImageSharp/Formats/Heif/Av1/Av1ColorConversionParameters.cs
deleted file mode 100644
index 79474a252c..0000000000
--- a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConversionParameters.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
-
-namespace SixLabors.ImageSharp.Formats.Heif.Av1;
-
-///
-/// Stores the resolved H.273 values shared by every scalar and SIMD lane.
-///
-internal readonly struct Av1ColorConversionParameters
-{
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The red luma coefficient.
- /// The green luma coefficient.
- /// The blue luma coefficient.
- /// The signaled transfer characteristics.
- /// The constant-luminance chroma scales.
- /// The encoded luma bias.
- /// The encoded luma range.
- /// The encoded chroma midpoint.
- /// The encoded chroma range.
- /// The largest encoded sample value.
- /// The reversible transform's RGB code-value bias.
- /// The reversible transform's RGB code-value range.
- /// The largest reversible transform RGB code value.
- public Av1ColorConversionParameters(
- float kr,
- float kg,
- float kb,
- ObuTransferCharacteristics transferCharacteristics,
- in Av1ConstantLuminanceScales constantLuminanceScales,
- float lumaBias,
- float lumaScale,
- float chromaBias,
- float chromaScale,
- float encodedSampleMaximum,
- float rgbBias,
- float rgbScale,
- float rgbSampleMaximum)
- {
- this.Kr = kr;
- this.Kg = kg;
- this.Kb = kb;
- this.RedChromaScale = 2F * (1F - kr);
- this.BlueChromaScale = 2F * (1F - kb);
- this.GreenRedChromaScale = 2F * kr * (1F - kr) / kg;
- this.GreenBlueChromaScale = 2F * kb * (1F - kb) / kg;
- this.TransferCharacteristics = transferCharacteristics;
- this.ConstantLuminanceScales = constantLuminanceScales;
- this.LumaBias = lumaBias;
- this.LumaScale = lumaScale;
- this.ChromaBias = chromaBias;
- this.ChromaScale = chromaScale;
- this.EncodedSampleMaximum = encodedSampleMaximum;
- this.RgbBias = rgbBias;
- this.RgbScale = rgbScale;
- this.RgbSampleMaximum = rgbSampleMaximum;
- }
-
- ///
- /// Gets the red luma coefficient.
- ///
- public float Kr { get; }
-
- ///
- /// Gets the green luma coefficient.
- ///
- public float Kg { get; }
-
- ///
- /// Gets the blue luma coefficient.
- ///
- public float Kb { get; }
-
- ///
- /// Gets the red contribution from the red-difference component.
- ///
- public float RedChromaScale { get; }
-
- ///
- /// Gets the blue contribution from the blue-difference component.
- ///
- public float BlueChromaScale { get; }
-
- ///
- /// Gets the red-difference subtraction from green.
- ///
- public float GreenRedChromaScale { get; }
-
- ///
- /// Gets the blue-difference subtraction from green.
- ///
- public float GreenBlueChromaScale { get; }
-
- ///
- /// Gets the signaled transfer characteristics.
- ///
- public ObuTransferCharacteristics TransferCharacteristics { get; }
-
- ///
- /// Gets the constant-luminance chroma scales.
- ///
- public Av1ConstantLuminanceScales ConstantLuminanceScales { get; }
-
- ///
- /// Gets the encoded luma bias.
- ///
- public float LumaBias { get; }
-
- ///
- /// Gets the encoded luma range.
- ///
- public float LumaScale { get; }
-
- ///
- /// Gets the encoded chroma midpoint.
- ///
- public float ChromaBias { get; }
-
- ///
- /// Gets the encoded chroma range.
- ///
- public float ChromaScale { get; }
-
- ///
- /// Gets the largest encoded sample value.
- ///
- public float EncodedSampleMaximum { get; }
-
- ///
- /// Gets the reversible transform's RGB code-value bias.
- ///
- public float RgbBias { get; }
-
- ///
- /// Gets the reversible transform's RGB code-value range.
- ///
- public float RgbScale { get; }
-
- ///
- /// Gets the largest reversible transform RGB code value.
- ///
- public float RgbSampleMaximum { get; }
-}
-
-///
-/// Stores the H.273 chroma normalization constants for constant-luminance conversion.
-///
-internal readonly struct Av1ConstantLuminanceScales
-{
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The signaled transfer characteristics.
- /// The red luma coefficient.
- /// The blue luma coefficient.
- public Av1ConstantLuminanceScales(ObuTransferCharacteristics transferCharacteristics, float kr, float kb)
- {
- this.NegativeBlue = Av1TransferFunctions.ToGamma(transferCharacteristics, 1F - kb);
- this.PositiveBlue = 1F - Av1TransferFunctions.ToGamma(transferCharacteristics, kb);
- this.NegativeRed = Av1TransferFunctions.ToGamma(transferCharacteristics, 1F - kr);
- this.PositiveRed = 1F - Av1TransferFunctions.ToGamma(transferCharacteristics, kr);
- }
-
- ///
- /// Gets the negative blue-difference scale.
- ///
- public float NegativeBlue { get; }
-
- ///
- /// Gets the positive blue-difference scale.
- ///
- public float PositiveBlue { get; }
-
- ///
- /// Gets the negative red-difference scale.
- ///
- public float NegativeRed { get; }
-
- ///
- /// Gets the positive red-difference scale.
- ///
- public float PositiveRed { get; }
-}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.RowConverters.cs b/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.RowConverters.cs
index 515f05d088..c2e38c499a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.RowConverters.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.RowConverters.cs
@@ -5,8 +5,10 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+using SixLabors.ImageSharp.Formats.Heif.Color;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
+using static SixLabors.ImageSharp.Formats.Heif.Color.HeifColorConverterBase;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@@ -24,7 +26,7 @@ internal static partial class Av1YuvConverter
private readonly struct YuvToRgbRowConverter
where TPixel : unmanaged, IPixel
where TSample : unmanaged
- where TLoader : struct, ISampleLoader
+ where TLoader : struct, IHeifSampleLoader
{
///
/// The configuration used by bulk pixel conversion.
@@ -59,7 +61,7 @@ internal static partial class Av1YuvConverter
///
/// The frame-scoped color-model converter.
///
- private readonly Av1ColorConverterBase colorConverter;
+ private readonly HeifColorConverterBase colorConverter;
///
/// The signaled chroma sample position used for reconstruction.
@@ -92,7 +94,7 @@ internal static partial class Av1YuvConverter
Configuration configuration,
Av1FrameBuffer frameBuffer,
ImageFrame image,
- Av1ColorConverterBase colorConverter)
+ HeifColorConverterBase colorConverter)
{
this.configuration = configuration;
this.frameBuffer = frameBuffer;
@@ -229,7 +231,7 @@ internal static partial class Av1YuvConverter
private readonly struct RgbToYuvRowConverter
where TPixel : unmanaged, IPixel
where TSample : unmanaged
- where TStorer : struct, ISampleStorer
+ where TStorer : struct, IHeifSampleStorer
{
///
/// The configuration used by bulk pixel conversion.
@@ -264,7 +266,7 @@ internal static partial class Av1YuvConverter
///
/// The frame-scoped color-model converter.
///
- private readonly Av1ColorConverterBase colorConverter;
+ private readonly HeifColorConverterBase colorConverter;
///
/// The largest value represented by the encoded AV1 bit depth.
@@ -298,7 +300,7 @@ internal static partial class Av1YuvConverter
Configuration configuration,
Av1FrameBuffer frameBuffer,
ImageFrame image,
- Av1ColorConverterBase colorConverter,
+ HeifColorConverterBase colorConverter,
float sampleMaximum)
{
this.configuration = configuration;
@@ -383,8 +385,26 @@ internal static partial class Av1YuvConverter
}
else
{
- WriteSubsampledSamples(cbRow0, cbRow1, uDestination, chromaScale, chromaBias, this.sampleMaximum);
- WriteSubsampledSamples(crRow0, crRow1, vDestination, chromaScale, chromaBias, this.sampleMaximum);
+ float secondRowWeight = hasSecondSourceRow ? 0.5F : 0F;
+ WriteSubsampledSamples(
+ cbRow0,
+ cbRow1,
+ uDestination,
+ true,
+ secondRowWeight,
+ chromaScale,
+ chromaBias,
+ this.sampleMaximum);
+
+ WriteSubsampledSamples(
+ crRow0,
+ crRow1,
+ vDestination,
+ true,
+ secondRowWeight,
+ chromaScale,
+ chromaBias,
+ this.sampleMaximum);
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.cs b/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.cs
index 7ef0638a60..616d25d2a6 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1YuvConverter.cs
@@ -2,12 +2,13 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+using SixLabors.ImageSharp.Formats.Heif.Color;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.PixelFormats;
+using static SixLabors.ImageSharp.Formats.Heif.Color.HeifColorConverterBase;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@@ -36,12 +37,12 @@ internal static partial class Av1YuvConverter
public static void ConvertToRgb(Configuration configuration, Av1FrameBuffer frameBuffer, ImageFrame image)
where TPixel : unmanaged, IPixel
{
- Av1ColorConversionParameters parameters = GetConversionParameters(frameBuffer, out Av1ColorConversionMode mode);
+ HeifColorConversionParameters parameters = GetConversionParameters(frameBuffer, out HeifColorConversionMode mode);
- Av1ColorConverterBase colorConverter = Av1ColorConverterBase.Create(mode, in parameters, frameBuffer.ColorFormat == Av1ColorFormat.Yuv400);
+ HeifColorConverterBase colorConverter = HeifColorConverterBase.Create(mode, in parameters, frameBuffer.ColorFormat == Av1ColorFormat.Yuv400);
if (frameBuffer.BitDepth == Av1BitDepth.EightBit)
{
- YuvToRgbRowConverter converter = new(configuration, frameBuffer, image, colorConverter);
+ YuvToRgbRowConverter converter = new(configuration, frameBuffer, image, colorConverter);
using IMemoryOwner scratchOwner = configuration.MemoryAllocator.Allocate(converter.BufferLength);
using IMemoryOwner proxyOwner = configuration.MemoryAllocator.Allocate(image.Width + 3);
Span scratch = scratchOwner.GetSpan();
@@ -53,7 +54,7 @@ internal static partial class Av1YuvConverter
}
else
{
- YuvToRgbRowConverter converter = new(configuration, frameBuffer, image, colorConverter);
+ YuvToRgbRowConverter converter = new(configuration, frameBuffer, image, colorConverter);
using IMemoryOwner owner = configuration.MemoryAllocator.Allocate(converter.BufferLength);
Span scratch = owner.GetSpan();
for (int y = 0; y < image.Height; y++)
@@ -73,16 +74,16 @@ internal static partial class Av1YuvConverter
public static void ConvertFromRgb(Configuration configuration, ImageFrame image, Av1FrameBuffer frameBuffer)
where TPixel : unmanaged, IPixel
{
- Av1ColorConversionParameters parameters = GetConversionParameters(frameBuffer, out Av1ColorConversionMode mode);
- float sampleMaximum = parameters.EncodedSampleMaximum;
+ HeifColorConversionParameters parameters = GetConversionParameters(frameBuffer, out HeifColorConversionMode mode);
+ float sampleMaximum = parameters.LumaSampleMaximum;
bool isMonochrome = frameBuffer.ColorFormat == Av1ColorFormat.Yuv400;
- Av1ColorConverterBase colorConverter = Av1ColorConverterBase.Create(mode, in parameters, isMonochrome);
+ HeifColorConverterBase colorConverter = HeifColorConverterBase.Create(mode, in parameters, isMonochrome);
int rowShift = !isMonochrome && frameBuffer.ColorConfig.SubSamplingY ? 1 : 0;
int iterationCount = (image.Height + rowShift) >> rowShift;
if (frameBuffer.BitDepth == Av1BitDepth.EightBit)
{
- RgbToYuvRowConverter converter = new(configuration, frameBuffer, image, colorConverter, sampleMaximum);
+ RgbToYuvRowConverter converter = new(configuration, frameBuffer, image, colorConverter, sampleMaximum);
using IMemoryOwner componentOwner = configuration.MemoryAllocator.Allocate(converter.ComponentBufferLength);
Span components = componentOwner.GetSpan();
for (int y = 0; y < iterationCount; y++)
@@ -92,7 +93,7 @@ internal static partial class Av1YuvConverter
}
else
{
- RgbToYuvRowConverter converter = new(configuration, frameBuffer, image, colorConverter, sampleMaximum);
+ RgbToYuvRowConverter converter = new(configuration, frameBuffer, image, colorConverter, sampleMaximum);
using IMemoryOwner packedOwner = configuration.MemoryAllocator.Allocate(image.Width);
using IMemoryOwner componentOwner = configuration.MemoryAllocator.Allocate(converter.ComponentBufferLength);
Span packed = packedOwner.GetSpan()[..image.Width];
@@ -110,288 +111,25 @@ internal static partial class Av1YuvConverter
/// The AV1 frame containing the signaled color configuration.
/// The resolved conversion mode.
/// The resolved conversion parameters.
- private static Av1ColorConversionParameters GetConversionParameters(Av1FrameBuffer frameBuffer, out Av1ColorConversionMode mode)
+ private static HeifColorConversionParameters GetConversionParameters(Av1FrameBuffer frameBuffer, out HeifColorConversionMode mode)
{
- mode = Av1ColorConversionMode.Coefficients;
- float kr = 0F;
- float kb = 0F;
-
- // These code points and fixed coefficient matrices are defined by H.273.
- switch (frameBuffer.ColorConfig.MatrixCoefficients)
- {
- case ObuMatrixCoefficients.Identity:
- mode = Av1ColorConversionMode.Identity;
- break;
- case ObuMatrixCoefficients.Bt709:
- kr = 0.2126F;
- kb = 0.0722F;
- break;
- case ObuMatrixCoefficients.Fcc:
- kr = 0.30F;
- kb = 0.11F;
- break;
- case ObuMatrixCoefficients.Bt470BG:
- case ObuMatrixCoefficients.Bt601:
- case ObuMatrixCoefficients.Unspecified:
- // libavif falls back to BT.601 coefficients when the matrix is unavailable.
- kr = 0.299F;
- kb = 0.114F;
- break;
- case ObuMatrixCoefficients.Smpte240:
- kr = 0.212F;
- kb = 0.087F;
- break;
- case ObuMatrixCoefficients.SmpteYCgCo:
- mode = Av1ColorConversionMode.YCgCo;
- break;
- case ObuMatrixCoefficients.Bt2020NonConstantLuminance:
- kr = 0.2627F;
- kb = 0.0593F;
- break;
- case ObuMatrixCoefficients.Bt2020ConstantLuminance:
- mode = Av1ColorConversionMode.ConstantLuminance;
- kr = 0.2627F;
- kb = 0.0593F;
- break;
- case ObuMatrixCoefficients.Smpte2085:
- mode = Av1ColorConversionMode.Smpte2085;
- break;
- case ObuMatrixCoefficients.ChromaticityDerivedNonConstantLuminance:
- GetChromaticityDerivedCoefficients(frameBuffer.ColorConfig.ColorPrimaries, out kr, out kb);
- break;
- case ObuMatrixCoefficients.ChromaticityDerivedConstantLuminance:
- mode = Av1ColorConversionMode.ConstantLuminance;
- GetChromaticityDerivedCoefficients(frameBuffer.ColorConfig.ColorPrimaries, out kr, out kb);
- break;
- case ObuMatrixCoefficients.Bt2100ICtCp:
- mode = Av1ColorConversionMode.ICtCp;
- break;
- case ObuMatrixCoefficients.IptC2:
- mode = Av1ColorConversionMode.IptC2;
- break;
- case ObuMatrixCoefficients.YCgCoRe:
- case ObuMatrixCoefficients.YCgCoRo:
- mode = Av1ColorConversionMode.YCgCoReversible;
- break;
- default:
- throw new NotSupportedException($"AV1 matrix coefficients '{frameBuffer.ColorConfig.MatrixCoefficients}' are not currently supported.");
- }
-
- float kg = 1F - kr - kb;
- bool isMonochrome = frameBuffer.ColorFormat == Av1ColorFormat.Yuv400;
- bool isFullRange = frameBuffer.ColorConfig.ColorRange;
- bool requiresFullChroma = mode is Av1ColorConversionMode.Identity or Av1ColorConversionMode.YCgCoReversible;
- if (requiresFullChroma && !isMonochrome && frameBuffer.ColorFormat != Av1ColorFormat.Yuv444)
- {
- throw new InvalidImageContentException($"AV1 {frameBuffer.ColorConfig.MatrixCoefficients} matrix coefficients require YUV 4:4:4 sampling.");
- }
-
if (frameBuffer.ColorConfig.ChromaSamplePosition == ObuChromoSamplePosition.Reserved)
{
throw new InvalidImageContentException("The reserved AV1 chroma sample position is invalid.");
}
- int bitCount = frameBuffer.BitDepth.GetBitCount();
- int depthScale = 1 << (bitCount - 8);
- float sampleMaximum = (1 << bitCount) - 1;
- float chromaBias = 128F * depthScale;
- float lumaBias = isFullRange ? 0F : 16F * depthScale;
- float lumaScale = isFullRange ? sampleMaximum : 219F * depthScale;
-
- // H.273 limited-range YCgCo first maps R, G, and B through the 219-code luma range, so its
- // difference components inherit that scale instead of the 224-code scale used by YCbCr.
- float chromaScale = isFullRange || mode == Av1ColorConversionMode.YCgCo
- ? lumaScale
- : 224F * depthScale;
-
- float rgbBias = 0F;
- float rgbScale = 1F;
- float rgbSampleMaximum = 1F;
- if (mode == Av1ColorConversionMode.YCgCoReversible)
- {
- int bitOffset = frameBuffer.ColorConfig.MatrixCoefficients == ObuMatrixCoefficients.YCgCoRe ? 2 : 1;
- int rgbBitCount = bitCount - bitOffset;
- float rgbDepthScale = MathF.ScaleB(1F, rgbBitCount - 8);
- rgbSampleMaximum = (1 << rgbBitCount) - 1;
- rgbBias = isFullRange ? 0F : 16F * rgbDepthScale;
- rgbScale = isFullRange ? rgbSampleMaximum : 219F * rgbDepthScale;
-
- // The reversible lifting transform operates on raw integer code values. Range adjustment therefore
- // belongs inside the operator, while the row converter only normalizes the encoded Y, Cg, and Co planes.
- lumaBias = 0F;
- lumaScale = sampleMaximum;
- chromaScale = sampleMaximum;
- }
-
- ObuTransferCharacteristics transferCharacteristics = frameBuffer.ColorConfig.TransferCharacteristics;
- Av1ConstantLuminanceScales constantLuminanceScales = mode == Av1ColorConversionMode.ConstantLuminance
- ? new Av1ConstantLuminanceScales(transferCharacteristics, kr, kb)
- : default;
-
- return new Av1ColorConversionParameters(
- kr,
- kg,
- kb,
- transferCharacteristics,
- in constantLuminanceScales,
- lumaBias,
- lumaScale,
- chromaBias,
- chromaScale,
- sampleMaximum,
- rgbBias,
- rgbScale,
- rgbSampleMaximum);
- }
-
- ///
- /// Computes the luma coefficients defined by the signaled H.273 primary chromaticities.
- ///
- /// The signaled color-primary code point.
- /// The resulting red luma coefficient.
- /// The resulting blue luma coefficient.
- private static void GetChromaticityDerivedCoefficients(
- ObuColorPrimaries colorPrimaries,
- out float kr,
- out float kb)
- {
- float redX;
- float redY;
- float greenX;
- float greenY;
- float blueX;
- float blueY;
- float whiteX;
- float whiteY;
-
- switch (colorPrimaries)
- {
- case ObuColorPrimaries.Bt470M:
- redX = 0.67F;
- redY = 0.33F;
- greenX = 0.21F;
- greenY = 0.71F;
- blueX = 0.14F;
- blueY = 0.08F;
- whiteX = 0.310F;
- whiteY = 0.316F;
- break;
- case ObuColorPrimaries.Bt470BG:
- redX = 0.64F;
- redY = 0.33F;
- greenX = 0.29F;
- greenY = 0.60F;
- blueX = 0.15F;
- blueY = 0.06F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- case ObuColorPrimaries.Bt601:
- case ObuColorPrimaries.Smpte240:
- redX = 0.630F;
- redY = 0.340F;
- greenX = 0.310F;
- greenY = 0.595F;
- blueX = 0.155F;
- blueY = 0.070F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- case ObuColorPrimaries.GenericFilm:
- redX = 0.681F;
- redY = 0.319F;
- greenX = 0.243F;
- greenY = 0.692F;
- blueX = 0.145F;
- blueY = 0.049F;
- whiteX = 0.310F;
- whiteY = 0.316F;
- break;
- case ObuColorPrimaries.Bt2020:
- redX = 0.708F;
- redY = 0.292F;
- greenX = 0.170F;
- greenY = 0.797F;
- blueX = 0.131F;
- blueY = 0.046F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- case ObuColorPrimaries.Xyz:
- redX = 1F;
- redY = 0F;
- greenX = 0F;
- greenY = 1F;
- blueX = 0F;
- blueY = 0F;
- whiteX = 1F / 3F;
- whiteY = 1F / 3F;
- break;
- case ObuColorPrimaries.Smpte431:
- redX = 0.680F;
- redY = 0.320F;
- greenX = 0.265F;
- greenY = 0.690F;
- blueX = 0.150F;
- blueY = 0.060F;
- whiteX = 0.314F;
- whiteY = 0.351F;
- break;
- case ObuColorPrimaries.Smpte432:
- redX = 0.680F;
- redY = 0.320F;
- greenX = 0.265F;
- greenY = 0.690F;
- blueX = 0.150F;
- blueY = 0.060F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- case ObuColorPrimaries.Ebu3213:
- redX = 0.630F;
- redY = 0.340F;
- greenX = 0.295F;
- greenY = 0.605F;
- blueX = 0.155F;
- blueY = 0.077F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- default:
- // Unspecified and reserved code points have no chromaticities to derive. Match libavif's established
- // still-image fallback so such files retain the same deterministic BT.709 interpretation.
- redX = 0.64F;
- redY = 0.33F;
- greenX = 0.30F;
- greenY = 0.60F;
- blueX = 0.15F;
- blueY = 0.06F;
- whiteX = 0.3127F;
- whiteY = 0.3290F;
- break;
- }
-
- float redZ = 1F - (redX + redY);
- float greenZ = 1F - (greenX + greenY);
- float blueZ = 1F - (blueX + blueY);
- float whiteZ = 1F - (whiteX + whiteY);
-
- // H.273 equations 39 and 40 solve the RGB-to-XYZ primary matrix at the signaled white point. Keeping
- // the expanded determinant matches libavif and avoids introducing a general matrix inversion dependency.
- float denominator = whiteY *
- ((redX * ((greenY * blueZ) - (blueY * greenZ))) +
- (greenX * ((blueY * redZ) - (redY * blueZ))) +
- (blueX * ((redY * greenZ) - (greenY * redZ))));
-
- kr = (redY *
- ((whiteX * ((greenY * blueZ) - (blueY * greenZ))) +
- (whiteY * ((blueX * greenZ) - (greenX * blueZ))) +
- (whiteZ * ((greenX * blueY) - (blueX * greenY))))) / denominator;
+ bool isMonochrome = frameBuffer.ColorFormat == Av1ColorFormat.Yuv400;
- kb = (blueY *
- ((whiteX * ((redY * greenZ) - (greenY * redZ))) +
- (whiteY * ((greenX * redZ) - (redX * greenZ))) +
- (whiteZ * ((redX * greenY) - (greenX * redY))))) / denominator;
+ return HeifColorConversionParameters.Create(
+ (CicpColorPrimaries)(byte)frameBuffer.ColorConfig.ColorPrimaries,
+ (CicpTransferCharacteristics)(byte)frameBuffer.ColorConfig.TransferCharacteristics,
+ (CicpMatrixCoefficients)(byte)frameBuffer.ColorConfig.MatrixCoefficients,
+ frameBuffer.ColorConfig.ColorRange,
+ frameBuffer.BitDepth.GetBitCount(),
+ frameBuffer.BitDepth.GetBitCount(),
+ isMonochrome,
+ frameBuffer.ColorFormat == Av1ColorFormat.Yuv444,
+ out mode);
}
///
@@ -435,43 +173,4 @@ internal static partial class Av1YuvConverter
upper = isOdd ? Math.Min(sample + 1, maximum) : sample;
upperWeight = isOdd ? 2 : 0;
}
-
- ///
- /// Reads an 8-bit or 16-bit unsigned sample without introducing a separate conversion buffer.
- ///
- /// The encoded sample type.
- /// The source samples.
- /// The sample index.
- /// The sample value.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static float GetSample(ReadOnlySpan source, int index)
- where TSample : unmanaged
- {
- ref TSample sample = ref Unsafe.Add(ref MemoryMarshal.GetReference(source), index);
- return typeof(TSample) == typeof(byte)
- ? Unsafe.As(ref sample)
- : Unsafe.As(ref sample);
- }
-
- ///
- /// Rounds and clamps a conversion result to the encoded sample range.
- ///
- /// The encoded sample type.
- /// The conversion result.
- /// The largest encoded sample value.
- /// The bounded sample.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static TSample ToSample(float value, float maximum)
- where TSample : unmanaged
- {
- int sample = Numerics.Clamp((int)MathF.Round(value, MidpointRounding.AwayFromZero), 0, (int)maximum);
- if (typeof(TSample) == typeof(byte))
- {
- byte result = (byte)sample;
- return Unsafe.As(ref result);
- }
-
- ushort highBitDepthResult = (ushort)sample;
- return Unsafe.As(ref highBitDepthResult);
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Color/HeifColorConversionParameters.cs b/src/ImageSharp/Formats/Heif/Color/HeifColorConversionParameters.cs
new file mode 100644
index 0000000000..800ea90e9d
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Color/HeifColorConversionParameters.cs
@@ -0,0 +1,498 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Color;
+
+///
+/// Stores the resolved H.273 values shared by every scalar and SIMD lane.
+///
+internal readonly struct HeifColorConversionParameters
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red luma coefficient.
+ /// The green luma coefficient.
+ /// The blue luma coefficient.
+ /// The signaled transfer characteristics.
+ /// The constant-luminance chroma scales.
+ /// The encoded luma bias.
+ /// The encoded luma range.
+ /// The encoded chroma midpoint.
+ /// The encoded chroma range.
+ /// The largest encoded luma sample value.
+ /// The largest encoded chroma sample value.
+ /// The reversible transform's RGB code-value bias.
+ /// The reversible transform's RGB code-value range.
+ /// The largest reversible transform RGB code value.
+ public HeifColorConversionParameters(
+ float kr,
+ float kg,
+ float kb,
+ CicpTransferCharacteristics transferCharacteristics,
+ in HeifConstantLuminanceScales constantLuminanceScales,
+ float lumaBias,
+ float lumaScale,
+ float chromaBias,
+ float chromaScale,
+ float lumaSampleMaximum,
+ float chromaSampleMaximum,
+ float rgbBias,
+ float rgbScale,
+ float rgbSampleMaximum)
+ {
+ this.Kr = kr;
+ this.Kg = kg;
+ this.Kb = kb;
+ this.RedChromaScale = 2F * (1F - kr);
+ this.BlueChromaScale = 2F * (1F - kb);
+ this.GreenRedChromaScale = 2F * kr * (1F - kr) / kg;
+ this.GreenBlueChromaScale = 2F * kb * (1F - kb) / kg;
+ this.TransferCharacteristics = transferCharacteristics;
+ this.ConstantLuminanceScales = constantLuminanceScales;
+ this.LumaBias = lumaBias;
+ this.LumaScale = lumaScale;
+ this.ChromaBias = chromaBias;
+ this.ChromaScale = chromaScale;
+ this.LumaSampleMaximum = lumaSampleMaximum;
+ this.ChromaSampleMaximum = chromaSampleMaximum;
+ this.RgbBias = rgbBias;
+ this.RgbScale = rgbScale;
+ this.RgbSampleMaximum = rgbSampleMaximum;
+ }
+
+ ///
+ /// Gets the red luma coefficient.
+ ///
+ public float Kr { get; }
+
+ ///
+ /// Gets the green luma coefficient.
+ ///
+ public float Kg { get; }
+
+ ///
+ /// Gets the blue luma coefficient.
+ ///
+ public float Kb { get; }
+
+ ///
+ /// Gets the red contribution from the red-difference component.
+ ///
+ public float RedChromaScale { get; }
+
+ ///
+ /// Gets the blue contribution from the blue-difference component.
+ ///
+ public float BlueChromaScale { get; }
+
+ ///
+ /// Gets the red-difference subtraction from green.
+ ///
+ public float GreenRedChromaScale { get; }
+
+ ///
+ /// Gets the blue-difference subtraction from green.
+ ///
+ public float GreenBlueChromaScale { get; }
+
+ ///
+ /// Gets the signaled transfer characteristics.
+ ///
+ public CicpTransferCharacteristics TransferCharacteristics { get; }
+
+ ///
+ /// Gets the constant-luminance chroma scales.
+ ///
+ public HeifConstantLuminanceScales ConstantLuminanceScales { get; }
+
+ ///
+ /// Gets the encoded luma bias.
+ ///
+ public float LumaBias { get; }
+
+ ///
+ /// Gets the encoded luma range.
+ ///
+ public float LumaScale { get; }
+
+ ///
+ /// Gets the encoded chroma midpoint.
+ ///
+ public float ChromaBias { get; }
+
+ ///
+ /// Gets the encoded chroma range.
+ ///
+ public float ChromaScale { get; }
+
+ ///
+ /// Gets the largest encoded luma sample value.
+ ///
+ public float LumaSampleMaximum { get; }
+
+ ///
+ /// Gets the largest encoded chroma sample value.
+ ///
+ public float ChromaSampleMaximum { get; }
+
+ ///
+ /// Gets the reversible transform's RGB code-value bias.
+ ///
+ public float RgbBias { get; }
+
+ ///
+ /// Gets the reversible transform's RGB code-value range.
+ ///
+ public float RgbScale { get; }
+
+ ///
+ /// Gets the largest reversible transform RGB code value.
+ ///
+ public float RgbSampleMaximum { get; }
+
+ ///
+ /// Resolves the H.273 matrix coefficients and sample ranges shared by HEVC and AV1 image items.
+ ///
+ /// The H.273 color-primary code point.
+ /// The H.273 transfer-characteristic code point.
+ /// The H.273 matrix-coefficient code point.
+ /// Whether encoded components use their complete numeric range.
+ /// The encoded luma precision in bits.
+ /// The encoded chroma precision in bits.
+ /// Whether the image contains only luma samples.
+ /// Whether both chroma planes have luma resolution.
+ /// The resolved conversion operation.
+ /// The immutable values used by each scalar and SIMD conversion lane.
+ public static HeifColorConversionParameters Create(
+ CicpColorPrimaries colorPrimaries,
+ CicpTransferCharacteristics transferCharacteristics,
+ CicpMatrixCoefficients matrixCoefficients,
+ bool isFullRange,
+ int lumaBitDepth,
+ int chromaBitDepth,
+ bool isMonochrome,
+ bool hasFullResolutionChroma,
+ out HeifColorConversionMode mode)
+ {
+ mode = HeifColorConversionMode.Coefficients;
+ float kr = 0F;
+ float kb = 0F;
+
+ // H.273 assigns one closed arithmetic operation to each non-reserved matrix code point. Resolving the
+ // operation once keeps both codec wrappers and every vector lane free from per-sample format dispatch.
+ switch (matrixCoefficients)
+ {
+ case CicpMatrixCoefficients.Identity:
+ mode = HeifColorConversionMode.Identity;
+ break;
+ case CicpMatrixCoefficients.ItuRBt709_6:
+ kr = 0.2126F;
+ kb = 0.0722F;
+ break;
+ case CicpMatrixCoefficients.Fcc47:
+ kr = 0.30F;
+ kb = 0.11F;
+ break;
+ case CicpMatrixCoefficients.ItuRBt601_7_625:
+ case CicpMatrixCoefficients.ItuRBt601_7_525:
+ case CicpMatrixCoefficients.Unspecified:
+ kr = 0.299F;
+ kb = 0.114F;
+ break;
+ case CicpMatrixCoefficients.SmpteSt240:
+ kr = 0.212F;
+ kb = 0.087F;
+ break;
+ case CicpMatrixCoefficients.YCgCo:
+ mode = HeifColorConversionMode.YCgCo;
+ break;
+ case CicpMatrixCoefficients.ItuRBt2020_2_Ncl:
+ kr = 0.2627F;
+ kb = 0.0593F;
+ break;
+ case CicpMatrixCoefficients.ItuRBt2020_2_Cl:
+ mode = HeifColorConversionMode.ConstantLuminance;
+ kr = 0.2627F;
+ kb = 0.0593F;
+ break;
+ case CicpMatrixCoefficients.SmpteSt2085:
+ mode = HeifColorConversionMode.Smpte2085;
+ break;
+ case CicpMatrixCoefficients.ChromaDerivedNcl:
+ GetChromaticityDerivedCoefficients(colorPrimaries, out kr, out kb);
+ break;
+ case CicpMatrixCoefficients.ChromaDerivedCl:
+ mode = HeifColorConversionMode.ConstantLuminance;
+ GetChromaticityDerivedCoefficients(colorPrimaries, out kr, out kb);
+ break;
+ case CicpMatrixCoefficients.ICtCp:
+ mode = HeifColorConversionMode.ICtCp;
+ break;
+ case CicpMatrixCoefficients.IptC2:
+ mode = HeifColorConversionMode.IptC2;
+ break;
+ case CicpMatrixCoefficients.YCgCoRe:
+ case CicpMatrixCoefficients.YCgCoRo:
+ mode = HeifColorConversionMode.YCgCoReversible;
+ break;
+ default:
+ throw new InvalidImageContentException($"The image declares reserved H.273 matrix coefficients '{(byte)matrixCoefficients}'.");
+ }
+
+ bool requiresFullResolutionChroma = mode is HeifColorConversionMode.Identity or HeifColorConversionMode.YCgCoReversible;
+ if (requiresFullResolutionChroma && !isMonochrome && !hasFullResolutionChroma)
+ {
+ throw new InvalidImageContentException($"H.273 matrix coefficients '{matrixCoefficients}' require 4:4:4 sampling.");
+ }
+
+ bool requiresEqualBitDepth = mode is HeifColorConversionMode.Identity or HeifColorConversionMode.YCgCoReversible;
+ if (requiresEqualBitDepth && !isMonochrome && lumaBitDepth != chromaBitDepth)
+ {
+ throw new InvalidImageContentException($"H.273 matrix coefficients '{matrixCoefficients}' require equal component bit depths.");
+ }
+
+ float kg = 1F - kr - kb;
+ int lumaDepthScale = 1 << (lumaBitDepth - 8);
+ int chromaDepthScale = 1 << (chromaBitDepth - 8);
+ float lumaSampleMaximum = (1 << lumaBitDepth) - 1;
+ float chromaSampleMaximum = (1 << chromaBitDepth) - 1;
+ float chromaBias = 128F * chromaDepthScale;
+ float lumaBias = isFullRange ? 0F : 16F * lumaDepthScale;
+ float lumaScale = isFullRange ? lumaSampleMaximum : 219F * lumaDepthScale;
+
+ // Limited-range YCgCo first range-adjusts RGB through the luma range, so its difference components use
+ // 219 codes. Conventional YCbCr uses the independently specified 224-code chroma excursion.
+ float chromaScale = isFullRange
+ ? chromaSampleMaximum
+ : (mode == HeifColorConversionMode.YCgCo ? 219F : 224F) * chromaDepthScale;
+
+ float rgbBias = 0F;
+ float rgbScale = 1F;
+ float rgbSampleMaximum = 1F;
+ if (mode == HeifColorConversionMode.YCgCoReversible)
+ {
+ int bitOffset = matrixCoefficients == CicpMatrixCoefficients.YCgCoRe ? 2 : 1;
+ int rgbBitDepth = lumaBitDepth - bitOffset;
+ float rgbDepthScale = MathF.ScaleB(1F, rgbBitDepth - 8);
+ rgbSampleMaximum = (1 << rgbBitDepth) - 1;
+ rgbBias = isFullRange ? 0F : 16F * rgbDepthScale;
+ rgbScale = isFullRange ? rgbSampleMaximum : 219F * rgbDepthScale;
+
+ // The reversible lifting transform works on integer code values. It performs RGB range adjustment
+ // internally, while the row traversal normalizes all three encoded components to their full code range.
+ lumaBias = 0F;
+ lumaScale = lumaSampleMaximum;
+ chromaScale = chromaSampleMaximum;
+ }
+
+ HeifConstantLuminanceScales constantLuminanceScales = mode == HeifColorConversionMode.ConstantLuminance
+ ? new HeifConstantLuminanceScales(transferCharacteristics, kr, kb)
+ : default;
+
+ return new HeifColorConversionParameters(
+ kr,
+ kg,
+ kb,
+ transferCharacteristics,
+ in constantLuminanceScales,
+ lumaBias,
+ lumaScale,
+ chromaBias,
+ chromaScale,
+ lumaSampleMaximum,
+ chromaSampleMaximum,
+ rgbBias,
+ rgbScale,
+ rgbSampleMaximum);
+ }
+
+ ///
+ /// Computes the luma coefficients defined by an H.273 primary-chromaticity code point.
+ ///
+ /// The H.273 color-primary code point.
+ /// The resulting red luma coefficient.
+ /// The resulting blue luma coefficient.
+ private static void GetChromaticityDerivedCoefficients(CicpColorPrimaries colorPrimaries, out float kr, out float kb)
+ {
+ float redX;
+ float redY;
+ float greenX;
+ float greenY;
+ float blueX;
+ float blueY;
+ float whiteX;
+ float whiteY;
+
+ switch (colorPrimaries)
+ {
+ case CicpColorPrimaries.ItuRBt470_6M:
+ redX = 0.67F;
+ redY = 0.33F;
+ greenX = 0.21F;
+ greenY = 0.71F;
+ blueX = 0.14F;
+ blueY = 0.08F;
+ whiteX = 0.310F;
+ whiteY = 0.316F;
+ break;
+ case CicpColorPrimaries.ItuRBt601_7_625:
+ redX = 0.64F;
+ redY = 0.33F;
+ greenX = 0.29F;
+ greenY = 0.60F;
+ blueX = 0.15F;
+ blueY = 0.06F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ case CicpColorPrimaries.ItuRBt601_7_525:
+ case CicpColorPrimaries.SmpteSt240:
+ redX = 0.630F;
+ redY = 0.340F;
+ greenX = 0.310F;
+ greenY = 0.595F;
+ blueX = 0.155F;
+ blueY = 0.070F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ case CicpColorPrimaries.GenericFilm:
+ redX = 0.681F;
+ redY = 0.319F;
+ greenX = 0.243F;
+ greenY = 0.692F;
+ blueX = 0.145F;
+ blueY = 0.049F;
+ whiteX = 0.310F;
+ whiteY = 0.316F;
+ break;
+ case CicpColorPrimaries.ItuRBt2020_2:
+ redX = 0.708F;
+ redY = 0.292F;
+ greenX = 0.170F;
+ greenY = 0.797F;
+ blueX = 0.131F;
+ blueY = 0.046F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ case CicpColorPrimaries.SmpteSt428_1:
+ redX = 1F;
+ redY = 0F;
+ greenX = 0F;
+ greenY = 1F;
+ blueX = 0F;
+ blueY = 0F;
+ whiteX = 1F / 3F;
+ whiteY = 1F / 3F;
+ break;
+ case CicpColorPrimaries.SmpteRp431_2:
+ redX = 0.680F;
+ redY = 0.320F;
+ greenX = 0.265F;
+ greenY = 0.690F;
+ blueX = 0.150F;
+ blueY = 0.060F;
+ whiteX = 0.314F;
+ whiteY = 0.351F;
+ break;
+ case CicpColorPrimaries.SmpteEg432_1:
+ redX = 0.680F;
+ redY = 0.320F;
+ greenX = 0.265F;
+ greenY = 0.690F;
+ blueX = 0.150F;
+ blueY = 0.060F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ case CicpColorPrimaries.EbuTech3213E:
+ redX = 0.630F;
+ redY = 0.340F;
+ greenX = 0.295F;
+ greenY = 0.605F;
+ blueX = 0.155F;
+ blueY = 0.077F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ default:
+ // Unspecified primaries cannot define a chromaticity-derived matrix. The established libavif
+ // behavior supplies BT.709/D65 so the image has one deterministic interpretation.
+ redX = 0.64F;
+ redY = 0.33F;
+ greenX = 0.30F;
+ greenY = 0.60F;
+ blueX = 0.15F;
+ blueY = 0.06F;
+ whiteX = 0.3127F;
+ whiteY = 0.3290F;
+ break;
+ }
+
+ float redZ = 1F - (redX + redY);
+ float greenZ = 1F - (greenX + greenY);
+ float blueZ = 1F - (blueX + blueY);
+ float whiteZ = 1F - (whiteX + whiteY);
+
+ // H.273 equations 39 and 40 solve the RGB-to-XYZ primary matrix at the signaled white point. Expanding the
+ // determinant avoids a general matrix inversion and gives both codec paths the same rounding sequence.
+ float denominator = whiteY *
+ ((redX * ((greenY * blueZ) - (blueY * greenZ))) +
+ (greenX * ((blueY * redZ) - (redY * blueZ))) +
+ (blueX * ((redY * greenZ) - (greenY * redZ))));
+
+ kr = (redY *
+ ((whiteX * ((greenY * blueZ) - (blueY * greenZ))) +
+ (whiteY * ((blueX * greenZ) - (greenX * blueZ))) +
+ (whiteZ * ((greenX * blueY) - (blueX * greenY))))) / denominator;
+
+ kb = (blueY *
+ ((whiteX * ((redY * greenZ) - (greenY * redZ))) +
+ (whiteY * ((greenX * redZ) - (redX * greenZ))) +
+ (whiteZ * ((redX * greenY) - (greenX * redY))))) / denominator;
+ }
+}
+
+///
+/// Stores the H.273 chroma normalization constants for constant-luminance conversion.
+///
+internal readonly struct HeifConstantLuminanceScales
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The signaled transfer characteristics.
+ /// The red luma coefficient.
+ /// The blue luma coefficient.
+ public HeifConstantLuminanceScales(CicpTransferCharacteristics transferCharacteristics, float kr, float kb)
+ {
+ this.NegativeBlue = HeifTransferFunctions.ToGamma(transferCharacteristics, 1F - kb);
+ this.PositiveBlue = 1F - HeifTransferFunctions.ToGamma(transferCharacteristics, kb);
+ this.NegativeRed = HeifTransferFunctions.ToGamma(transferCharacteristics, 1F - kr);
+ this.PositiveRed = 1F - HeifTransferFunctions.ToGamma(transferCharacteristics, kr);
+ }
+
+ ///
+ /// Gets the negative blue-difference scale.
+ ///
+ public float NegativeBlue { get; }
+
+ ///
+ /// Gets the positive blue-difference scale.
+ ///
+ public float PositiveBlue { get; }
+
+ ///
+ /// Gets the negative red-difference scale.
+ ///
+ public float NegativeRed { get; }
+
+ ///
+ /// Gets the positive red-difference scale.
+ ///
+ public float PositiveRed { get; }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.CoefficientOperator.cs b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.CoefficientOperator.cs
similarity index 88%
rename from src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.CoefficientOperator.cs
rename to src/ImageSharp/Formats/Heif/Color/HeifColorConverter.CoefficientOperator.cs
index 8d92ee7ad8..6219716a0d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.CoefficientOperator.cs
+++ b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.CoefficientOperator.cs
@@ -4,21 +4,21 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
-namespace SixLabors.ImageSharp.Formats.Heif.Av1;
+namespace SixLabors.ImageSharp.Formats.Heif.Color;
-internal abstract partial class Av1ColorConverterBase
+internal abstract partial class HeifColorConverterBase
{
///
/// Implements coefficient-based YCbCr conversion for scalar and SIMD lanes.
///
- internal readonly struct Av1CoefficientColorOperator : IAv1ColorOperator
+ internal readonly struct HeifCoefficientColorOperator : IHeifColorOperator
{
///
public static bool ChromaUsesLumaRange => false;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref float y, ref float cb, ref float cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref float y, ref float cb, ref float cr, in HeifColorConversionParameters parameters)
{
// The resolved coefficients are frame invariants. Keeping the operator in this direct matrix form
// lets every SIMD overload map the same three equations to fused multiply-add instructions.
@@ -33,7 +33,7 @@ internal abstract partial class Av1ColorConverterBase
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector128 y, ref Vector128 cb, ref Vector128 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector128 y, ref Vector128 cb, ref Vector128 cr, in HeifColorConversionParameters parameters)
{
Vector128 r = Vector128.MultiplyAddEstimate(Vector128.Create(parameters.RedChromaScale), cr, y);
Vector128 g = Vector128.MultiplyAddEstimate(
@@ -49,7 +49,7 @@ internal abstract partial class Av1ColorConverterBase
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector256 y, ref Vector256 cb, ref Vector256 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector256 y, ref Vector256 cb, ref Vector256 cr, in HeifColorConversionParameters parameters)
{
Vector256 r = Vector256.MultiplyAddEstimate(Vector256.Create(parameters.RedChromaScale), cr, y);
Vector256 g = Vector256.MultiplyAddEstimate(
@@ -65,7 +65,7 @@ internal abstract partial class Av1ColorConverterBase
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector512 y, ref Vector512 cb, ref Vector512 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector512 y, ref Vector512 cb, ref Vector512 cr, in HeifColorConversionParameters parameters)
{
Vector512 r = Vector512.MultiplyAddEstimate(Vector512.Create(parameters.RedChromaScale), cr, y);
Vector512 g = Vector512.MultiplyAddEstimate(
@@ -85,7 +85,7 @@ internal abstract partial class Av1ColorConverterBase
float r,
float g,
float b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out float y,
out float cb,
out float cr)
@@ -102,7 +102,7 @@ internal abstract partial class Av1ColorConverterBase
Vector128 r,
Vector128 g,
Vector128 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector128 y,
out Vector128 cb,
out Vector128 cr)
@@ -121,7 +121,7 @@ internal abstract partial class Av1ColorConverterBase
Vector256 r,
Vector256 g,
Vector256 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector256 y,
out Vector256 cb,
out Vector256 cr)
@@ -140,7 +140,7 @@ internal abstract partial class Av1ColorConverterBase
Vector512 r,
Vector512 g,
Vector512 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector512 y,
out Vector512 cb,
out Vector512 cr)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ConstantLuminanceOperator.cs b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ConstantLuminanceOperator.cs
similarity index 66%
rename from src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ConstantLuminanceOperator.cs
rename to src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ConstantLuminanceOperator.cs
index 990113ead3..3fe0f47715 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ConstantLuminanceOperator.cs
+++ b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ConstantLuminanceOperator.cs
@@ -4,42 +4,42 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
-namespace SixLabors.ImageSharp.Formats.Heif.Av1;
+namespace SixLabors.ImageSharp.Formats.Heif.Color;
-internal abstract partial class Av1ColorConverterBase
+internal abstract partial class HeifColorConverterBase
{
///
/// Implements H.273 constant-luminance conversion for scalar and SIMD lanes.
///
- internal readonly struct Av1ConstantLuminanceColorOperator : IAv1ColorOperator
+ internal readonly struct HeifConstantLuminanceColorOperator : IHeifColorOperator
{
///
public static bool ChromaUsesLumaRange => false;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref float y, ref float cb, ref float cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref float y, ref float cb, ref float cr, in HeifColorConversionParameters parameters)
{
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
// Constant-luminance chroma has different positive and negative divisors. Reconstruct nonlinear
// red and blue first, then solve for green in linear light using the signaled transfer curve.
float nonlinearBlue = y + (2F * (cb <= 0F ? scales.NegativeBlue : scales.PositiveBlue) * cb);
float nonlinearRed = y + (2F * (cr <= 0F ? scales.NegativeRed : scales.PositiveRed) * cr);
- float linearY = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
- float linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
- float linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
+ float linearY = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
+ float linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
+ float linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
float linearGreen = (linearY - (parameters.Kr * linearRed) - (parameters.Kb * linearBlue)) / parameters.Kg;
y = nonlinearRed;
- cb = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cb = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
cr = nonlinearBlue;
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector128 y, ref Vector128 cb, ref Vector128 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector128 y, ref Vector128 cb, ref Vector128 cr, in HeifColorConversionParameters parameters)
{
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector128 blueScale = Vector128.ConditionalSelect(
Vector128.LessThanOrEqual(cb, Vector128.Zero),
Vector128.Create(scales.NegativeBlue),
@@ -50,23 +50,23 @@ internal abstract partial class Av1ColorConverterBase
Vector128.Create(scales.PositiveRed));
Vector128 nonlinearBlue = Vector128.MultiplyAddEstimate(Vector128.Create(2F) * blueScale, cb, y);
Vector128 nonlinearRed = Vector128.MultiplyAddEstimate(Vector128.Create(2F) * redScale, cr, y);
- Vector128 linearY = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
- Vector128 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
- Vector128 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
+ Vector128 linearY = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
+ Vector128 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
+ Vector128 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
Vector128 linearGreen = (
linearY - (Vector128.Create(parameters.Kr) * linearRed) - (Vector128.Create(parameters.Kb) * linearBlue))
/ Vector128.Create(parameters.Kg);
y = nonlinearRed;
- cb = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cb = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
cr = nonlinearBlue;
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector256 y, ref Vector256 cb, ref Vector256 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector256 y, ref Vector256 cb, ref Vector256 cr, in HeifColorConversionParameters parameters)
{
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector256 blueScale = Vector256.ConditionalSelect(
Vector256.LessThanOrEqual(cb, Vector256.Zero),
Vector256.Create(scales.NegativeBlue),
@@ -77,23 +77,23 @@ internal abstract partial class Av1ColorConverterBase
Vector256.Create(scales.PositiveRed));
Vector256 nonlinearBlue = Vector256.MultiplyAddEstimate(Vector256.Create(2F) * blueScale, cb, y);
Vector256 nonlinearRed = Vector256.MultiplyAddEstimate(Vector256.Create(2F) * redScale, cr, y);
- Vector256 linearY = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
- Vector256 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
- Vector256 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
+ Vector256 linearY = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
+ Vector256 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
+ Vector256 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
Vector256 linearGreen = (
linearY - (Vector256.Create(parameters.Kr) * linearRed) - (Vector256.Create(parameters.Kb) * linearBlue))
/ Vector256.Create(parameters.Kg);
y = nonlinearRed;
- cb = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cb = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
cr = nonlinearBlue;
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref Vector512 y, ref Vector512 cb, ref Vector512 cr, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref Vector512 y, ref Vector512 cb, ref Vector512 cr, in HeifColorConversionParameters parameters)
{
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector512 blueScale = Vector512.ConditionalSelect(
Vector512.LessThanOrEqual(cb, Vector512.Zero),
Vector512.Create(scales.NegativeBlue),
@@ -104,15 +104,15 @@ internal abstract partial class Av1ColorConverterBase
Vector512.Create(scales.PositiveRed));
Vector512 nonlinearBlue = Vector512.MultiplyAddEstimate(Vector512.Create(2F) * blueScale, cb, y);
Vector512 nonlinearRed = Vector512.MultiplyAddEstimate(Vector512.Create(2F) * redScale, cr, y);
- Vector512 linearY = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
- Vector512 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
- Vector512 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
+ Vector512 linearY = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, y);
+ Vector512 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearBlue);
+ Vector512 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearRed);
Vector512 linearGreen = (
linearY - (Vector512.Create(parameters.Kr) * linearRed) - (Vector512.Create(parameters.Kb) * linearBlue))
/ Vector512.Create(parameters.Kg);
y = nonlinearRed;
- cb = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cb = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
cr = nonlinearBlue;
}
@@ -122,21 +122,21 @@ internal abstract partial class Av1ColorConverterBase
float r,
float g,
float b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out float y,
out float cb,
out float cr)
{
// Luma is formed in linear light. The nonlinear red and blue differences then choose the
// sign-dependent denominators that define constant-luminance Cb and Cr.
- float linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
- float linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
- float linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
+ float linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
+ float linearGreen = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
+ float linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
float linearY = (parameters.Kr * linearRed) + (parameters.Kg * linearGreen) + (parameters.Kb * linearBlue);
- y = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
+ y = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
float blueDifference = b - y;
float redDifference = r - y;
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
cb = blueDifference / (2F * (blueDifference <= 0F ? scales.NegativeBlue : scales.PositiveBlue));
cr = redDifference / (2F * (redDifference <= 0F ? scales.NegativeRed : scales.PositiveRed));
}
@@ -147,22 +147,22 @@ internal abstract partial class Av1ColorConverterBase
Vector128 r,
Vector128 g,
Vector128 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector128 y,
out Vector128 cb,
out Vector128 cr)
{
- Vector128 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
- Vector128 linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
- Vector128 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
+ Vector128 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
+ Vector128 linearGreen = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
+ Vector128 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
Vector128 linearY = Vector128.MultiplyAddEstimate(
Vector128.Create(parameters.Kr),
linearRed,
Vector128.MultiplyAddEstimate(Vector128.Create(parameters.Kg), linearGreen, Vector128.Create(parameters.Kb) * linearBlue));
- y = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
+ y = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
Vector128 blueDifference = b - y;
Vector128 redDifference = r - y;
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector128 blueScale = Vector128.ConditionalSelect(
Vector128.LessThanOrEqual(blueDifference, Vector128.Zero),
Vector128.Create(scales.NegativeBlue),
@@ -182,22 +182,22 @@ internal abstract partial class Av1ColorConverterBase
Vector256 r,
Vector256 g,
Vector256 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector256 y,
out Vector256 cb,
out Vector256 cr)
{
- Vector256 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
- Vector256 linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
- Vector256 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
+ Vector256 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
+ Vector256 linearGreen = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
+ Vector256 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
Vector256 linearY = Vector256.MultiplyAddEstimate(
Vector256.Create(parameters.Kr),
linearRed,
Vector256.MultiplyAddEstimate(Vector256.Create(parameters.Kg), linearGreen, Vector256.Create(parameters.Kb) * linearBlue));
- y = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
+ y = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
Vector256 blueDifference = b - y;
Vector256 redDifference = r - y;
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector256 blueScale = Vector256.ConditionalSelect(
Vector256.LessThanOrEqual(blueDifference, Vector256.Zero),
Vector256.Create(scales.NegativeBlue),
@@ -217,22 +217,22 @@ internal abstract partial class Av1ColorConverterBase
Vector512 r,
Vector512 g,
Vector512 b,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector512 y,
out Vector512 cb,
out Vector512 cr)
{
- Vector512 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
- Vector512 linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
- Vector512 linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
+ Vector512 linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, r);
+ Vector512 linearGreen = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, g);
+ Vector512 linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, b);
Vector512 linearY = Vector512.MultiplyAddEstimate(
Vector512.Create(parameters.Kr),
linearRed,
Vector512.MultiplyAddEstimate(Vector512.Create(parameters.Kg), linearGreen, Vector512.Create(parameters.Kb) * linearBlue));
- y = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
+ y = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearY);
Vector512 blueDifference = b - y;
Vector512 redDifference = r - y;
- Av1ConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
+ HeifConstantLuminanceScales scales = parameters.ConstantLuminanceScales;
Vector512 blueScale = Vector512.ConditionalSelect(
Vector512.LessThanOrEqual(blueDifference, Vector512.Zero),
Vector512.Create(scales.NegativeBlue),
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ICtCpOperator.cs b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ICtCpOperator.cs
similarity index 74%
rename from src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ICtCpOperator.cs
rename to src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ICtCpOperator.cs
index 146623057d..325eaea586 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1ColorConverter.ICtCpOperator.cs
+++ b/src/ImageSharp/Formats/Heif/Color/HeifColorConverter.ICtCpOperator.cs
@@ -3,16 +3,16 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
-using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
-namespace SixLabors.ImageSharp.Formats.Heif.Av1;
+namespace SixLabors.ImageSharp.Formats.Heif.Color;
-internal abstract partial class Av1ColorConverterBase
+internal abstract partial class HeifColorConverterBase
{
///
/// Implements BT.2100 ICtCp conversion for scalar and SIMD lanes.
///
- internal readonly struct Av1ICtCpColorOperator : IAv1ColorOperator
+ internal readonly struct HeifICtCpColorOperator : IHeifColorOperator
{
///
/// The PQ Ct contribution to nonlinear L.
@@ -209,9 +209,9 @@ internal abstract partial class Av1ColorConverterBase
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ConvertToRgb(ref float intensity, ref float ct, ref float cp, in Av1ColorConversionParameters parameters)
+ public static void ConvertToRgb(ref float intensity, ref float ct, ref float cp, in HeifColorConversionParameters parameters)
{
- bool isHlg = parameters.TransferCharacteristics == ObuTransferCharacteristics.Hlg;
+ bool isHlg = parameters.TransferCharacteristics == CicpTransferCharacteristics.AribStdB67;
float ctToL = isHlg ? HlgCtToL : PqCtToL;
float cpToL = isHlg ? HlgCpToL : PqCpToL;
@@ -220,16 +220,16 @@ internal abstract partial class Av1ColorConverterBase
float nonlinearL = intensity + (ctToL * ct) + (cpToL * cp);
float nonlinearM = intensity - (ctToL * ct) - (cpToL * cp);
float nonlinearS = intensity + ((isHlg ? HlgCtToS : PqCtToS) * ct) + ((isHlg ? HlgCpToS : PqCpToS) * cp);
- float linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
- float linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
- float linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
+ float linearL = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
+ float linearM = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
+ float linearS = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
float linearRed = (LToRed * linearL) + (MToRed * linearM) + (SToRed * linearS);
float linearGreen = (LToGreen * linearL) + (MToGreen * linearM) + (SToGreen * linearS);
float linearBlue = (LToBlue * linearL) + (MToBlue * linearM) + (SToBlue * linearS);
- intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
- ct = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
- cp = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
+ intensity = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
+ ct = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cp = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
}
///
@@ -238,9 +238,9 @@ internal abstract partial class Av1ColorConverterBase
ref Vector128 intensity,
ref Vector128 ct,
ref Vector128 cp,
- in Av1ColorConversionParameters parameters)
+ in HeifColorConversionParameters parameters)
{
- bool isHlg = parameters.TransferCharacteristics == ObuTransferCharacteristics.Hlg;
+ bool isHlg = parameters.TransferCharacteristics == CicpTransferCharacteristics.AribStdB67;
Vector128 ctContribution = Vector128.Create(isHlg ? HlgCtToL : PqCtToL) * ct;
Vector128 cpContribution = Vector128.Create(isHlg ? HlgCpToL : PqCpToL) * cp;
Vector128 nonlinearL = intensity + ctContribution + cpContribution;
@@ -249,9 +249,9 @@ internal abstract partial class Av1ColorConverterBase
Vector128.Create(isHlg ? HlgCpToS : PqCpToS),
cp,
Vector128.MultiplyAddEstimate(Vector128.Create(isHlg ? HlgCtToS : PqCtToS), ct, intensity));
- Vector128 linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
- Vector128 linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
- Vector128 linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
+ Vector128 linearL = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
+ Vector128 linearM = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
+ Vector128 linearS = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
Vector128 linearRed = Vector128.MultiplyAddEstimate(
Vector128.Create(SToRed),
linearS,
@@ -265,9 +265,9 @@ internal abstract partial class Av1ColorConverterBase
linearS,
Vector128.MultiplyAddEstimate(Vector128.Create(MToBlue), linearM, Vector128.Create(LToBlue) * linearL));
- intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
- ct = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
- cp = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
+ intensity = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
+ ct = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cp = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
}
///
@@ -276,9 +276,9 @@ internal abstract partial class Av1ColorConverterBase
ref Vector256 intensity,
ref Vector256 ct,
ref Vector256 cp,
- in Av1ColorConversionParameters parameters)
+ in HeifColorConversionParameters parameters)
{
- bool isHlg = parameters.TransferCharacteristics == ObuTransferCharacteristics.Hlg;
+ bool isHlg = parameters.TransferCharacteristics == CicpTransferCharacteristics.AribStdB67;
Vector256 ctContribution = Vector256.Create(isHlg ? HlgCtToL : PqCtToL) * ct;
Vector256 cpContribution = Vector256.Create(isHlg ? HlgCpToL : PqCpToL) * cp;
Vector256 nonlinearL = intensity + ctContribution + cpContribution;
@@ -287,9 +287,9 @@ internal abstract partial class Av1ColorConverterBase
Vector256.Create(isHlg ? HlgCpToS : PqCpToS),
cp,
Vector256.MultiplyAddEstimate(Vector256.Create(isHlg ? HlgCtToS : PqCtToS), ct, intensity));
- Vector256 linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
- Vector256 linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
- Vector256 linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
+ Vector256 linearL = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
+ Vector256 linearM = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
+ Vector256 linearS = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
Vector256 linearRed = Vector256.MultiplyAddEstimate(
Vector256.Create(SToRed),
linearS,
@@ -303,9 +303,9 @@ internal abstract partial class Av1ColorConverterBase
linearS,
Vector256.MultiplyAddEstimate(Vector256.Create(MToBlue), linearM, Vector256.Create(LToBlue) * linearL));
- intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
- ct = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
- cp = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
+ intensity = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
+ ct = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cp = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
}
///
@@ -314,9 +314,9 @@ internal abstract partial class Av1ColorConverterBase
ref Vector512 intensity,
ref Vector512 ct,
ref Vector512 cp,
- in Av1ColorConversionParameters parameters)
+ in HeifColorConversionParameters parameters)
{
- bool isHlg = parameters.TransferCharacteristics == ObuTransferCharacteristics.Hlg;
+ bool isHlg = parameters.TransferCharacteristics == CicpTransferCharacteristics.AribStdB67;
Vector512 ctContribution = Vector512.Create(isHlg ? HlgCtToL : PqCtToL) * ct;
Vector512 cpContribution = Vector512.Create(isHlg ? HlgCpToL : PqCpToL) * cp;
Vector512 nonlinearL = intensity + ctContribution + cpContribution;
@@ -325,9 +325,9 @@ internal abstract partial class Av1ColorConverterBase
Vector512.Create(isHlg ? HlgCpToS : PqCpToS),
cp,
Vector512.MultiplyAddEstimate(Vector512.Create(isHlg ? HlgCtToS : PqCtToS), ct, intensity));
- Vector512 linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
- Vector512 linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
- Vector512 linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
+ Vector512 linearL = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL);
+ Vector512 linearM = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM);
+ Vector512 linearS = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS);
Vector512 linearRed = Vector512.MultiplyAddEstimate(
Vector512.Create(SToRed),
linearS,
@@ -341,9 +341,9 @@ internal abstract partial class Av1ColorConverterBase
linearS,
Vector512.MultiplyAddEstimate(Vector512.Create(MToBlue), linearM, Vector512.Create(LToBlue) * linearL));
- intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
- ct = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
- cp = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
+ intensity = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed);
+ ct = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen);
+ cp = HeifTransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue);
}
///
@@ -352,27 +352,27 @@ internal abstract partial class Av1ColorConverterBase
float red,
float green,
float blue,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out float intensity,
out float ct,
out float cp)
{
// ICtCp is defined in nonlinear LMS. Convert RGB to linear light, apply the LMS matrix, then
// apply the signaled PQ or HLG transfer curve before deriving intensity and the chroma axes.
- float linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red);
- float linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green);
- float linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, blue);
- float nonlinearL = Av1TransferFunctions.ToGamma(
+ float linearRed = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, red);
+ float linearGreen = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, green);
+ float linearBlue = HeifTransferFunctions.ToLinear(parameters.TransferCharacteristics, blue);
+ float nonlinearL = HeifTransferFunctions.ToGamma(
parameters.TransferCharacteristics,
(RedToL * linearRed) + (GreenToL * linearGreen) + (BlueToL * linearBlue));
- float nonlinearM = Av1TransferFunctions.ToGamma(
+ float nonlinearM = HeifTransferFunctions.ToGamma(
parameters.TransferCharacteristics,
(RedToM * linearRed) + (GreenToM * linearGreen) + (BlueToM * linearBlue));
- float nonlinearS = Av1TransferFunctions.ToGamma(
+ float nonlinearS = HeifTransferFunctions.ToGamma(
parameters.TransferCharacteristics,
(RedToS * linearRed) + (GreenToS * linearGreen) + (BlueToS * linearBlue));
intensity = 0.5F * (nonlinearL + nonlinearM);
- bool isHlg = parameters.TransferCharacteristics == ObuTransferCharacteristics.Hlg;
+ bool isHlg = parameters.TransferCharacteristics == CicpTransferCharacteristics.AribStdB67;
ct = isHlg
? (HlgLToCt * nonlinearL) + (HlgMToCt * nonlinearM) + (HlgSToCt * nonlinearS)
: (PqLToCt * nonlinearL) + (PqMToCt * nonlinearM) + (PqSToCt * nonlinearS);
@@ -388,14 +388,14 @@ internal abstract partial class Av1ColorConverterBase
Vector128 red,
Vector128 green,
Vector128 blue,
- in Av1ColorConversionParameters parameters,
+ in HeifColorConversionParameters parameters,
out Vector128 intensity,
out Vector128 ct,
out Vector128 cp)
{
- Vector128 linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red);
- Vector128 linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green);
- Vector128