Browse Source

Unify HEIF color conversion architecture

pull/2633/head
James Jackson-South 2 months ago
parent
commit
0ed638e395
  1. 15
      HEIF_IMPLEMENTATION_PLAN.md
  2. 91
      src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.CoefficientOperator.cs
  3. 16
      src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.Parameters.cs
  4. 198
      src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.Simd.cs
  5. 105
      src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.cs
  6. 31
      src/ImageSharp/Formats/Heif/Color/IHeifYuv420ToRgb8Source.cs
  7. 92
      src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Converter.cs
  8. 32
      src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Source.cs
  9. 16
      src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs

15
HEIF_IMPLEMENTATION_PLAN.md

@ -41,6 +41,7 @@ Checkboxes may be marked complete only when the implementation and the verificat
- [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] Keep optimized color arithmetic, fixed-point parameter storage, SIMD-width traversal, sample loading/storage, chroma filtering, and RGB packing primitives in the shared HEIF color namespace. AV1 and HEVC namespaces adapt their native planes, crop windows, and signaled chroma positions without owning parallel color arithmetic.
- [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.
- [x] Implement allocation-free SIMD-first HEVC intra prediction for all 35 modes, 4/8/16/32 blocks, and 8/10/12-bit samples, including three-tap and strong-bilinear reference filtering, negative-angle reference extension, luma boundary filters, and SIMD horizontal transposition. Verify the normal and forced-scalar paths against one scalar oracle.
- [x] Implement allocation-free reconstructed-plane reference collection and normative substitution for complete, empty, and partially available borders, including asymmetric 4:2:2 availability units and the HEVC mode/size/component filter-selection rules.
@ -48,14 +49,14 @@ Checkboxes may be marked complete only when the implementation and the verificat
- [x] Derive each reference-unit availability flag from coding-tree, slice, tile, constrained-intra, and picture-boundary state, then connect prediction to transform-unit traversal and the bounded item decoder lifecycle.
- [x] Register the bounded `hvc1` still-image decoder, reconstruct the selected PPS/SPS picture, crop the conformance window directly in the pooled row converter, and pass the native planes through the shared SIMD-first H.273 pipeline.
- [x] Compare native HEVC planes byte-for-byte with HM 18 output for four item payloads extracted from independently produced HEIC files: two 8-bit 4:2:0 grid tiles, a cropped 8-bit 4:2:0 grid tile, and an 8-bit 4:4:4 wavefront tile.
- [x] Compare complete HEIC presentation output with libheif 1.23.1 and libde265 1.1.1 for single images, multi-tile grids, an auxiliary alpha image, an ICC-bearing image, and a 4:4:4 wavefront image. Exact native-plane tests remain the codec oracle because libheif's selected fused 4:2:0 RGB path uses nearest-neighbor chroma while ImageSharp uses its higher-quality signaled-position interpolation.
- [x] Compare complete HEIC presentation output exactly with libheif 1.23.1 and libde265 1.1.1 for single images, multi-tile grids, an auxiliary alpha image, ICC-bearing images, 4:2:0, and 4:4:4 output. Keep the exact HM native-plane tests as the independent codec-reconstruction oracle.
- [ ] Add permanent HEVC 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 fixtures covering every exposed profile and Range Extensions tool, with exact native-plane references from HM or another independent decoder.
- [x] Implement the shared SIMD-first YUV/CICP paths in both directions for AV1 and HEVC bit depths, chroma formats, ranges, matrix coefficients, transfer functions, color primaries, and chroma positions, with scalar fallback only after the available vector widths are exhausted.
- [ ] Complete the independently generated AV1 and HEVC conformance matrix for every supported color-signaling combination before closing the Phase 5 color exit gate.
- [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.
- [x] Independently encode AVIF primary, grid, alpha-auxiliary, and two-frame sequence vectors with libavif 1.4.2/libaom 3.14.1; verify exact profile preservation, non-sRGB conversion, canonical-sRGB compaction, non-sRGB compact preservation, metadata skipping, every presented frame, and unchanged composed alpha.
- [ ] Repeat the complete ICC preservation, conversion, compaction, and skipping matrix for HEVC primary, grid, alpha-auxiliary, and sequence paths. The current libheif comparison establishes presentation interoperability for one ICC-bearing still image but does not close these separate color-management contracts.
- [ ] Repeat the complete ICC preservation, conversion, compaction, and skipping matrix for HEVC primary, grid, alpha-auxiliary, and sequence paths. Exact ICC-bearing still-image presentation is covered, but it does not close these separate color-management contracts.
- [ ] 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.
@ -282,8 +283,8 @@ This assessment is based on the current source after the upstream ImageSharp mer
- Item IDs are resolved as keys rather than list indices; metadata children are indexed and parsed in dependency order rather than physical order.
- Item property associations now preserve physical `ipco` indices, apply one-based 7-bit or 15-bit indices and essential flags, associate by item ID, and reject arbitrary unknown essential properties.
- Item locations now support bounded file-relative and `idat`-relative storage, multiple ordered extents, versioned item IDs, 0/4/8-byte registered field sizes, and 64-bit offsets. Referenced-item construction method two and external data references remain explicitly unsupported.
- Grid derived-image decoding now parses both registered descriptor widths, resolves the ordered `dimg` cells, validates tile count, coding format, dimensions, canvas coverage, and edge overlap, then composes the output through ImageSharp row buffers. Unsupported grid tile codecs can select only a decodable thumbnail of the same primary grid. HEVC grids are exercised by independent libheif output and exact HM tile-plane references; independent AV1 and JPEG grid fixtures are still required.
- Alpha auxiliary decoding now recognizes `auxC`, `auxl`, and `prem`, supports a direct auxiliary image/grid and the per-color-grid-tile form, preserves normalized alpha through `L16`/`Rgba64`, box-resamples differing plane sizes, and reports alpha presence. HEVC auxiliary-alpha presentation is exercised against independent libheif output, while independent AVIF alpha fixtures are still required against the incomplete AV1 reconstruction pipeline.
- Grid derived-image decoding now parses both registered descriptor widths, resolves the ordered `dimg` cells, validates tile count, coding format, dimensions, canvas coverage, and edge overlap, then composes the output through ImageSharp row buffers. Unsupported grid tile codecs can select only a decodable thumbnail of the same primary grid. HEVC grids have exact complete-image libheif references and exact HM tile-plane references; independent AV1 and JPEG grid fixtures are still required.
- Alpha auxiliary decoding now recognizes `auxC`, `auxl`, and `prem`, supports a direct auxiliary image/grid and the per-color-grid-tile form, preserves normalized alpha through `L16`/`Rgba64`, box-resamples differing plane sizes, and reports alpha presence. HEVC auxiliary-alpha presentation matches the independent libheif reference exactly, while independent AVIF alpha fixtures are still required against the incomplete AV1 reconstruction pipeline.
- Clean aperture, image rotation, and image mirror properties now validate their registered payloads, exact integer crop geometry, and essential associations; affect Identify dimensions; and reuse ImageSharp's optimized crop/rotate/flip processors after auxiliary alpha composition in the MIAF-defined order. Independent transform vectors must still verify every crop/rotation/mirror/alpha combination.
- Decode now resolves `cdsc`-associated Exif and `application/rdf+xml` XMP items for the primary still image, validates the declared Exif TIFF-header offset, and attaches the payloads through ImageSharp's existing profile types before presentation transforms. Independent AVIF, HEIC, and HIF metadata fixtures and Identify-time profile reporting remain required.
- ICC and `nclx` CICP color properties are now associated with the presented color item instead of global parser state, validated, and exposed on Decode and Identify through the existing ImageSharp profiles. AV1 decode requires specified sequence-header fields and the range flag to match `nclx`, uses the property only for fields the bitstream leaves unspecified, and exposes the effective sequence-header CICP description when no property is present. Independent libavif fixtures verify exact ICC byte preservation, observable non-sRGB conversion, canonical-sRGB compaction, non-sRGB compact preservation, and metadata skipping for primary, grid, alpha-composited, and every presented sequence frame; the alpha vector also verifies that ICC conversion does not alter composed alpha. Identify-time bitstream fallback, independent CICP vectors, and HEVC integration remain required.
@ -295,9 +296,9 @@ This assessment is based on the current source after the upstream ImageSharp mer
### HEVC decoder and encoder
- `Heif4CharCode` recognizes `hvc1` image items, `HevcCodecConfiguration` validates and associates the bounded `hvcC` property, Identify reports its HEVC precision and monochrome shape, and `HeifCompressionFactory` registers the bounded HEVC still-image decoder.
- The HEVC path validates NAL headers and RBSP escaping, links `hvcC` VPS/SPS/PPS structures through Range Extensions, parses one length-delimited base-layer IDR picture, and reconstructs its complete intra-coded coding tree. It includes CABAC, transform traversal, coefficient decoding, intra prediction, inverse quantization and transform, PCM, deblocking, sample-adaptive offset, and allocator-owned native `ushort` planes for monochrome, 4:2:0, 4:2:2, 4:4:4, separate-color-plane, and 8-through-16-bit samples. The item adapter converts the SPS conformance window directly through the shared pooled SIMD-first H.273 pipeline and preserves the effective container-or-VUI CICP description. Four real item payloads now match HM 18 native planes exactly, covering 8-bit 4:2:0 grids, cropping, 8-bit 4:4:4, and wavefront entry points. Complete images covering grids, auxiliary alpha, ICC, and 4:4:4 presentation also match independent libheif output within the documented chroma-reconstruction tolerance. High bit depths, monochrome, 4:2:2, remaining Range Extensions tools, and the complete color matrix still require independent fixtures before HEVC conformance is established.
- The HEVC path validates NAL headers and RBSP escaping, links `hvcC` VPS/SPS/PPS structures through Range Extensions, parses one length-delimited base-layer IDR picture, and reconstructs its complete intra-coded coding tree. It includes CABAC, transform traversal, coefficient decoding, intra prediction, inverse quantization and transform, PCM, deblocking, sample-adaptive offset, and allocator-owned native `ushort` planes for monochrome, 4:2:0, 4:2:2, 4:4:4, separate-color-plane, and 8-through-16-bit samples. The item adapter converts the SPS conformance window directly through the shared pooled SIMD-first H.273 pipeline and preserves the effective container-or-VUI CICP description. Four real item payloads match HM 18 native planes exactly, covering 8-bit 4:2:0 grids, cropping, 8-bit 4:4:4, and wavefront entry points. Complete images covering grids, auxiliary alpha, ICC, 4:2:0, and 4:4:4 presentation also match independent libheif output exactly. High bit depths, monochrome, 4:2:2, remaining Range Extensions tools, and the complete color matrix still require independent fixtures before HEVC conformance is established.
- There is no HEVC encoder. The current HEIC-branded encoder writes a legacy JPEG payload and therefore cannot provide HEIC output.
- Permanent HEVC conformance tests compare extracted item planes exactly with HM output and complete presented images with pinned libheif/libde265 reference PNGs. The fixtures do not yet cover the complete exposed profile matrix.
- Permanent HEVC conformance tests compare extracted item planes exactly with HM output and complete presented images exactly with pinned libheif/libde265 reference PNGs. The fixtures do not yet cover the complete exposed profile matrix.
### AV1 decoder
@ -489,7 +490,7 @@ Implement and verify in dependency order:
- [ ] Tiles, wavefront entry points, dependent slices, and all other parallelization syntax permitted by the exposed still-image profiles.
- [ ] Supplemental enhancement information that changes image presentation or metadata exposed by ImageSharp.
- [x] Establish exact HM 18 native-plane conformance for four real 8-bit item payloads covering 4:2:0, 4:4:4, cropping, multiple grid positions, and wavefront entry points.
- [x] Establish independent libheif/libde265 presentation comparisons for single images, grids, auxiliary alpha, ICC, and 4:4:4 output without weakening the exact native-plane codec oracle.
- [x] Establish exact independent presentation verification for single images, grids, auxiliary alpha, ICC, 4:2:0, and 4:4:4 output without weakening the exact native-plane codec oracle.
- [ ] Establish exact native-plane conformance for 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 across every exposed HEVC profile and required Range Extensions tool.
Each subsystem keeps a specification-shaped scalar oracle derived from the HEVC specification and the pinned implementation reference, while production hot paths are designed SIMD-first with scalar fallback. No valid syntax in the exposed HEIC profiles may terminate in an unsupported branch or silently omit a normative reconstruction stage.

91
src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.CoefficientOperator.cs

@ -0,0 +1,91 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Color;
/// <content>
/// Provides full-range fixed-point coefficient conversion.
/// </content>
internal static partial class HeifYuv420ToRgb8Converter
{
/// <summary>
/// Implements full-range coefficient-based YCbCr conversion for scalar and SIMD lanes.
/// </summary>
private readonly struct FixedPointCoefficientOperator : IHeifYuv420ToRgb8Operator
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(ushort y, ushort cb, ushort cr, in FixedPointParameters parameters, out byte r, out byte g, out byte b)
{
int centeredBlue = cb - ChromaMidpoint;
int centeredRed = cr - ChromaMidpoint;
// All overloads preserve this term grouping and round once after the complete contribution for a
// component has been accumulated, so vector width cannot change an output code value.
int red = y + (((parameters.RedCr * centeredRed) + RoundingBias) >> CoefficientShift);
int green = y + (((parameters.GreenCb * centeredBlue) + (parameters.GreenCr * centeredRed) + RoundingBias) >> CoefficientShift);
int blue = y + (((parameters.BlueCb * centeredBlue) + RoundingBias) >> CoefficientShift);
r = (byte)Numerics.Clamp(red, 0, byte.MaxValue);
g = (byte)Numerics.Clamp(green, 0, byte.MaxValue);
b = (byte)Numerics.Clamp(blue, 0, byte.MaxValue);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in Vector128Parameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector128.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
g = Vector128.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
b = Vector128.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in Vector256Parameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector256.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
g = Vector256.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
b = Vector256.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in Vector512Parameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector512.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
g = Vector512.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
b = Vector512.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum);
}
}
}

16
src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Converter.Parameters.cs → src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.Parameters.cs

@ -4,12 +4,12 @@
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Formats.Heif.Color;
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color;
namespace SixLabors.ImageSharp.Formats.Heif.Color;
/// <content>
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
/// </content>
internal static partial class HevcYuv420ToRgb8Converter
internal static partial class HeifYuv420ToRgb8Converter
{
/// <summary>
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
@ -103,8 +103,8 @@ internal static partial class HevcYuv420ToRgb8Converter
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector128Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector128.Create(HevcYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector128.Create(HevcYuv420ToRgb8Converter.RoundingBias);
this.ChromaMidpoint = Vector128.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector128.Create(HeifYuv420ToRgb8Converter.RoundingBias);
this.Maximum = Vector128.Create((int)byte.MaxValue);
this.RedCr = Vector128.Create(parameters.RedCr);
this.GreenCb = Vector128.Create(parameters.GreenCb);
@ -159,8 +159,8 @@ internal static partial class HevcYuv420ToRgb8Converter
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector256Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector256.Create(HevcYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector256.Create(HevcYuv420ToRgb8Converter.RoundingBias);
this.ChromaMidpoint = Vector256.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector256.Create(HeifYuv420ToRgb8Converter.RoundingBias);
this.Maximum = Vector256.Create((int)byte.MaxValue);
this.RedCr = Vector256.Create(parameters.RedCr);
this.GreenCb = Vector256.Create(parameters.GreenCb);
@ -215,8 +215,8 @@ internal static partial class HevcYuv420ToRgb8Converter
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector512Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector512.Create(HevcYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector512.Create(HevcYuv420ToRgb8Converter.RoundingBias);
this.ChromaMidpoint = Vector512.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector512.Create(HeifYuv420ToRgb8Converter.RoundingBias);
this.Maximum = Vector512.Create((int)byte.MaxValue);
this.RedCr = Vector512.Create(parameters.RedCr);
this.GreenCb = Vector512.Create(parameters.GreenCb);

198
src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Converter.Simd.cs → src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.Simd.cs

@ -7,16 +7,92 @@ using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
using static SixLabors.ImageSharp.Formats.Heif.Color.HeifColorConverterBase;
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color;
namespace SixLabors.ImageSharp.Formats.Heif.Color;
/// <content>
/// Provides the fixed-point scalar and SIMD row kernels for eight-bit 4:2:0 conversion.
/// Provides the operator-driven SIMD row traversal for eight-bit 4:2:0 conversion.
/// </content>
internal static partial class HevcYuv420ToRgb8Converter
internal static partial class HeifYuv420ToRgb8Converter
{
/// <summary>
/// Defines fixed-point color arithmetic for scalar and SIMD lanes.
/// </summary>
private interface IHeifYuv420ToRgb8Operator
{
/// <summary>
/// Converts one full-range YCbCr sample to eight-bit RGB.
/// </summary>
/// <param name="y">The luma sample.</param>
/// <param name="cb">The blue-difference sample.</param>
/// <param name="cr">The red-difference sample.</param>
/// <param name="parameters">The scalar fixed-point parameters.</param>
/// <param name="r">The converted red sample.</param>
/// <param name="g">The converted green sample.</param>
/// <param name="b">The converted blue sample.</param>
public static abstract void Convert(ushort y, ushort cb, ushort cr, in FixedPointParameters parameters, out byte r, out byte g, out byte b);
/// <summary>
/// Converts four full-range YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The four-lane fixed-point parameters.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
public static abstract void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in Vector128Parameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b);
/// <summary>
/// Converts eight full-range YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The eight-lane fixed-point parameters.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
public static abstract void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in Vector256Parameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b);
/// <summary>
/// Converts sixteen full-range YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The sixteen-lane fixed-point parameters.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
public static abstract void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in Vector512Parameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b);
}
/// <summary>
/// Converts one luma row and its nearest native chroma row to planar eight-bit RGB.
/// </summary>
/// <typeparam name="TOperator">The fixed-point color arithmetic selected for the row.</typeparam>
/// <param name="luma">The full-resolution luma samples.</param>
/// <param name="chromaBlue">The half-width blue-difference samples.</param>
/// <param name="chromaRed">The half-width red-difference samples.</param>
@ -24,7 +100,7 @@ internal static partial class HevcYuv420ToRgb8Converter
/// <param name="green">The destination green samples.</param>
/// <param name="blue">The destination blue samples.</param>
/// <param name="parameters">The fixed-point matrix coefficients.</param>
private static void ConvertRow(
private static void ConvertRow<TOperator>(
ReadOnlySpan<ushort> luma,
ReadOnlySpan<ushort> chromaBlue,
ReadOnlySpan<ushort> chromaRed,
@ -32,6 +108,7 @@ internal static partial class HevcYuv420ToRgb8Converter
Span<byte> green,
Span<byte> blue,
in ConversionParameters parameters)
where TOperator : struct, IHeifYuv420ToRgb8Operator
{
ref ushort lumaBase = ref MemoryMarshal.GetReference(luma);
ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue);
@ -53,7 +130,7 @@ internal static partial class HevcYuv420ToRgb8Converter
Vector512<int> cb = LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector512<int> cr = LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1));
Convert(y, cb, cr, in parameters.SixteenLane, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b);
TOperator.Convert(y, cb, cr, in parameters.SixteenLane, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b);
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleStorer.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -70,7 +147,7 @@ internal static partial class HevcYuv420ToRgb8Converter
Vector256<int> cb = LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector256<int> cr = LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1));
Convert(y, cb, cr, in parameters.EightLane, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b);
TOperator.Convert(y, cb, cr, in parameters.EightLane, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b);
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleStorer.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -87,7 +164,7 @@ internal static partial class HevcYuv420ToRgb8Converter
Vector128<int> cb = LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector128<int> cr = LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1));
Convert(y, cb, cr, in parameters.FourLane, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b);
TOperator.Convert(y, cb, cr, in parameters.FourLane, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b);
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleStorer.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -96,7 +173,7 @@ internal static partial class HevcYuv420ToRgb8Converter
for (; x < luma.Length; x++)
{
Convert(
TOperator.Convert(
Unsafe.Add(ref lumaBase, x),
Unsafe.Add(ref chromaBlueBase, x >> 1),
Unsafe.Add(ref chromaRedBase, x >> 1),
@ -185,109 +262,4 @@ internal static partial class HevcYuv420ToRgb8Converter
Vector128<ushort> repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16();
return Vector128.WidenLower(repeated).AsInt32();
}
/// <summary>
/// Converts one coefficient-based H.273 YCbCr sample to eight-bit RGB.
/// </summary>
/// <param name="y">The luma sample.</param>
/// <param name="cb">The blue-difference sample.</param>
/// <param name="cr">The red-difference sample.</param>
/// <param name="parameters">The fixed-point matrix coefficients.</param>
/// <param name="r">The converted red sample.</param>
/// <param name="g">The converted green sample.</param>
/// <param name="b">The converted blue sample.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Convert(ushort y, ushort cb, ushort cr, in FixedPointParameters parameters, out byte r, out byte g, out byte b)
{
int centeredBlue = cb - ChromaMidpoint;
int centeredRed = cr - ChromaMidpoint;
int red = y + (((parameters.RedCr * centeredRed) + RoundingBias) >> CoefficientShift);
int green = y + (((parameters.GreenCb * centeredBlue) + (parameters.GreenCr * centeredRed) + RoundingBias) >> CoefficientShift);
int blue = y + (((parameters.BlueCb * centeredBlue) + RoundingBias) >> CoefficientShift);
r = (byte)Numerics.Clamp(red, 0, byte.MaxValue);
g = (byte)Numerics.Clamp(green, 0, byte.MaxValue);
b = (byte)Numerics.Clamp(blue, 0, byte.MaxValue);
}
/// <summary>
/// Converts four coefficient-based H.273 YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The fixed-point matrix coefficient lanes.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in Vector128Parameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector128.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum);
g = Vector128.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum);
b = Vector128.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum);
}
/// <summary>
/// Converts eight coefficient-based H.273 YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The fixed-point matrix coefficient lanes.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in Vector256Parameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector256.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum);
g = Vector256.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum);
b = Vector256.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum);
}
/// <summary>
/// Converts sixteen coefficient-based H.273 YCbCr samples to eight-bit RGB lanes.
/// </summary>
/// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The fixed-point matrix coefficient lanes.</param>
/// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in Vector512Parameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
cb -= parameters.ChromaMidpoint;
cr -= parameters.ChromaMidpoint;
r = Vector512.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum);
g = Vector512.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum);
b = Vector512.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum);
}
}

105
src/ImageSharp/Formats/Heif/Color/HeifYuv420ToRgb8Converter.cs

@ -0,0 +1,105 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heif.Color;
/// <summary>
/// Converts full-range eight-bit HEIF 4:2:0 planes with an unspecified matrix to packed RGB pixels.
/// </summary>
internal static partial class HeifYuv420ToRgb8Converter
{
/// <summary>
/// The fixed-point precision used for H.273 matrix coefficients.
/// </summary>
private const int CoefficientShift = 8;
/// <summary>
/// The half-unit bias used before fixed-point coefficient results are shifted to integer samples.
/// </summary>
private const int RoundingBias = 1 << (CoefficientShift - 1);
/// <summary>
/// The neutral code value for full-range eight-bit chroma.
/// </summary>
private const int ChromaMidpoint = 128;
/// <summary>
/// Determines whether the specialized integer conversion supports the supplied plane and color description.
/// </summary>
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
/// <param name="lumaBitDepth">The luma sample precision in bits.</param>
/// <param name="chromaBitDepth">The chroma sample precision in bits.</param>
/// <param name="isFullRange">Whether the samples use the complete numeric range.</param>
/// <param name="matrixCoefficients">The H.273 matrix-coefficient code point.</param>
/// <param name="mode">The resolved H.273 conversion operation.</param>
/// <returns><see langword="true"/> when the planes can use this converter; otherwise, <see langword="false"/>.</returns>
public static bool IsSupported(
int subsamplingX,
int subsamplingY,
int lumaBitDepth,
int chromaBitDepth,
bool isFullRange,
CicpMatrixCoefficients matrixCoefficients,
HeifColorConversionMode mode)
=> subsamplingX == 1
&& subsamplingY == 1
&& lumaBitDepth == 8
&& chromaBitDepth == 8
&& isFullRange
&& matrixCoefficients == CicpMatrixCoefficients.Unspecified
&& mode == HeifColorConversionMode.Coefficients;
/// <summary>
/// Converts supported HEIF component planes to packed pixels using integer SIMD with a scalar tail.
/// </summary>
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
/// <typeparam name="TSource">The codec adapter that exposes reconstructed component rows.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="source">The reconstructed component-plane source.</param>
/// <param name="image">The destination image frame.</param>
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
public static void Convert<TPixel, TSource>(
Configuration configuration,
TSource source,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
where TSource : struct, IHeifYuv420ToRgb8Source
{
ConversionParameters conversionParameters = new(in parameters);
using IMemoryOwner<byte> componentOwner = configuration.MemoryAllocator.Allocate<byte>(image.Width * 3);
Span<byte> components = componentOwner.GetSpan();
Span<byte> red = components[..image.Width];
Span<byte> green = components.Slice(image.Width, image.Width);
Span<byte> blue = components.Slice(image.Width * 2, image.Width);
// The value-type source closes the row-access contract at the call site. Constrained calls are therefore
// devirtualized without boxing while keeping codec-specific buffer ownership outside the color pipeline.
for (int y = 0; y < image.Height; y++)
{
int lumaY = sourceY + y;
// The codec boundary validates 4:2:0 crop offsets in complete chroma-sample units. Each native chroma
// sample therefore covers one 2x2 luma cell without an alignment branch in the SIMD loop.
ReadOnlySpan<ushort> luma = source.GetLumaRow(lumaY).Slice(sourceX, image.Width);
ReadOnlySpan<ushort> chromaBlue = source.GetChromaBlueRow(lumaY >> 1).Slice(sourceX >> 1);
ReadOnlySpan<ushort> chromaRed = source.GetChromaRedRow(lumaY >> 1).Slice(sourceX >> 1);
ConvertRow<FixedPointCoefficientOperator>(luma, chromaBlue, chromaRed, red, green, blue, in conversionParameters);
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination);
}
}
}

31
src/ImageSharp/Formats/Heif/Color/IHeifYuv420ToRgb8Source.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Color;
/// <summary>
/// Exposes reconstructed eight-bit 4:2:0 component rows to the shared fixed-point HEIF converter.
/// </summary>
internal interface IHeifYuv420ToRgb8Source
{
/// <summary>
/// Gets one complete luma row in native sample storage.
/// </summary>
/// <param name="row">The zero-based luma row.</param>
/// <returns>The reconstructed luma samples.</returns>
public ReadOnlySpan<ushort> GetLumaRow(int row);
/// <summary>
/// Gets one complete blue-difference chroma row in native sample storage.
/// </summary>
/// <param name="row">The zero-based chroma row.</param>
/// <returns>The reconstructed blue-difference samples.</returns>
public ReadOnlySpan<ushort> GetChromaBlueRow(int row);
/// <summary>
/// Gets one complete red-difference chroma row in native sample storage.
/// </summary>
/// <param name="row">The zero-based chroma row.</param>
/// <returns>The reconstructed red-difference samples.</returns>
public ReadOnlySpan<ushort> GetChromaRedRow(int row);
}

92
src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Converter.cs

@ -1,92 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Heif.Color;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color;
/// <summary>
/// Converts full-range eight-bit HEVC 4:2:0 planes with an unspecified matrix to packed RGB pixels.
/// </summary>
internal static partial class HevcYuv420ToRgb8Converter
{
/// <summary>
/// The fixed-point precision used for H.273 matrix coefficients.
/// </summary>
private const int CoefficientShift = 8;
/// <summary>
/// The half-unit bias used before fixed-point coefficient results are shifted to integer samples.
/// </summary>
private const int RoundingBias = 1 << (CoefficientShift - 1);
/// <summary>
/// The neutral code value for full-range eight-bit chroma.
/// </summary>
private const int ChromaMidpoint = 128;
/// <summary>
/// Determines whether the specialized integer conversion supports the supplied picture and color description.
/// </summary>
/// <param name="picture">The reconstructed HEVC picture.</param>
/// <param name="colorProfile">The effective H.273 color description.</param>
/// <param name="mode">The resolved H.273 conversion operation.</param>
/// <returns><see langword="true"/> when the picture can use this converter; otherwise, <see langword="false"/>.</returns>
public static bool IsSupported(HevcPictureBuffer picture, CicpProfile colorProfile, HeifColorConversionMode mode)
=> picture.ChromaFormat == 1
&& !picture.SeparateColorPlane
&& picture.BitDepthLuma == 8
&& picture.BitDepthChroma == 8
&& colorProfile.FullRange
&& colorProfile.MatrixCoefficients == CicpMatrixCoefficients.Unspecified
&& mode == HeifColorConversionMode.Coefficients;
/// <summary>
/// Converts a supported HEVC picture to packed pixels using integer SIMD with a scalar tail.
/// </summary>
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="picture">The reconstructed HEVC picture.</param>
/// <param name="image">The destination image frame.</param>
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
public static void Convert<TPixel>(
Configuration configuration,
HevcPictureBuffer picture,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
{
ConversionParameters conversionParameters = new(in parameters);
using IMemoryOwner<byte> componentOwner = configuration.MemoryAllocator.Allocate<byte>(image.Width * 3);
Span<byte> components = componentOwner.GetSpan();
Span<byte> red = components[..image.Width];
Span<byte> green = components.Slice(image.Width, image.Width);
Span<byte> blue = components.Slice(image.Width * 2, image.Width);
for (int y = 0; y < image.Height; y++)
{
int lumaY = sourceY + y;
// HEVC expresses 4:2:0 conformance-window offsets in complete chroma sample units, so both source
// offsets are even here. The unspecified-matrix presentation replicates each native chroma sample
// across its 2x2 luma cell before applying the default BT.601 coefficients.
ReadOnlySpan<ushort> luma = picture.GetRowSpan(HevcPlane.Y, lumaY).Slice(sourceX, image.Width);
ReadOnlySpan<ushort> chromaBlue = picture.GetRowSpan(HevcPlane.Cb, lumaY >> 1).Slice(sourceX >> 1);
ReadOnlySpan<ushort> chromaRed = picture.GetRowSpan(HevcPlane.Cr, lumaY >> 1).Slice(sourceX >> 1);
ConvertRow(luma, chromaBlue, chromaRed, red, green, blue, in conversionParameters);
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination);
}
}
}

32
src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuv420ToRgb8Source.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Color;
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color;
/// <summary>
/// Adapts reconstructed HEVC planes to the shared fixed-point HEIF color converter.
/// </summary>
internal readonly struct HevcYuv420ToRgb8Source : IHeifYuv420ToRgb8Source
{
/// <summary>
/// The reconstructed HEVC picture containing the component planes.
/// </summary>
private readonly HevcPictureBuffer picture;
/// <summary>
/// Initializes a new instance of the <see cref="HevcYuv420ToRgb8Source"/> struct.
/// </summary>
/// <param name="picture">The reconstructed HEVC picture.</param>
public HevcYuv420ToRgb8Source(HevcPictureBuffer picture) => this.picture = picture;
/// <inheritdoc/>
public ReadOnlySpan<ushort> GetLumaRow(int row) => this.picture.GetRowSpan(HevcPlane.Y, row);
/// <inheritdoc/>
public ReadOnlySpan<ushort> GetChromaBlueRow(int row) => this.picture.GetRowSpan(HevcPlane.Cb, row);
/// <inheritdoc/>
public ReadOnlySpan<ushort> GetChromaRedRow(int row) => this.picture.GetRowSpan(HevcPlane.Cr, row);
}

16
src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs

@ -61,11 +61,19 @@ internal static partial class HevcYuvConverter
{
HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out HeifColorConversionMode mode);
// H.273 resolves an unspecified matrix to BT.601 coefficients. The common full-range eight-bit 4:2:0
// presentation can therefore remain in the integer sample domain and avoid float staging and rounding.
if (HevcYuv420ToRgb8Converter.IsSupported(picture, colorProfile, mode))
// The common full-range eight-bit path remains in the integer sample domain. The codec adapter exposes
// only native rows; shared HEIF code owns coefficient arithmetic, SIMD traversal, pooling, and packing.
if (HeifYuv420ToRgb8Converter.IsSupported(
picture.GetSubsamplingX(HevcPlane.Cb),
picture.GetSubsamplingY(HevcPlane.Cb),
picture.BitDepthLuma,
picture.BitDepthChroma,
colorProfile.FullRange,
colorProfile.MatrixCoefficients,
mode))
{
HevcYuv420ToRgb8Converter.Convert(configuration, picture, image, in parameters, sourceX, sourceY);
HevcYuv420ToRgb8Source source = new(picture);
HeifYuv420ToRgb8Converter.Convert(configuration, source, image, in parameters, sourceX, sourceY);
return;
}

Loading…
Cancel
Save