Browse Source

Preserve HEVC high-bit presentation precision

pull/2633/head
James Jackson-South 3 days ago
parent
commit
692e21c601
  1. 30
      HEIF_IMPLEMENTATION_PLAN.md
  2. 4
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifPlanarColorConverter.cs
  3. 75
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifSampleConversion.cs
  4. 261
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Parameters.cs
  5. 105
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.cs
  6. 151
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs
  7. 83
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs
  8. 293
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs
  9. 347
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs
  10. 114
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs
  11. 104
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.FixedPointCoefficientOperator.cs
  12. 125
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs
  13. 82
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs
  14. 113
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Operator.cs
  15. 540
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs
  16. 207
      src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs
  17. 44
      src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs
  18. 867
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs
  19. 256
      tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs
  20. 74
      tests/ImageSharp.Tests/TestImages.cs
  21. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-10b-444.png
  22. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-12b-444.png
  23. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-8b-444.png
  24. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-400.png
  25. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-420.png
  26. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-422.png
  27. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-444.png
  28. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-400.png
  29. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-420.png
  30. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-422.png
  31. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-444.png
  32. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-400.png
  33. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-420.png
  34. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-422.png
  35. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-444.png
  36. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProgressiveSingleReferenceMatchesPinnedReferences_Rgba32_libavif-progressive-draw-points-8b.png
  37. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifGlobalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-global-warp.png
  38. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-local-warp.png
  39. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifObmcSequenceMatchesPinnedReferences_Rgba32_libavif-webp-logo-obmc.png
  40. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequenceWithEqualAverageCompoundMatchesPinnedReferences_Rgba32_libavif-webp-logo-average-compound.png
  41. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-difference-weighted-compound.png
  42. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-distance-weighted-compound.png
  43. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-inter-intra.png
  44. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-wedge-compound.png
  45. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeScaledReferenceMatchesPinnedReferences_Rgba32_libavif-webp-logo-scaled-reference.png
  46. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeSelectedProgressiveSpatialLayerMatchesPinnedReferences_Rgba32_libavif-webp-logo-scaled-reference-lsel0.png
  47. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-cosmos-10b.png
  48. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-cosmos-12b.png
  49. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-kodim23-8b.png
  50. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-10b-444.png
  51. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-12b-444.png
  52. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-8b-444.png
  53. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-cosmos-10b.png
  54. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-cosmos-12b.png
  55. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-kodim23-8b.png
  56. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithPaletteMatchesPinnedLibavifPresentation_Rgba32_libavif-palette-draw-points-8b.png
  57. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-cosmos-10b.png
  58. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-cosmos-12b.png
  59. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-kodim23-8b.png
  60. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png
  61. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png
  62. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png
  63. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png
  64. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png
  65. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png
  66. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png
  67. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png
  68. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png
  69. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png
  70. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png
  71. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png
  72. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png
  73. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png
  74. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png
  75. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png
  76. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png
  77. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png
  78. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png
  79. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png
  80. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png
  81. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png
  82. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png
  83. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png
  84. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png
  85. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png
  86. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png
  87. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png
  88. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png
  89. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png
  90. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png
  91. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png
  92. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png
  93. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png
  94. 3
      tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png
  95. 4
      tests/Images/Input/Heif/Av1/Conformance/README.md
  96. 3
      tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0-ffmpeg.png
  97. 3
      tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0-ffmpeg.png
  98. 3
      tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0-ffmpeg.png
  99. 3
      tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0-ffmpeg.png
  100. 3
      tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended-hm-ffmpeg.png

30
HEIF_IMPLEMENTATION_PLAN.md

@ -29,7 +29,7 @@ Checkboxes may be marked complete only when the implementation and the verificat
## Delivery dashboard ## Delivery dashboard
Last reconciled with the source tree on 2026-08-30 against production checkpoint `9d7348efd`. Committed checkpoints include the AV1 transform architecture, OBU framing, tile partitioning, mode information, segmentation, delta quantization, transform-size selection, coefficient decoding, inverse quantization, inverse transforms, complete intra prediction, intra-block copy, official extreme-displacement intra-block-copy conformance, the 12-profile reconstruction matrix, corrected twelve-bit inverse-transform SIMD arithmetic, layered-item properties, layered reference/header/CDF/motion-field state, inter-frame intra blocks, SIMD-first translational prediction, complete single-reference inter reconstruction, compound reference trees and modes, paired reference-MV derivation, reference-dependent bounded sequence decoding, allocation-free SIMD-first equal averaging, selected inter-intra prediction, selectable compound blending, OBMC, scaled-reference reconstruction, local warped prediction, non-translational global prediction, official motion-vector conformance, official spatial- and temporal-layer default-operating-point conformance, official active-film-grain and monochrome sequence conformance, selected spatial-layer presentation, progressive color and auxiliary-alpha conformance, pinned-HM HEVC CABAC verification, pinned-HM HEVC coding-tree traversal verification, official HEVC intra-prediction verification, official HEVC residual-reconstruction verification, HEVC parallel slice decoding, bounded HEVC supplemental presentation and metadata handling, and the complete independently coded HEVC Range Extensions native-plane profile/tool matrix. This dashboard is the authoritative delivery order. The detailed phase checklists below provide subsystem evidence; they do not override the current-stage marker or permit work to skip ahead. Last reconciled with the source tree on 2026-08-30 against production checkpoint `640b64ee5` and the current local HEVC precision and presentation corrections. Committed checkpoints include the AV1 transform architecture, OBU framing, tile partitioning, mode information, segmentation, delta quantization, transform-size selection, coefficient decoding, inverse quantization, inverse transforms, complete intra prediction, intra-block copy, official extreme-displacement intra-block-copy conformance, the 12-profile reconstruction matrix, corrected twelve-bit inverse-transform SIMD arithmetic, layered-item properties, layered reference/header/CDF/motion-field state, inter-frame intra blocks, SIMD-first translational prediction, complete single-reference inter reconstruction, compound reference trees and modes, paired reference-MV derivation, reference-dependent bounded sequence decoding, allocation-free SIMD-first equal averaging, selected inter-intra prediction, selectable compound blending, OBMC, scaled-reference reconstruction, local warped prediction, non-translational global prediction, official motion-vector conformance, official spatial- and temporal-layer default-operating-point conformance, official active-film-grain and monochrome sequence conformance, selected spatial-layer presentation, progressive color and auxiliary-alpha conformance, pinned-HM HEVC CABAC verification, pinned-HM HEVC coding-tree traversal verification, official HEVC intra-prediction verification, official HEVC residual-reconstruction verification, HEVC parallel slice decoding, bounded HEVC supplemental presentation and metadata handling, and the complete independently coded HEVC Range Extensions native-plane profile/tool matrix. This dashboard is the authoritative delivery order. The detailed phase checklists below provide subsystem evidence; they do not override the current-stage marker or permit work to skip ahead.
Commit `a0d1b5af56acb3a3a9aebb29c916d1b36cd49367` restores the established JPEG color-converter operator architecture throughout the HEIF implementation. `Av1ForwardTransformer` owns its nested one-dimensional contract and all twelve concrete forward transform operators; `Av1Inverse2dTransformer` owns the corresponding inverse contract and operators; and `Av1InverseTransformer` owns the byte and high-bit-depth reconstruction-output operators. Each contract lives in the owner's `.Operator.cs`, while every semantic transform with a shared traversal has its own `<Owner>.<Semantic>Operator.cs` partial-family file and implements concrete scalar and SIMD overload bodies without a forwarding semantic core. The same owner-and-partial-file rule now covers HEVC inverse transforms and intra prediction, AV1 and HEVC deblocking, AV1 CDEF, and HEVC transform-skip reconstruction. Vector-width and sample-storage adapters that are not semantic dispatch operators are named `Operations`; no operator type is named after a hardware width, no static contract uses CRTP, no source file groups multiple concrete semantic operator structs, and no semantic operator remains top-level. Roslynk reports zero compiler errors, both Release source targets build with zero warnings and errors, and 736 focused `net10.0` transform, loop-filter, prediction, reconstruction, transfer-function, and film-grain cases pass without failures or skips through the existing `FeatureTestRunner` coverage. All 56 new C# paths resolve to the existing `csharp` diff driver without an attributes change, and `git diff --check` is clean. Commit `a0d1b5af56acb3a3a9aebb29c916d1b36cd49367` restores the established JPEG color-converter operator architecture throughout the HEIF implementation. `Av1ForwardTransformer` owns its nested one-dimensional contract and all twelve concrete forward transform operators; `Av1Inverse2dTransformer` owns the corresponding inverse contract and operators; and `Av1InverseTransformer` owns the byte and high-bit-depth reconstruction-output operators. Each contract lives in the owner's `.Operator.cs`, while every semantic transform with a shared traversal has its own `<Owner>.<Semantic>Operator.cs` partial-family file and implements concrete scalar and SIMD overload bodies without a forwarding semantic core. The same owner-and-partial-file rule now covers HEVC inverse transforms and intra prediction, AV1 and HEVC deblocking, AV1 CDEF, and HEVC transform-skip reconstruction. Vector-width and sample-storage adapters that are not semantic dispatch operators are named `Operations`; no operator type is named after a hardware width, no static contract uses CRTP, no source file groups multiple concrete semantic operator structs, and no semantic operator remains top-level. Roslynk reports zero compiler errors, both Release source targets build with zero warnings and errors, and 736 focused `net10.0` transform, loop-filter, prediction, reconstruction, transfer-function, and film-grain cases pass without failures or skips through the existing `FeatureTestRunner` coverage. All 56 new C# paths resolve to the existing `csharp` diff driver without an attributes change, and `git diff --check` is clean.
@ -83,16 +83,16 @@ Status meanings:
- **Not started:** supporting primitives may exist, but the production format path is absent. - **Not started:** supporting primitives may exist, but the production format path is absent.
- **Current:** the only work item that should be advanced before taking the next queued item. - **Current:** the only work item that should be advanced before taking the next queued item.
Current development stage: **Stage 4 — complete HEVC still-image decoding.** The AV1 still-image exit gate is complete across the exact 12-profile matrix, layered reference-dependent items, every connected normative reconstruction tool, official quantizer and size boundaries, constrained allocation, and pinned native and presentation output. The bounded HEVC item-local syntax, allocator-backed decoder lifecycle, CABAC arithmetic and context state, coding-tree traversal, intra prediction, residual reconstruction, in-loop filtering, tiles, wavefront entry points, dependent slices, entry-point offset handling, supplemental presentation and metadata, and independently coded Range Extensions native-plane profile/tool matrix are implemented and verified through pinned-HM tables and analysis, official native-plane output, real HEIC payloads, constrained allocation, constructor failure, public presentation cases, and SIMD/scalar feature isolation. The next dependency is independently encoded HEIC container-presentation evidence across every exposed profile and required Range Extensions tool. Neither AV1 nor HEVC production encoding is implemented. Current development stage: **Stage 4 — complete decoded presentation and animation.** The AV1 and independently coded HEVC still-image exit gates are complete across their exact profile, tool, constrained-allocation, SIMD-fallback, native-plane, and presentation matrices. The next dependency is the remaining shared color, ICC, alpha, grid, transform, metadata, and reference-dependent bounded-sequence decode matrix. Neither AV1 nor HEVC production encoding is implemented.
Immediate checkpoint: **current — close the HEVC container-presentation matrix.** Retain or independently produce genuine HEIC files across every exposed profile and required Range Extensions tool, preserve the completed exact native-plane oracle, and compare final ImageSharp presentation with an independent HEIF decoder before closing the Phase 4 decoder exit gate. Immediate checkpoint: **current — close shared decoded presentation and bounded animation.** Complete the remaining color-signaling and ICC matrix, alpha, grid, transform, metadata, HEVC sequence, and reference-dependent AV1/HEVC presentation gates before starting either payload encoder.
| Order | Delivery stage | State | Delivered state | Gate that remains open | | Order | Delivery stage | State | Delivered state | Gate that remains open |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| 1 | Baseline, provenance, documentation, and public contract | In progress | Pinned codec references, a bounded image-only scope, encoder options, typed bit depth, decoder-option propagation, and extensive HEIF documentation exist. | Complete the all-file documentation audit, record a fresh Release baseline, finish distinct public HEIC/AVIF save boundaries, and close API review. | | 1 | Baseline, provenance, documentation, and public contract | In progress | Pinned codec references, a bounded image-only scope, encoder options, typed bit depth, decoder-option propagation, and extensive HEIF documentation exist. | Complete the all-file documentation audit, record a fresh Release baseline, finish distinct public HEIC/AVIF save boundaries, and close API review. |
| 2 | Bounded HEIF item and image-sequence container | In progress | Still-item parsing, grids, auxiliary alpha, metadata properties, bounded image-sequence tracks, Identify, and all-sync AV1 sequence presentation are connected. | Complete adversarial boundary coverage, remaining item/property behavior, reference-dependent sequence reconstruction, and the bounded sequence writer. | | 2 | Bounded HEIF item and image-sequence container | In progress | Still-item parsing, grids, auxiliary alpha, metadata properties, bounded image-sequence tracks, Identify, and all-sync AV1 sequence presentation are connected. | Complete adversarial boundary coverage, remaining item/property behavior, reference-dependent sequence reconstruction, and the bounded sequence writer. |
| 3 | Still-image AV1 and HEVC decoding | **Current** | AV1 still-image decoding has passed its complete exact native/presentation profile, layered-item, normative-tool, official-sequence, constrained-allocation, and SIMD-fallback exit gate. HEVC item-local syntax, lifecycle, CABAC, coding-tree traversal, intra prediction, residual reconstruction, in-loop filtering, tiles, wavefront entry points, dependent slices, entry-point offsets, bounded supplemental presentation and metadata, and the independently coded Range Extensions native-plane profile/tool matrix pass the recorded pinned-HM, official-plane, constrained-allocation, and scalar/SIMD evidence. | Close the remaining genuine-HEIC presentation matrix across every exposed profile and required Range Extensions tool. | | 3 | Still-image AV1 and HEVC decoding | Complete | AV1 still-image decoding has passed its complete exact native/presentation profile, layered-item, normative-tool, official-sequence, constrained-allocation, and SIMD-fallback exit gate. HEVC independently coded still-image decoding passes the complete Range Extensions profile/tool matrix with exact pinned-HM native planes, exact pinned presentation, constrained allocation, and scalar/SIMD evidence. | None for bounded still-image decoding. |
| 4 | Complete decoded presentation and animation | In progress | Shared SIMD-first AV1/HEVC color conversion, ICC application, grids, transforms, direct planar alpha composition, frame metadata, repetition, and independently decodable AV1 sequence samples exist. | Close the full color/ICC cross-product, HEVC sequence decoding, AV1/HEVC reference-dependent samples, frame-local metadata/alpha behavior, and independent animated decode vectors. | | 4 | Complete decoded presentation and animation | **Current** | Shared SIMD-first AV1/HEVC color conversion, ICC application, grids, transforms, direct planar alpha composition, frame metadata, repetition, and independently decodable AV1 sequence samples exist. | Close the full color/ICC cross-product, HEVC sequence decoding, AV1/HEVC reference-dependent samples, frame-local metadata/alpha behavior, and independent animated decode vectors. |
| 5 | AV1/AVIF encoding | Not started | RGB-to-planar conversion, forward transforms, OBU writer foundations, options, and container-writing infrastructure exist. | `HeifEncoderCore` still rejects AV1. Implement a real independently decodable lossy/lossless AV1 payload and the complete AVIF item/metadata matrix. | | 5 | AV1/AVIF encoding | Not started | RGB-to-planar conversion, forward transforms, OBU writer foundations, options, and container-writing infrastructure exist. | `HeifEncoderCore` still rejects AV1. Implement a real independently decodable lossy/lossless AV1 payload and the complete AVIF item/metadata matrix. |
| 6 | HEVC/HEIC encoding | Not started | Shared input color conversion, options, and HEIF writer infrastructure exist. | `HeifEncoderCore` still rejects HEVC. Implement a real independently decodable lossy/lossless HEVC payload and the complete HEIC item/metadata matrix. | | 6 | HEVC/HEIC encoding | Not started | Shared input color conversion, options, and HEIF writer infrastructure exist. | `HeifEncoderCore` still rejects HEVC. Implement a real independently decodable lossy/lossless HEVC payload and the complete HEIC item/metadata matrix. |
| 7 | Animated AVIF and HEIC encoding | Not started | Image-level repetition and per-frame duration contracts exist; bounded sequence parsing defines the required container surface. | Write the bounded sequence container and encode frame dependencies, timing, repetition, alpha, presentation, color, and metadata through both completed payload encoders. | | 7 | Animated AVIF and HEIC encoding | Not started | Image-level repetition and per-frame duration contracts exist; bounded sequence parsing defines the required container surface. | Write the bounded sequence container and encode frame dependencies, timing, repetition, alpha, presentation, color, and metadata through both completed payload encoders. |
@ -151,8 +151,8 @@ Immediate checkpoint: **current — close the HEVC container-presentation matrix
- [x] Verify HEVC CABAC arithmetic and every required context transition against pinned HM tables and complete production slices. - [x] Verify HEVC CABAC arithmetic and every required context transition against pinned HM tables and complete production slices.
- [x] Complete coding-tree, coding-unit, prediction-unit, and transform-unit traversal across every permitted size and partition mode. Eight official ITU first pictures compare exactly with pinned-HM native planes, pinned HM matches every archive complete-sequence MD5 and every RQT decoded-picture hash SEI, analyser output proves the recorded coding and transform sizes and partition alternatives, and all 150 focused HEVC decoder cases pass in Release. Commit `66d1f3c41b0baeaa98726b57d5feaeba50874f16` records the checkpoint. - [x] Complete coding-tree, coding-unit, prediction-unit, and transform-unit traversal across every permitted size and partition mode. Eight official ITU first pictures compare exactly with pinned-HM native planes, pinned HM matches every archive complete-sequence MD5 and every RQT decoded-picture hash SEI, analyser output proves the recorded coding and transform sizes and partition alternatives, and all 150 focused HEVC decoder cases pass in Release. Commit `66d1f3c41b0baeaa98726b57d5feaeba50874f16` records the checkpoint.
- [x] Complete HEVC intra prediction for every luma and chroma mode, including strong intra smoothing and constrained prediction rules. The official `IPRED_B_Nokia_3` native output and independently coded first `CIP_A_Panasonic_3` picture match pinned HM, all 149 focused HEVC decoder cases pass in Release, and commit `9bc5b9842` records the checkpoint. - [x] Complete HEVC intra prediction for every luma and chroma mode, including strong intra smoothing and constrained prediction rules. The official `IPRED_B_Nokia_3` native output and independently coded first `CIP_A_Panasonic_3` picture match pinned HM, all 149 focused HEVC decoder cases pass in Release, and commit `9bc5b9842` records the checkpoint.
- [ ] **Current:** complete the genuine-HEIC presentation matrix across every exposed HEVC profile and required Range Extensions tool, then close Phase 4 still-image decoding. - [x] Correct and reverify the genuine-HEIC presentation matrix across every exposed HEVC profile and required Range Extensions tool, then close Phase 4 still-image decoding.
- [ ] Close shared decoded presentation, ICC, alpha, grid, transform, metadata, and animated AV1/HEVC decode gates. - [ ] **Current:** close shared decoded presentation, ICC, alpha, grid, transform, metadata, and animated AV1/HEVC decode gates.
- [ ] Implement and independently verify real AV1/AVIF still encoding. - [ ] Implement and independently verify real AV1/AVIF still encoding.
- [ ] Implement and independently verify real HEVC/HEIC still encoding. - [ ] Implement and independently verify real HEVC/HEIC still encoding.
- [ ] Implement and independently verify animated AVIF and HEIC encoding through the bounded image-sequence surface. - [ ] Implement and independently verify animated AVIF and HEIC encoding through the bounded image-sequence surface.
@ -162,7 +162,7 @@ Performance, allocation, documentation, and independent test work are part of ea
## Detailed implementation ledger ## Detailed implementation ledger
- [ ] **Implemented foundation; conformance gate open:** complete HEVC still-picture reconstruction and connect its native planes to the verified shared SIMD-first HEIF color pipeline. - [ ] **Implemented and profile/tool presentation verified; complete color-signaling conformance remains:** complete HEVC still-picture reconstruction and connect its native planes to the verified shared SIMD-first HEIF color pipeline.
- [x] Correct AV1 clipped-edge partition entropy handling and verify every block-mode cell from a real libavif AVIF against libaom 3.14.1. - [x] Correct AV1 clipped-edge partition entropy handling and verify every block-mode cell from a real libavif AVIF against libaom 3.14.1.
- [x] Preserve the exact ICC payload from an independently encoded AVIF primary item. - [x] Preserve the exact ICC payload from an independently encoded AVIF primary item.
- [x] Prove that a genuine non-sRGB AVIF profile changes decoded pixels and matches the independently converted source image within the documented AV1 tolerance. - [x] Prove that a genuine non-sRGB AVIF profile changes decoded pixels and matches the independently converted source image within the documented AV1 tolerance.
@ -175,6 +175,7 @@ Performance, allocation, documentation, and independent test work are part of ea
- [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] 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] 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] Drive AV1 and HEVC through the same `IHeifPlanarSampleBuffer<TSample>` and `HeifPlanarColorConverter` architecture. Codec namespaces now adapt only native rows, sample precision, subsampling, crop windows, chroma positions, and codec color signaling; pooled row scheduling, chroma reconstruction/downsampling, fixed-point specialization, SIMD traversal, scalar tails, and RGB packing are selected by the shared HEIF driver. - [x] Drive AV1 and HEVC through the same `IHeifPlanarSampleBuffer<TSample>` and `HeifPlanarColorConverter` architecture. Codec namespaces now adapt only native rows, sample precision, subsampling, crop windows, chroma positions, and codec color signaling; pooled row scheduling, chroma reconstruction/downsampling, fixed-point specialization, SIMD traversal, scalar tails, and RGB packing are selected by the shared HEIF driver.
- [x] Remove redundant `internal` member visibility from internal HEIF types. Existing assembly-visible methods, properties, accessors, and the cross-type constructor now use public member visibility, while type-local private members and JPEG-style internal nested operator contracts remain unchanged. Both Release source builds complete with zero warnings and zero errors, Roslynk reports zero compiler errors, and all 266 focused HEVC cases pass. Commit `6e906fed4` records the correction.
- [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 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 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] Match HM's channel-specific smoothing rules: combined 4:4:4 chroma uses the normal three-tap filter when selected, while strong bilinear smoothing remains restricted to luma syntax. Exact official RExt reconstruction passes at 8, 10, and 12 bits. - [x] Match HM's channel-specific smoothing rules: combined 4:4:4 chroma uses the normal three-tap filter when selected, while strong bilinear smoothing remains restricted to luma syntax. Exact official RExt reconstruction passes at 8, 10, and 12 bits.
@ -186,6 +187,7 @@ Performance, allocation, documentation, and independent test work are part of ea
- [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. - [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.
- [x] Add permanent official HEVC 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 fixtures with exact native-plane references and published per-plane digests. All 10 first pictures from the Sony GENERAL RExt conformance streams match every Y, Cb, and Cr sample with normal SIMD dispatch and with all hardware intrinsics disabled. - [x] Add permanent official HEVC 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 fixtures with exact native-plane references and published per-plane digests. All 10 first pictures from the Sony GENERAL RExt conformance streams match every Y, Cb, and Cr sample with normal SIMD dispatch and with all hardware intrinsics disabled.
- [x] Extend the permanent HEVC matrix to every exposed profile and each Range Extensions tool not individually isolated by the Sony GENERAL streams, with exact native-plane references from HM or another independent decoder. Commit `9d7348efd6496b54cef7ec27e42352641439c400` records 25 additional official-picture cases, exact profile/tool signaling, exact native-plane hashes, the PCM precision and chroma-deblocking corrections, both zero-warning Release source builds, all 220 focused HEVC cases, verified existing LFS attributes and fixture hashes, zero Roslynk compiler errors, and clean `git diff --check`. - [x] Extend the permanent HEVC matrix to every exposed profile and each Range Extensions tool not individually isolated by the Sony GENERAL streams, with exact native-plane references from HM or another independent decoder. Commit `9d7348efd6496b54cef7ec27e42352641439c400` records 25 additional official-picture cases, exact profile/tool signaling, exact native-plane hashes, the PCM precision and chroma-deblocking corrections, both zero-warning Release source builds, all 220 focused HEVC cases, verified existing LFS attributes and fixture hashes, zero Roslynk compiler errors, and clean `git diff --check`.
- [x] Retain 35 real HEIC containers and verify the complete independently coded profile/tool presentation matrix. Exact native-plane reconstruction remains pinned to HM; direct pinned-libheif presentation is used only where its HEVC backend reproduces those planes. Rejected or incorrectly reconstructed libheif profiles use pinned HM planes passed through an independent reproduction of libheif 1.23.1's float32 H.273 conversion, including nearest chroma traversal and source-precision clipping. FFmpeg raw output is accepted only as a byte-exact HM cross-check and its PNG conversion is not a reference. High-bit-depth conversion now retains source precision through `Rgba64` instead of an eight-bit intermediate. The exact 25-case native matrix, 29 direct presentation cases, and three `FeatureTestRunner` presentation cases pass; the latter cover 8-bit subsampled, 12-bit full-resolution, and 12-bit `Rgba64` output across the available SIMD widths and scalar fallback. Five constrained tracked-allocator cases retain balanced exactly-once returns. Both `net10.0` and `net11.0` Release source builds pass with zero warnings and errors. Commit `640b64ee5` records the original fixture matrix and the now-superseded weak comparison; the current local correction replaces that tolerance with exact pinned presentation evidence.
- [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. - [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. - [ ] 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. - [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.
@ -432,16 +434,16 @@ This assessment was reconciled with the source tree on 2026-08-30, including pro
- 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. - 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. - 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.
- Pixel aspect ratio now preserves the complete unsigned spacing pair, affects Decode and Identify through ImageSharp's existing unitless resolution metadata, and follows quarter-turn presentation rotation. Independent grid, rotation, and maximum-spacing fixtures remain required. - Pixel aspect ratio now preserves the complete unsigned spacing pair, affects Decode and Identify through ImageSharp's existing unitless resolution metadata, and follows quarter-turn presentation rotation. Independent grid, rotation, and maximum-spacing fixtures remain required.
- AV1 codec configuration is now retained per `av01` image item rather than in decoder-global state. Decode requires the property, validates its fixed record against the item's sequence header and any associated `pixi` channel depths, and requires matching configurations across grid tiles. Identify now reports the configuration's 8/10/12-bit precision and monochrome shape. The optional `configOBUs` sequence is bounded and validated, including its mandatory size fields, first-position/at-most-one sequence-header rule, the image item's exactly-one sequence-header rule, and exact comparison of a repeated configuration header with the item header. Content light-level and mastering-display color-volume information are decoded from the bounded `clli` and `mdcv` image properties and matching AV1 HDR metadata OBUs, with the representations' distinct fixed-point precision, grid/property precedence, and `SkipMetadata` behavior preserved. The still-image `cclv`, `amve`, `reve`, and `ndwt` properties preserve content-volume, ambient-viewing, mastering-environment, and diffuse-white descriptions without importing their video-SEI or sample-entry state models. Independent fixtures and encoder preservation remain required. HEVC `hvcC` is associated with each `hvc1` image item, structurally validated through its VPS/SPS/PPS arrays, and used to report encoded precision and monochrome shape. The bounded decoder reconstructs exact native planes for the complete independently coded 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 Range Extensions profile/tool matrix; genuine HEIC container-presentation evidence across that complete matrix remains open. - AV1 codec configuration is now retained per `av01` image item rather than in decoder-global state. Decode requires the property, validates its fixed record against the item's sequence header and any associated `pixi` channel depths, and requires matching configurations across grid tiles. Identify now reports the configuration's 8/10/12-bit precision and monochrome shape. The optional `configOBUs` sequence is bounded and validated, including its mandatory size fields, first-position/at-most-one sequence-header rule, the image item's exactly-one sequence-header rule, and exact comparison of a repeated configuration header with the item header. Content light-level and mastering-display color-volume information are decoded from the bounded `clli` and `mdcv` image properties and matching AV1 HDR metadata OBUs, with the representations' distinct fixed-point precision, grid/property precedence, and `SkipMetadata` behavior preserved. The still-image `cclv`, `amve`, `reve`, and `ndwt` properties preserve content-volume, ambient-viewing, mastering-environment, and diffuse-white descriptions without importing their video-SEI or sample-entry state models. Independent fixtures and encoder preservation remain required. HEVC `hvcC` is associated with each `hvc1` image item, structurally validated through its VPS/SPS/PPS arrays, and used to report encoded precision and monochrome shape. The bounded decoder reconstructs exact native planes and exact genuine-HEIC presentation for the complete independently coded 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 Range Extensions profile/tool matrix.
- Several image-item properties and relationships remain missing or parsed without fully affecting output. - Several image-item properties and relationships remain missing or parsed without fully affecting output.
- Identify and decode now use the same bounded metadata parser and both validate the complete leading file type box. The parsed state is still mutable and Identify does not yet report the complete bit depth, color, profile, or transform model. - Identify and decode now use the same bounded metadata parser and both validate the complete leading file type box. The parsed state is still mutable and Identify does not yet report the complete bit depth, color, profile, or transform model.
### HEVC decoder and encoder ### 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. - `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 match HM 18 native planes exactly, ten official Sony GENERAL pictures cover the 8/10/12-bit chroma-layout boundary, and 25 additional official pictures cover every exposed independently coded Range Extensions profile and required tool with exact native-plane hashes. Complete images covering grids, auxiliary alpha, ICC, 4:2:0, and 4:4:4 presentation also match independent libheif output exactly. Genuine HEIC presentation across the complete profile/tool matrix and the complete color matrix remain open. - 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, ten official Sony GENERAL pictures cover the 8/10/12-bit chroma-layout boundary, and 25 additional official pictures cover every exposed independently coded Range Extensions profile and required tool with exact native-plane hashes. Complete images covering grids, auxiliary alpha, ICC, and the complete independently coded Range Extensions profile/tool matrix match pinned presentation references exactly. The complete color-signaling matrix remains open.
- There is no HEVC encoder. `HeifEncoderCore` rejects `HeifCompressionMethod.Hevc`; the separately selected legacy JPEG path emits a generic HEIF/HIF-style JPEG image item and does not provide HEIC output. - There is no HEVC encoder. `HeifEncoderCore` rejects `HeifCompressionMethod.Hevc`; the separately selected legacy JPEG path emits a generic HEIF/HIF-style JPEG image item and does not provide HEIC output.
- Permanent HEVC conformance tests compare extracted item planes exactly with HM output or normative decoded-picture hashes and complete presented images exactly with pinned libheif/libde265 reference PNGs. Native-plane fixtures cover the complete independently coded exposed profile/tool matrix; genuine HEIC presentation fixtures do not yet cover that complete matrix. - Permanent HEVC conformance tests compare extracted item planes exactly with HM output or normative decoded-picture hashes and complete presented images exactly with pinned references. Direct libheif/libde265 output is retained where its native reconstruction is exact; other profile/tool cases use pinned HM planes and independently reproduced pinned-libheif conversion. Native-plane and genuine-HEIC presentation fixtures cover the complete independently coded exposed profile/tool matrix.
### AV1 decoder ### AV1 decoder
@ -481,7 +483,7 @@ This assessment was reconciled with the source tree on 2026-08-30, including pro
### Tests ### Tests
- HEVC coverage includes exact native-plane comparison with HM output or normative decoded-picture hashes, exact complete-image comparison with pinned libheif/libde265 references, ten official Sony GENERAL pictures across the 8/10/12-bit chroma-layout boundary, and 25 additional official Range Extensions pictures across every exposed independently coded profile and required tool. The remaining Phase 4 evidence is genuine HEIC presentation across that complete profile/tool matrix. - HEVC coverage includes exact native-plane comparison with HM output or normative decoded-picture hashes, exact complete-image comparison with pinned presentation references, ten official Sony GENERAL pictures across the 8/10/12-bit chroma-layout boundary, and 25 additional official Range Extensions pictures across every exposed independently coded profile and required tool. Genuine HEIC presentation covers that complete independently coded profile/tool matrix; dependent-picture decoding and encoding remain separate Phase 4 work.
- AV1 has focused bitstream, prediction, entropy, reconstruction, filter, film-grain, color, and transform coverage, plus real libavif inputs. A real two-layer libavif-derived OBU stream verifies the bounded frame lifecycle, retained-slot occupancy, resolved inter references, and `frame_size_with_refs` dimensions through a fake tile lifecycle. The corrected progressive color item then verifies its production single-reference inter branch against exact pinned-libaom native planes and pinned-libavif presentation. The pinned 19-frame YUV444 sequence separately reaches production equal-average compound reconstruction after every preceding reference sample and matches its final native and presented references exactly. A real palette stream truncated inside its tile entropy payload verifies libaom-equivalent overflow/trailing-bit rejection and decoder-session recovery. The committed equal-average checkpoint passes 119 direct focused cases plus three `FeatureTestRunner` cases, zero-warning `net10.0` and `net11.0` source builds, a zero-error test-project analyzer build with pre-existing repository warnings, allocator lifetime validation, and `git diff --check`. Four additional pinned sequences reach distance-weighted, both wedge signs, both difference-mask orientations, and smooth and wedge inter-intra reconstruction with exact final native and presented output. Commit `864b11ad9791d3254a38691246270c569001b5a1` records the selectable-compound checkpoint after both source targets, the test-project analyzer build, 25 direct focused cases, six `FeatureTestRunner` cases, constrained allocation, exactly-once tracked returns, and `git diff --check` passed. Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` adds exact traced OBMC sequence evidence, subsampled direct reconstruction, constrained allocation, both source builds, the analyzer build, 14 direct cases, the normal/scalar `FeatureTestRunner` comparison, and clean staged `git diff --check`. Commit `8f57c29b250b4291f034b31c1ea3de3fa3345467` adds a genuine size-changing layered AVIF, exact retained-base and final native planes, exact final presentation, active inter deblocking, constrained allocation, normal/scalar dispatch, direct 8/10/12-bit block reconstruction, and 87 passing focused cases. The local-warp checkpoint adds a genuine two-frame 256x256 bounded AVIF with many traced `WARPED_CAUSAL` blocks, exact pinned multi-sample projection, exact final native and presentation comparisons, constrained allocation, normal/scalar `FeatureTestRunner` dispatch, direct 8/10/12-bit predictor parity, zero-warning source builds, and 86 passing focused cases. Valid still-image syntax paths still contain explicit unsupported branches, so the independent AV1 decode matrix is not complete. - AV1 has focused bitstream, prediction, entropy, reconstruction, filter, film-grain, color, and transform coverage, plus real libavif inputs. A real two-layer libavif-derived OBU stream verifies the bounded frame lifecycle, retained-slot occupancy, resolved inter references, and `frame_size_with_refs` dimensions through a fake tile lifecycle. The corrected progressive color item then verifies its production single-reference inter branch against exact pinned-libaom native planes and pinned-libavif presentation. The pinned 19-frame YUV444 sequence separately reaches production equal-average compound reconstruction after every preceding reference sample and matches its final native and presented references exactly. A real palette stream truncated inside its tile entropy payload verifies libaom-equivalent overflow/trailing-bit rejection and decoder-session recovery. The committed equal-average checkpoint passes 119 direct focused cases plus three `FeatureTestRunner` cases, zero-warning `net10.0` and `net11.0` source builds, a zero-error test-project analyzer build with pre-existing repository warnings, allocator lifetime validation, and `git diff --check`. Four additional pinned sequences reach distance-weighted, both wedge signs, both difference-mask orientations, and smooth and wedge inter-intra reconstruction with exact final native and presented output. Commit `864b11ad9791d3254a38691246270c569001b5a1` records the selectable-compound checkpoint after both source targets, the test-project analyzer build, 25 direct focused cases, six `FeatureTestRunner` cases, constrained allocation, exactly-once tracked returns, and `git diff --check` passed. Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` adds exact traced OBMC sequence evidence, subsampled direct reconstruction, constrained allocation, both source builds, the analyzer build, 14 direct cases, the normal/scalar `FeatureTestRunner` comparison, and clean staged `git diff --check`. Commit `8f57c29b250b4291f034b31c1ea3de3fa3345467` adds a genuine size-changing layered AVIF, exact retained-base and final native planes, exact final presentation, active inter deblocking, constrained allocation, normal/scalar dispatch, direct 8/10/12-bit block reconstruction, and 87 passing focused cases. The local-warp checkpoint adds a genuine two-frame 256x256 bounded AVIF with many traced `WARPED_CAUSAL` blocks, exact pinned multi-sample projection, exact final native and presentation comparisons, constrained allocation, normal/scalar `FeatureTestRunner` dispatch, direct 8/10/12-bit predictor parity, zero-warning source builds, and 86 passing focused cases. Valid still-image syntax paths still contain explicit unsupported branches, so the independent AV1 decode matrix is not complete.
- Local-warp fixture provenance is pinned to libavif `062e582e8afda88e6baf988fdcf046a801efa0f5` and libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. A deterministic two-frame limited-range YUV444 grid sequence was encoded with `avifenc -j 1 -s 0 -q 60 -a color:enable-warped-motion=1 -a color:enable-global-motion=0 -a color:enable-obmc=0`. SHA-256 values are `990BAC4AD443005C217B0DA4FCCFA9ADFB3AA147AD06C85F9A655A4433E9E8A7` for the AVIF, `984B2815CEE0C05FDE26430F150A21B5C993141E25BA1ED4FDE09372DB64AC13` for the pinned-libaom frame-1 Y4M, and `4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1` for the pinned-libavif frame-1 PNG. - Local-warp fixture provenance is pinned to libavif `062e582e8afda88e6baf988fdcf046a801efa0f5` and libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. A deterministic two-frame limited-range YUV444 grid sequence was encoded with `avifenc -j 1 -s 0 -q 60 -a color:enable-warped-motion=1 -a color:enable-global-motion=0 -a color:enable-obmc=0`. SHA-256 values are `990BAC4AD443005C217B0DA4FCCFA9ADFB3AA147AD06C85F9A655A4433E9E8A7` for the AVIF, `984B2815CEE0C05FDE26430F150A21B5C993141E25BA1ED4FDE09372DB64AC13` for the pinned-libaom frame-1 Y4M, and `4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1` for the pinned-libavif frame-1 PNG.
- Scaled-reference fixture provenance is pinned to libavif `062e582e8afda88e6baf988fdcf046a801efa0f5` and libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. The source is libavif `tests/data/webp_logo_animated.y4m`, encoded as two layered YUV444 inputs with `avifenc -j 1 -q 90 --layered --scaling-mode:u 1/2 <input> --scaling-mode:u 1/1 <input> <output>`. The resulting item contains a 701-byte 40x40 base layer followed by a 1,494-byte 80x80 dependent layer. SHA-256 values are `EB239F31EC8DBF5E97AD6F52670FCA6497AE2A933822CFE724C75F66AAA2520B` for the AVIF, `B7E30E04A935414A517BAA2DF06AB756DA18BA7C291220D9D7C063297761AE82` for the 2,195-byte logical AV1 payload, `873DC1AB5623910FBF8053CBF684399F4F3BDDBA4110492E3E11DE86E01FEC1B` for the 4,800-byte pinned-libaom base YUV, `964F4D85A79FC10EEB3722E2C9DA3415083E1EB8C2611FE154946FA55E2171ED` for the pinned-libaom final Y4M, and `DC4C6DBE6BD92C5FCE1E3E23700AFA603EF04ED02EDD336213EBBA1E3BD84BA0` for the pinned-libavif final PNG. - Scaled-reference fixture provenance is pinned to libavif `062e582e8afda88e6baf988fdcf046a801efa0f5` and libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. The source is libavif `tests/data/webp_logo_animated.y4m`, encoded as two layered YUV444 inputs with `avifenc -j 1 -q 90 --layered --scaling-mode:u 1/2 <input> --scaling-mode:u 1/1 <input> <output>`. The resulting item contains a 701-byte 40x40 base layer followed by a 1,494-byte 80x80 dependent layer. SHA-256 values are `EB239F31EC8DBF5E97AD6F52670FCA6497AE2A933822CFE724C75F66AAA2520B` for the AVIF, `B7E30E04A935414A517BAA2DF06AB756DA18BA7C291220D9D7C063297761AE82` for the 2,195-byte logical AV1 payload, `873DC1AB5623910FBF8053CBF684399F4F3BDDBA4110492E3E11DE86E01FEC1B` for the 4,800-byte pinned-libaom base YUV, `964F4D85A79FC10EEB3722E2C9DA3415083E1EB8C2611FE154946FA55E2171ED` for the pinned-libaom final Y4M, and `DC4C6DBE6BD92C5FCE1E3E23700AFA603EF04ED02EDD336213EBBA1E3BD84BA0` for the pinned-libavif final PNG.
@ -901,8 +903,8 @@ The dashboard and immediate execution queue define the remaining critical path.
- [x] Complete progressive color and auxiliary-alpha verification through exact native and presentation comparisons, every required `FeatureTestRunner` fallback, and constrained tracked allocation. - [x] Complete progressive color and auxiliary-alpha verification through exact native and presentation comparisons, every required `FeatureTestRunner` fallback, and constrained tracked allocation.
- [x] Correct the audited 12-bit inverse ADST4, Identity4, and Identity16 arithmetic through exact pinned boundary vectors and SIMD/scalar feature isolation. - [x] Correct the audited 12-bit inverse ADST4, Identity4, and Identity16 arithmetic through exact pinned boundary vectors and SIMD/scalar feature isolation.
- [x] Remove every other unsupported valid AV1 still-image syntax path and prove the complete AVIF decode matrix with independent inputs and scalar/SIMD parity. Commit `2219edbc91edb6a2d905f1221a45207eb2719abc` closes Phase 3. - [x] Remove every other unsupported valid AV1 still-image syntax path and prove the complete AVIF decode matrix with independent inputs and scalar/SIMD parity. Commit `2219edbc91edb6a2d905f1221a45207eb2719abc` closes Phase 3.
- [ ] **Current:** close Phase 4 with genuine HEIC presentation evidence across every exposed profile and required Range Extensions tool; the native-plane matrix is complete. - [x] Close Phase 4 with genuine HEIC presentation evidence across every exposed profile and required Range Extensions tool while retaining the exact native-plane matrix.
- [ ] Close Phase 5 and the decode portion of the bounded sequence ledger: color, ICC, alpha, grids, presentation transforms, reference-dependent samples, and complete animated AVIF/HEIC decode. - [ ] **Current:** close Phase 5 and the decode portion of the bounded sequence ledger: color, ICC, alpha, grids, presentation transforms, reference-dependent samples, and complete animated AVIF/HEIC decode.
- [ ] Close the still-image portions of Phases 0, 1, and 2 that remain as release gates: documentation, provenance, public format boundaries, API review, parser hardening, and malformed-input coverage. - [ ] Close the still-image portions of Phases 0, 1, and 2 that remain as release gates: documentation, provenance, public format boundaries, API review, parser hardening, and malformed-input coverage.
- [ ] Complete Phase 6 with a real AV1 encoder and independent AVIF cross-decode across every public option and metadata combination. - [ ] Complete Phase 6 with a real AV1 encoder and independent AVIF cross-decode across every public option and metadata combination.
- [ ] Complete Phase 7 with a real HEVC encoder and independent HEIC cross-decode across every public option and metadata combination. - [ ] Complete Phase 7 with a real HEVC encoder and independent HEIC cross-decode across every public option and metadata combination.

4
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifPlanarColorConverter.cs

@ -48,7 +48,7 @@ internal static class HeifPlanarColorConverter
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort> where TBuffer : struct, IHeifPlanarSampleBuffer<ushort>
{ {
if (HeifYuv420ToRgb8Converter.IsSupported( if (HeifYuvToRgb8Converter.SupportsFixedPointConversion(
buffer.ChromaSubsamplingX, buffer.ChromaSubsamplingX,
buffer.ChromaSubsamplingY, buffer.ChromaSubsamplingY,
buffer.LumaBitDepth, buffer.LumaBitDepth,
@ -59,7 +59,7 @@ internal static class HeifPlanarColorConverter
{ {
// The fixed-point operator preserves the exact code-value rounding used by the verified eight-bit // The fixed-point operator preserves the exact code-value rounding used by the verified eight-bit
// presentation path. Selection belongs here so no codec can acquire a private color-conversion route. // presentation path. Selection belongs here so no codec can acquire a private color-conversion route.
HeifYuv420ToRgb8Converter.Convert(configuration, buffer, image, in parameters, sourceX, sourceY); HeifYuvToRgb8Converter.ConvertFixedPoint(configuration, buffer, image, in parameters, sourceX, sourceY);
return; return;
} }

75
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifSampleConversion.cs

@ -589,6 +589,81 @@ internal static class HeifSampleConversion
} }
} }
/// <summary>
/// Packs 16-bit RGB component rows into opaque 16-bit RGBA pixels.
/// </summary>
/// <param name="red">The red components.</param>
/// <param name="green">The green components.</param>
/// <param name="blue">The blue components.</param>
/// <param name="destination">The destination pixels.</param>
public static void PackRgba64(ReadOnlySpan<ushort> red, ReadOnlySpan<ushort> green, ReadOnlySpan<ushort> blue, Span<Rgba64> destination)
{
ref ushort redBase = ref MemoryMarshal.GetReference(red);
ref ushort greenBase = ref MemoryMarshal.GetReference(green);
ref ushort blueBase = ref MemoryMarshal.GetReference(blue);
ref Rgba64 destinationBase = ref MemoryMarshal.GetReference(destination);
int length = destination.Length;
int i = 0;
if (Vector512.IsHardwareAccelerated)
{
int oneVectorFromEnd = length - Vector512<int>.Count;
for (; i <= oneVectorFromEnd; i += Vector512<int>.Count)
{
(Vector256<uint> redLower, Vector256<uint> redUpper) = Vector256.Widen(Vector256.LoadUnsafe(ref Unsafe.Add(ref redBase, i)));
(Vector256<uint> greenLower, Vector256<uint> greenUpper) = Vector256.Widen(Vector256.LoadUnsafe(ref Unsafe.Add(ref greenBase, i)));
(Vector256<uint> blueLower, Vector256<uint> blueUpper) = Vector256.Widen(Vector256.LoadUnsafe(ref Unsafe.Add(ref blueBase, i)));
Vector512<int> r = Vector512.Create(redLower, redUpper).AsInt32();
Vector512<int> g = Vector512.Create(greenLower, greenUpper).AsInt32();
Vector512<int> b = Vector512.Create(blueLower, blueUpper).AsInt32();
StoreRgba64Batch(r.GetLower().GetLower(), g.GetLower().GetLower(), b.GetLower().GetLower(), ref Unsafe.Add(ref destinationBase, i));
StoreRgba64Batch(r.GetLower().GetUpper(), g.GetLower().GetUpper(), b.GetLower().GetUpper(), ref Unsafe.Add(ref destinationBase, i + 4));
StoreRgba64Batch(r.GetUpper().GetLower(), g.GetUpper().GetLower(), b.GetUpper().GetLower(), ref Unsafe.Add(ref destinationBase, i + 8));
StoreRgba64Batch(r.GetUpper().GetUpper(), g.GetUpper().GetUpper(), b.GetUpper().GetUpper(), ref Unsafe.Add(ref destinationBase, i + 12));
}
}
if (Vector256.IsHardwareAccelerated)
{
int oneVectorFromEnd = length - Vector256<int>.Count;
for (; i <= oneVectorFromEnd; i += Vector256<int>.Count)
{
Vector128<ushort> red16 = Vector128.LoadUnsafe(ref Unsafe.Add(ref redBase, i));
Vector128<ushort> green16 = Vector128.LoadUnsafe(ref Unsafe.Add(ref greenBase, i));
Vector128<ushort> blue16 = Vector128.LoadUnsafe(ref Unsafe.Add(ref blueBase, i));
Vector256<int> r = Vector256.Create(Vector128.WidenLower(red16), Vector128.WidenUpper(red16)).AsInt32();
Vector256<int> g = Vector256.Create(Vector128.WidenLower(green16), Vector128.WidenUpper(green16)).AsInt32();
Vector256<int> b = Vector256.Create(Vector128.WidenLower(blue16), Vector128.WidenUpper(blue16)).AsInt32();
StoreRgba64Batch(r.GetLower(), g.GetLower(), b.GetLower(), ref Unsafe.Add(ref destinationBase, i));
StoreRgba64Batch(r.GetUpper(), g.GetUpper(), b.GetUpper(), ref Unsafe.Add(ref destinationBase, i + 4));
}
}
if (Vector128.IsHardwareAccelerated)
{
int oneVectorFromEnd = length - Vector128<int>.Count;
for (; i <= oneVectorFromEnd; i += Vector128<int>.Count)
{
ulong packedRed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref redBase, i)));
ulong packedGreen = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref greenBase, i)));
ulong packedBlue = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref blueBase, i)));
Vector128<int> r = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packedRed).AsUInt16()).AsInt32();
Vector128<int> g = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packedGreen).AsUInt16()).AsInt32();
Vector128<int> b = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packedBlue).AsUInt16()).AsInt32();
StoreRgba64Batch(r, g, b, ref Unsafe.Add(ref destinationBase, i));
}
}
for (; i < length; i++)
{
Unsafe.Add(ref destinationBase, i) = new Rgba64(
Unsafe.Add(ref redBase, i),
Unsafe.Add(ref greenBase, i),
Unsafe.Add(ref blueBase, i),
ushort.MaxValue);
}
}
/// <summary> /// <summary>
/// Packs normalized RGB component rows into opaque 16-bit RGBA pixels. /// Packs normalized RGB component rows into opaque 16-bit RGBA pixels.
/// </summary> /// </summary>

261
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Parameters.cs

@ -1,261 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
/// </content>
internal static partial class HeifYuv420ToRgb8Converter
{
/// <summary>
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
/// </summary>
private readonly struct ConversionParameters
{
/// <summary>
/// The scalar fixed-point coefficients.
/// </summary>
public readonly FixedPointParameters Scalar;
/// <summary>
/// The four-lane SIMD coefficients.
/// </summary>
public readonly Vector128Parameters FourLane;
/// <summary>
/// The eight-lane SIMD coefficients.
/// </summary>
public readonly Vector256Parameters EightLane;
/// <summary>
/// The sixteen-lane SIMD coefficients.
/// </summary>
public readonly Vector512Parameters SixteenLane;
/// <summary>
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
/// </summary>
/// <param name="parameters">The shared floating-point conversion parameters.</param>
public ConversionParameters(in HeifColorConversionParameters parameters)
{
FixedPointParameters scalar = new(in parameters);
this.Scalar = scalar;
this.FourLane = new(in scalar);
this.EightLane = new(in scalar);
this.SixteenLane = new(in scalar);
}
}
/// <summary>
/// Stores the scalar fixed-point coefficients resolved for one image.
/// </summary>
private readonly struct FixedPointParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="FixedPointParameters"/> struct.
/// </summary>
/// <param name="parameters">The shared floating-point conversion parameters.</param>
public FixedPointParameters(in HeifColorConversionParameters parameters)
{
float scale = 1 << CoefficientShift;
// Rounding each image-invariant coefficient once gives the integer kernel eight fractional bits.
// The signed green coefficients retain the exact addition and rounding order used by every SIMD lane.
this.RedCr = (int)MathF.Round(parameters.RedChromaScale * scale, MidpointRounding.AwayFromZero);
this.GreenCb = -(int)MathF.Round(parameters.GreenBlueChromaScale * scale, MidpointRounding.AwayFromZero);
this.GreenCr = -(int)MathF.Round(parameters.GreenRedChromaScale * scale, MidpointRounding.AwayFromZero);
this.BlueCb = (int)MathF.Round(parameters.BlueChromaScale * scale, MidpointRounding.AwayFromZero);
}
/// <summary>
/// Gets the red contribution from centered Cr.
/// </summary>
public int RedCr { get; }
/// <summary>
/// Gets the green contribution from centered Cb.
/// </summary>
public int GreenCb { get; }
/// <summary>
/// Gets the green contribution from centered Cr.
/// </summary>
public int GreenCr { get; }
/// <summary>
/// Gets the blue contribution from centered Cb.
/// </summary>
public int BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for four-lane conversion.
/// </summary>
private readonly struct Vector128Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector128Parameters(in FixedPointParameters parameters)
{
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);
this.GreenCr = Vector128.Create(parameters.GreenCr);
this.BlueCb = Vector128.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector128<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector128<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector128<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector128<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector128<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector128<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector128<int> BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for eight-lane conversion.
/// </summary>
private readonly struct Vector256Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector256Parameters(in FixedPointParameters parameters)
{
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);
this.GreenCr = Vector256.Create(parameters.GreenCr);
this.BlueCb = Vector256.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector256<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector256<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector256<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector256<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector256<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector256<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector256<int> BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for sixteen-lane conversion.
/// </summary>
private readonly struct Vector512Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector512Parameters(in FixedPointParameters parameters)
{
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);
this.GreenCr = Vector512.Create(parameters.GreenCr);
this.BlueCb = Vector512.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector512<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector512<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector512<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector512<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector512<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector512<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector512<int> BlueCb { get; }
}
}

105
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.cs

@ -1,105 +0,0 @@
// 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.Components;
/// <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="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="buffer">The reconstructed component-plane buffer.</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, TBuffer>(
Configuration configuration,
TBuffer buffer,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort>
{
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 buffer 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 = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width);
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(lumaY >> 1).Slice(sourceX >> 1);
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(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);
}
}
}

151
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs

@ -0,0 +1,151 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides pinned-libheif high-bit-depth coefficient conversion at the source RGB precision.
/// </content>
internal static partial class HeifYuvToRgb16Converter
{
/// <summary>
/// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes.
/// </summary>
private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb16Operator
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in ConversionParameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
Vector512Parameters values = parameters.SixteenLane;
Vector512<float> luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector512<float> blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector512<float> redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector512<float> half = Vector512.Create(0.5F);
// libheif evaluates these as distinct float32 multiplies and adds; FMA changes some 12-bit results by one.
Vector512<float> redValue = Vector512.Multiply(values.RedCr, redDifference);
redValue = Vector512.Add(luma, redValue);
Vector512<float> greenValue = Vector512.Multiply(values.GreenCb, blueDifference);
greenValue = Vector512.Add(luma, greenValue);
Vector512<float> greenRedValue = Vector512.Multiply(values.GreenCr, redDifference);
greenValue = Vector512.Add(greenValue, greenRedValue);
Vector512<float> blueValue = Vector512.Multiply(values.BlueCb, blueDifference);
blueValue = Vector512.Add(luma, blueValue);
Vector512<int> red = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(redValue, half)));
Vector512<int> green = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(greenValue, half)));
Vector512<int> blue = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(blueValue, half)));
r = Vector512.Clamp(red, default, values.Maximum);
g = Vector512.Clamp(green, default, values.Maximum);
b = Vector512.Clamp(blue, default, values.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in ConversionParameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
Vector256Parameters values = parameters.EightLane;
Vector256<float> luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector256<float> blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector256<float> redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector256<float> half = Vector256.Create(0.5F);
Vector256<float> redValue = Vector256.Multiply(values.RedCr, redDifference);
redValue = Vector256.Add(luma, redValue);
Vector256<float> greenValue = Vector256.Multiply(values.GreenCb, blueDifference);
greenValue = Vector256.Add(luma, greenValue);
Vector256<float> greenRedValue = Vector256.Multiply(values.GreenCr, redDifference);
greenValue = Vector256.Add(greenValue, greenRedValue);
Vector256<float> blueValue = Vector256.Multiply(values.BlueCb, blueDifference);
blueValue = Vector256.Add(luma, blueValue);
Vector256<int> red = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(redValue, half)));
Vector256<int> green = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(greenValue, half)));
Vector256<int> blue = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(blueValue, half)));
r = Vector256.Clamp(red, default, values.Maximum);
g = Vector256.Clamp(green, default, values.Maximum);
b = Vector256.Clamp(blue, default, values.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in ConversionParameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
Vector128Parameters values = parameters.FourLane;
Vector128<float> luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector128<float> blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector128<float> redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector128<float> half = Vector128.Create(0.5F);
Vector128<float> redValue = Vector128.Multiply(values.RedCr, redDifference);
redValue = Vector128.Add(luma, redValue);
Vector128<float> greenValue = Vector128.Multiply(values.GreenCb, blueDifference);
greenValue = Vector128.Add(luma, greenValue);
Vector128<float> greenRedValue = Vector128.Multiply(values.GreenCr, redDifference);
greenValue = Vector128.Add(greenValue, greenRedValue);
Vector128<float> blueValue = Vector128.Multiply(values.BlueCb, blueDifference);
blueValue = Vector128.Add(luma, blueValue);
Vector128<int> red = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(redValue, half)));
Vector128<int> green = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(greenValue, half)));
Vector128<int> blue = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(blueValue, half)));
r = Vector128.Clamp(red, default, values.Maximum);
g = Vector128.Clamp(green, default, values.Maximum);
b = Vector128.Clamp(blue, default, values.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
ushort y,
ushort cb,
ushort cr,
in ConversionParameters parameters,
out int r,
out int g,
out int b)
{
ScalarParameters values = parameters.Scalar;
float luma = (y - values.LumaOffset) * values.LumaScale;
float blueDifference = (cb - values.ChromaMidpoint) * values.ChromaScale;
float redDifference = (cr - values.ChromaMidpoint) * values.ChromaScale;
// Keep each assignment separate so the JIT cannot fuse the reference float32 operations.
float redValue = values.RedCr * redDifference;
redValue = luma + redValue;
float greenValue = values.GreenCb * blueDifference;
greenValue = luma + greenValue;
float greenRedValue = values.GreenCr * redDifference;
greenValue += greenRedValue;
float blueValue = values.BlueCb * blueDifference;
blueValue = luma + blueValue;
r = Numerics.Clamp((int)(redValue + 0.5F), 0, values.Maximum);
g = Numerics.Clamp((int)(greenValue + 0.5F), 0, values.Maximum);
b = Numerics.Clamp((int)(blueValue + 0.5F), 0, values.Maximum);
}
}
}

83
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides pinned-libheif high-bit-depth monochrome presentation without luma-range expansion.
/// </content>
internal static partial class HeifYuvToRgb16Converter
{
/// <summary>
/// Copies source-precision luma into each source-precision RGB component.
/// </summary>
private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb16Operator
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in ConversionParameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
r = y;
g = y;
b = y;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in ConversionParameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
r = y;
g = y;
b = y;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in ConversionParameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
r = y;
g = y;
b = y;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
ushort y,
ushort cb,
ushort cr,
in ConversionParameters parameters,
out int r,
out int g,
out int b)
{
r = y;
g = y;
b = y;
}
}
}

293
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs

@ -0,0 +1,293 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Defines closed high-bit-depth color operators and nearest-sample row traversal.
/// </content>
internal static partial class HeifYuvToRgb16Converter
{
/// <summary>
/// Defines source-precision RGB arithmetic for scalar and SIMD lanes.
/// </summary>
private interface IHeifYuvToRgb16Operator
{
/// <summary>
/// Converts sixteen YCbCr samples to source-precision 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 image conversion 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 ConversionParameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b);
/// <summary>
/// Converts eight YCbCr samples to source-precision 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 image conversion 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 ConversionParameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b);
/// <summary>
/// Converts four YCbCr samples to source-precision 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 image conversion 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 ConversionParameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b);
/// <summary>
/// Converts one YCbCr sample to source-precision 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 image conversion 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 ConversionParameters parameters,
out int r,
out int g,
out int b);
}
/// <summary>
/// Converts one luma row and its nearest native chroma row to planar 16-bit RGB storage.
/// </summary>
/// <typeparam name="TOperator">The source-precision color arithmetic selected for the row.</typeparam>
/// <param name="luma">The full-resolution luma samples.</param>
/// <param name="chromaBlue">The native blue-difference samples.</param>
/// <param name="chromaRed">The native red-difference samples.</param>
/// <param name="red">The destination red samples.</param>
/// <param name="green">The destination green samples.</param>
/// <param name="blue">The destination blue samples.</param>
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
/// <param name="parameters">The image conversion parameters.</param>
private static void ConvertRow<TOperator>(
ReadOnlySpan<ushort> luma,
ReadOnlySpan<ushort> chromaBlue,
ReadOnlySpan<ushort> chromaRed,
Span<ushort> red,
Span<ushort> green,
Span<ushort> blue,
int subsamplingX,
in ConversionParameters parameters)
where TOperator : struct, IHeifYuvToRgb16Operator
{
ref ushort lumaBase = ref MemoryMarshal.GetReference(luma);
ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue);
ref ushort chromaRedBase = ref MemoryMarshal.GetReference(chromaRed);
ref ushort redBase = ref MemoryMarshal.GetReference(red);
ref ushort greenBase = ref MemoryMarshal.GetReference(green);
ref ushort blueBase = ref MemoryMarshal.GetReference(blue);
int outputLeftShift = parameters.Scalar.OutputLeftShift;
int x = 0;
// Each operator produces code values at the source precision. The traversal then left-aligns those values in
// UInt16 storage, matching libheif's high-bit-depth RGB output without discarding low source bits.
if (Vector512.IsHardwareAccelerated)
{
int oneVectorFromEnd = luma.Length - Vector512<int>.Count;
for (; x <= oneVectorFromEnd; x += Vector512<int>.Count)
{
Vector512<int> y = LoadVector512(ref Unsafe.Add(ref lumaBase, x));
Vector512<int> cb = subsamplingX == 0
? LoadVector512(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector512<int> cr = subsamplingX == 0
? LoadVector512(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.Convert(y, cb, cr, in parameters, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b);
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x));
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x));
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x));
}
}
if (Vector256.IsHardwareAccelerated)
{
int oneVectorFromEnd = luma.Length - Vector256<int>.Count;
for (; x <= oneVectorFromEnd; x += Vector256<int>.Count)
{
Vector256<int> y = LoadVector256(ref Unsafe.Add(ref lumaBase, x));
Vector256<int> cb = subsamplingX == 0
? LoadVector256(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector256<int> cr = subsamplingX == 0
? LoadVector256(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.Convert(y, cb, cr, in parameters, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b);
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x));
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x));
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x));
}
}
if (Vector128.IsHardwareAccelerated)
{
int oneVectorFromEnd = luma.Length - Vector128<int>.Count;
for (; x <= oneVectorFromEnd; x += Vector128<int>.Count)
{
Vector128<int> y = LoadVector128(ref Unsafe.Add(ref lumaBase, x));
Vector128<int> cb = subsamplingX == 0
? LoadVector128(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector128<int> cr = subsamplingX == 0
? LoadVector128(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.Convert(y, cb, cr, in parameters, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b);
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x));
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x));
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x));
}
}
for (; x < luma.Length; x++)
{
TOperator.Convert(
Unsafe.Add(ref lumaBase, x),
Unsafe.Add(ref chromaBlueBase, x >> subsamplingX),
Unsafe.Add(ref chromaRedBase, x >> subsamplingX),
in parameters,
out int r,
out int g,
out int b);
Unsafe.Add(ref redBase, x) = (ushort)(r << outputLeftShift);
Unsafe.Add(ref greenBase, x) = (ushort)(g << outputLeftShift);
Unsafe.Add(ref blueBase, x) = (ushort)(b << outputLeftShift);
}
}
/// <summary>
/// Loads sixteen native samples as signed 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native sample.</param>
/// <returns>The widened sample lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<int> LoadVector512(ref ushort source)
{
(Vector256<uint> lower, Vector256<uint> upper) = Vector256.Widen(Vector256.LoadUnsafe(ref source));
return Vector512.Create(lower, upper).AsInt32();
}
/// <summary>
/// Loads eight native samples as signed 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native sample.</param>
/// <returns>The widened sample lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<int> LoadVector256(ref ushort source)
{
Vector128<ushort> samples = Vector128.LoadUnsafe(ref source);
return Vector256.Create(Vector128.WidenLower(samples), Vector128.WidenUpper(samples)).AsInt32();
}
/// <summary>
/// Loads four native samples as signed 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native sample.</param>
/// <returns>The widened sample lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<int> LoadVector128(ref ushort source)
{
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref source));
return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32();
}
/// <summary>
/// Loads eight chroma samples and repeats each sample into two of sixteen 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native chroma sample.</param>
/// <returns>The horizontally replicated chroma lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<int> LoadRepeatedVector512(ref ushort source)
{
Vector128<ushort> samples = Vector128.LoadUnsafe(ref source);
Vector128<ushort> lower = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16();
Vector128<ushort> upper = Vector128_.UnpackHigh(samples.AsInt16(), samples.AsInt16()).AsUInt16();
(Vector256<uint> widenedLower, Vector256<uint> widenedUpper) = Vector256.Widen(Vector256.Create(lower, upper));
return Vector512.Create(widenedLower, widenedUpper).AsInt32();
}
/// <summary>
/// Loads four chroma samples and repeats each sample into two of eight 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native chroma sample.</param>
/// <returns>The horizontally replicated chroma lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<int> LoadRepeatedVector256(ref ushort source)
{
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref source));
Vector128<ushort> samples = Vector128.CreateScalarUnsafe(packed).AsUInt16();
Vector128<ushort> repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16();
return Vector256.Create(Vector128.WidenLower(repeated), Vector128.WidenUpper(repeated)).AsInt32();
}
/// <summary>
/// Loads two chroma samples and repeats each sample into two of four 32-bit SIMD lanes.
/// </summary>
/// <param name="source">The first native chroma sample.</param>
/// <returns>The horizontally replicated chroma lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<int> LoadRepeatedVector128(ref ushort source)
{
uint packed = Unsafe.ReadUnaligned<uint>(ref Unsafe.As<ushort, byte>(ref source));
Vector128<ushort> samples = Vector128.CreateScalarUnsafe(packed).AsUInt16();
Vector128<ushort> repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16();
return Vector128.WidenLower(repeated).AsInt32();
}
}

347
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs

@ -0,0 +1,347 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides scalar and SIMD parameter storage for high-bit-depth pinned-libheif conversion.
/// </content>
internal static partial class HeifYuvToRgb16Converter
{
/// <summary>
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
/// </summary>
private readonly struct ConversionParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
/// </summary>
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
/// <param name="bitDepth">The common source component precision.</param>
public ConversionParameters(in HeifColorConversionParameters parameters, int bitDepth)
{
ScalarParameters scalar = new(in parameters, bitDepth);
this.Scalar = scalar;
this.SixteenLane = new(in scalar);
this.EightLane = new(in scalar);
this.FourLane = new(in scalar);
}
/// <summary>
/// Gets the scalar conversion parameters.
/// </summary>
public ScalarParameters Scalar { get; }
/// <summary>
/// Gets the sixteen-lane conversion parameters.
/// </summary>
public Vector512Parameters SixteenLane { get; }
/// <summary>
/// Gets the eight-lane conversion parameters.
/// </summary>
public Vector256Parameters EightLane { get; }
/// <summary>
/// Gets the four-lane conversion parameters.
/// </summary>
public Vector128Parameters FourLane { get; }
}
/// <summary>
/// Stores the scalar arithmetic and output scaling used by pinned libheif.
/// </summary>
private readonly struct ScalarParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="ScalarParameters"/> struct.
/// </summary>
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
/// <param name="bitDepth">The common source component precision.</param>
public ScalarParameters(in HeifColorConversionParameters parameters, int bitDepth)
{
this.LumaOffset = parameters.IsFullRange ? 0F : parameters.LumaBias;
this.LumaScale = parameters.IsFullRange ? 1F : 1.1689F;
this.ChromaMidpoint = parameters.ChromaBias;
this.ChromaScale = parameters.IsFullRange ? 1F : 1.1429F;
if (parameters.MatrixCoefficients == CicpMatrixCoefficients.Unspecified)
{
// libheif falls back to these literal Rec.601 coefficients when no matrix is signaled. Deriving them
// from Kr and Kb produces different float32 values and can move high-bit-depth green by one code value.
this.RedCr = 1.402F;
this.GreenCb = -0.344136F;
this.GreenCr = -0.714136F;
this.BlueCb = 1.772F;
}
else
{
float kr = parameters.Kr;
float kb = parameters.Kb;
this.RedCr = 2F * (-kr + 1F);
this.GreenCb = 2F * kb * (-kb + 1F) / (kb + kr - 1F);
this.GreenCr = 2F * kr * (-kr + 1F) / (kb + kr - 1F);
this.BlueCb = 2F * (-kb + 1F);
}
this.Maximum = (1 << bitDepth) - 1;
this.OutputLeftShift = 16 - bitDepth;
}
/// <summary>
/// Gets the luma code-value offset removed before limited-range expansion.
/// </summary>
public float LumaOffset { get; }
/// <summary>
/// Gets the luma range-expansion factor.
/// </summary>
public float LumaScale { get; }
/// <summary>
/// Gets the neutral chroma code value.
/// </summary>
public float ChromaMidpoint { get; }
/// <summary>
/// Gets the chroma range-expansion factor.
/// </summary>
public float ChromaScale { get; }
/// <summary>
/// Gets the red contribution from Cr.
/// </summary>
public float RedCr { get; }
/// <summary>
/// Gets the green contribution from Cb.
/// </summary>
public float GreenCb { get; }
/// <summary>
/// Gets the green contribution from Cr.
/// </summary>
public float GreenCr { get; }
/// <summary>
/// Gets the blue contribution from Cb.
/// </summary>
public float BlueCb { get; }
/// <summary>
/// Gets the largest source-precision RGB code value.
/// </summary>
public int Maximum { get; }
/// <summary>
/// Gets the left shift mapping source-precision RGB into 16-bit pixel storage.
/// </summary>
public int OutputLeftShift { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for sixteen-lane conversion.
/// </summary>
private readonly struct Vector512Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public Vector512Parameters(in ScalarParameters parameters)
{
this.LumaOffset = Vector512.Create(parameters.LumaOffset);
this.LumaScale = Vector512.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector512.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector512.Create(parameters.ChromaScale);
this.RedCr = Vector512.Create(parameters.RedCr);
this.GreenCb = Vector512.Create(parameters.GreenCb);
this.GreenCr = Vector512.Create(parameters.GreenCr);
this.BlueCb = Vector512.Create(parameters.BlueCb);
this.Maximum = Vector512.Create(parameters.Maximum);
}
/// <summary>
/// Gets the luma offset lanes.
/// </summary>
public Vector512<float> LumaOffset { get; }
/// <summary>
/// Gets the luma scale lanes.
/// </summary>
public Vector512<float> LumaScale { get; }
/// <summary>
/// Gets the chroma-midpoint lanes.
/// </summary>
public Vector512<float> ChromaMidpoint { get; }
/// <summary>
/// Gets the chroma-scale lanes.
/// </summary>
public Vector512<float> ChromaScale { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector512<float> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector512<float> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector512<float> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector512<float> BlueCb { get; }
/// <summary>
/// Gets the maximum source-precision RGB lanes.
/// </summary>
public Vector512<int> Maximum { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for eight-lane conversion.
/// </summary>
private readonly struct Vector256Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public Vector256Parameters(in ScalarParameters parameters)
{
this.LumaOffset = Vector256.Create(parameters.LumaOffset);
this.LumaScale = Vector256.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector256.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector256.Create(parameters.ChromaScale);
this.RedCr = Vector256.Create(parameters.RedCr);
this.GreenCb = Vector256.Create(parameters.GreenCb);
this.GreenCr = Vector256.Create(parameters.GreenCr);
this.BlueCb = Vector256.Create(parameters.BlueCb);
this.Maximum = Vector256.Create(parameters.Maximum);
}
/// <summary>
/// Gets the luma offset lanes.
/// </summary>
public Vector256<float> LumaOffset { get; }
/// <summary>
/// Gets the luma scale lanes.
/// </summary>
public Vector256<float> LumaScale { get; }
/// <summary>
/// Gets the chroma-midpoint lanes.
/// </summary>
public Vector256<float> ChromaMidpoint { get; }
/// <summary>
/// Gets the chroma-scale lanes.
/// </summary>
public Vector256<float> ChromaScale { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector256<float> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector256<float> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector256<float> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector256<float> BlueCb { get; }
/// <summary>
/// Gets the maximum source-precision RGB lanes.
/// </summary>
public Vector256<int> Maximum { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for four-lane conversion.
/// </summary>
private readonly struct Vector128Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public Vector128Parameters(in ScalarParameters parameters)
{
this.LumaOffset = Vector128.Create(parameters.LumaOffset);
this.LumaScale = Vector128.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector128.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector128.Create(parameters.ChromaScale);
this.RedCr = Vector128.Create(parameters.RedCr);
this.GreenCb = Vector128.Create(parameters.GreenCb);
this.GreenCr = Vector128.Create(parameters.GreenCr);
this.BlueCb = Vector128.Create(parameters.BlueCb);
this.Maximum = Vector128.Create(parameters.Maximum);
}
/// <summary>
/// Gets the luma offset lanes.
/// </summary>
public Vector128<float> LumaOffset { get; }
/// <summary>
/// Gets the luma scale lanes.
/// </summary>
public Vector128<float> LumaScale { get; }
/// <summary>
/// Gets the chroma-midpoint lanes.
/// </summary>
public Vector128<float> ChromaMidpoint { get; }
/// <summary>
/// Gets the chroma-scale lanes.
/// </summary>
public Vector128<float> ChromaScale { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector128<float> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector128<float> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector128<float> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector128<float> BlueCb { get; }
/// <summary>
/// Gets the maximum source-precision RGB lanes.
/// </summary>
public Vector128<int> Maximum { get; }
}
}

114
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs

@ -0,0 +1,114 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <summary>
/// Converts high-bit-depth HEIF YUV planes to packed pixels through opaque 16-bit RGB.
/// </summary>
internal static partial class HeifYuvToRgb16Converter
{
/// <summary>
/// Determines whether the pinned libheif-compatible high-bit-depth conversion supports the supplied planes.
/// </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="isMonochrome">Whether the image contains only luma samples.</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 SupportsLibheifConversion(
int subsamplingX,
int subsamplingY,
int lumaBitDepth,
int chromaBitDepth,
bool isMonochrome,
HeifColorConversionMode mode)
=> (isMonochrome || (subsamplingX is 0 or 1 && subsamplingY is 0 or 1))
&& lumaBitDepth is > 8 and <= 16
&& (isMonochrome || chromaBitDepth == lumaBitDepth)
&& mode == HeifColorConversionMode.Coefficients;
/// <summary>
/// Converts supported high-bit-depth HEVC planes using pinned libheif arithmetic and nearest chroma sampling.
/// </summary>
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="buffer">The reconstructed component-plane buffer.</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, TBuffer>(
Configuration configuration,
TBuffer buffer,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort>
{
ConversionParameters conversionParameters = new(in parameters, buffer.LumaBitDepth);
// Three planar rows and one packed Rgba64 row share a single image-lifetime allocation. The latter occupies
// four UInt16 values per pixel, so the complete scratch requirement is seven samples per output pixel.
using IMemoryOwner<ushort> rowOwner = configuration.MemoryAllocator.Allocate<ushort>(image.Width * 7);
Span<ushort> storage = rowOwner.GetSpan();
Span<ushort> red = storage[..image.Width];
Span<ushort> green = storage.Slice(image.Width, image.Width);
Span<ushort> blue = storage.Slice(image.Width * 2, image.Width);
Span<Rgba64> packed = MemoryMarshal.Cast<ushort, Rgba64>(storage[(image.Width * 3)..]);
for (int y = 0; y < image.Height; y++)
{
int lumaY = sourceY + y;
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width);
if (buffer.IsMonochrome)
{
// Pinned libheif copies the reconstructed luma code value directly to RGB for monochrome images.
// Scaling to the 16-bit pixel domain happens after that copy, without limited-range expansion.
ConvertRow<LibheifMonochromeOperator>(
luma,
luma,
luma,
red,
green,
blue,
0,
in conversionParameters);
}
else
{
int subsamplingX = buffer.ChromaSubsamplingX;
int chromaY = lumaY >> buffer.ChromaSubsamplingY;
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX);
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(chromaY).Slice(sourceX >> subsamplingX);
// libheif's selected direct conversion addresses the native chroma sample at x >> subsamplingX.
// The HEIF crop boundary already keeps sourceX aligned to complete chroma samples.
ConvertRow<LibheifCoefficientOperator>(
luma,
chromaBlue,
chromaRed,
red,
green,
blue,
subsamplingX,
in conversionParameters);
}
HeifSampleConversion.PackRgba64(red, green, blue, packed);
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromRgba64(configuration, packed, destination);
}
}
}

104
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.CoefficientOperator.cs → src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.FixedPointCoefficientOperator.cs

@ -11,47 +11,30 @@ namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// chroma sample. Matrix coefficients use a common fixed-point scale; every component rounds once after its complete /// chroma sample. Matrix coefficients use a common fixed-point scale; every component rounds once after its complete
/// weighted sum, then clips before the enclosing row kernel narrows and packs the RGB result. /// weighted sum, then clips before the enclosing row kernel narrows and packs the RGB result.
/// </content> /// </content>
internal static partial class HeifYuv420ToRgb8Converter internal static partial class HeifYuvToRgb8Converter
{ {
/// <summary> /// <summary>
/// Implements full-range coefficient-based YCbCr conversion for scalar and SIMD lanes. /// Implements full-range coefficient-based YCbCr conversion for scalar and SIMD lanes.
/// </summary> /// </summary>
private readonly struct FixedPointCoefficientOperator : IHeifYuv420ToRgb8Operator private readonly struct FixedPointCoefficientOperator : IHeifYuvToRgb8Operator
{ {
/// <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/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert( public static void Convert(
Vector128<int> y, Vector512<int> y,
Vector128<int> cb, Vector512<int> cb,
Vector128<int> cr, Vector512<int> cr,
in Vector128Parameters parameters, in ConversionParameters parameters,
out Vector128<int> r, out Vector512<int> r,
out Vector128<int> g, out Vector512<int> g,
out Vector128<int> b) out Vector512<int> b)
{ {
cb -= parameters.ChromaMidpoint; Vector512Parameters fixedPoint = parameters.FixedPointSixteenLane;
cr -= parameters.ChromaMidpoint; cb -= fixedPoint.ChromaMidpoint;
r = Vector128.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); cr -= fixedPoint.ChromaMidpoint;
g = Vector128.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); r = Vector512.Clamp(y + (((fixedPoint.RedCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
b = Vector128.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); g = Vector512.Clamp(y + (((fixedPoint.GreenCb * cb) + (fixedPoint.GreenCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
b = Vector512.Clamp(y + (((fixedPoint.BlueCb * cb) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -60,34 +43,55 @@ internal static partial class HeifYuv420ToRgb8Converter
Vector256<int> y, Vector256<int> y,
Vector256<int> cb, Vector256<int> cb,
Vector256<int> cr, Vector256<int> cr,
in Vector256Parameters parameters, in ConversionParameters parameters,
out Vector256<int> r, out Vector256<int> r,
out Vector256<int> g, out Vector256<int> g,
out Vector256<int> b) out Vector256<int> b)
{ {
cb -= parameters.ChromaMidpoint; Vector256Parameters fixedPoint = parameters.FixedPointEightLane;
cr -= parameters.ChromaMidpoint; cb -= fixedPoint.ChromaMidpoint;
r = Vector256.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); cr -= fixedPoint.ChromaMidpoint;
g = Vector256.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); r = Vector256.Clamp(y + (((fixedPoint.RedCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
b = Vector256.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); g = Vector256.Clamp(y + (((fixedPoint.GreenCb * cb) + (fixedPoint.GreenCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
b = Vector256.Clamp(y + (((fixedPoint.BlueCb * cb) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
} }
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert( public static void Convert(
Vector512<int> y, Vector128<int> y,
Vector512<int> cb, Vector128<int> cb,
Vector512<int> cr, Vector128<int> cr,
in Vector512Parameters parameters, in ConversionParameters parameters,
out Vector512<int> r, out Vector128<int> r,
out Vector512<int> g, out Vector128<int> g,
out Vector512<int> b) out Vector128<int> b)
{
Vector128Parameters fixedPoint = parameters.FixedPointFourLane;
cb -= fixedPoint.ChromaMidpoint;
cr -= fixedPoint.ChromaMidpoint;
r = Vector128.Clamp(y + (((fixedPoint.RedCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
g = Vector128.Clamp(y + (((fixedPoint.GreenCb * cb) + (fixedPoint.GreenCr * cr) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
b = Vector128.Clamp(y + (((fixedPoint.BlueCb * cb) + fixedPoint.RoundingBias) >> CoefficientShift), default, fixedPoint.Maximum);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(ushort y, ushort cb, ushort cr, in ConversionParameters parameters, out byte r, out byte g, out byte b)
{ {
cb -= parameters.ChromaMidpoint; FixedPointParameters fixedPoint = parameters.FixedPointScalar;
cr -= parameters.ChromaMidpoint; int centeredBlue = cb - ChromaMidpoint;
r = Vector512.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), default, parameters.Maximum); int centeredRed = cr - ChromaMidpoint;
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); // 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 + (((fixedPoint.RedCr * centeredRed) + RoundingBias) >> CoefficientShift);
int green = y + (((fixedPoint.GreenCb * centeredBlue) + (fixedPoint.GreenCr * centeredRed) + RoundingBias) >> CoefficientShift);
int blue = y + (((fixedPoint.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);
} }
} }
} }

125
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs

@ -0,0 +1,125 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides the coefficient conversion executed by pinned libheif 1.23.1. Each SIMD lane carries one output pixel.
/// Range expansion and matrix arithmetic remain in single precision, RGB is rounded and clipped at the coded
/// precision, and the final integer shift reproduces libheif's separate high-bit-depth-to-eight-bit operation.
/// </content>
internal static partial class HeifYuvToRgb8Converter
{
/// <summary>
/// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes.
/// </summary>
private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb8Operator
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in ConversionParameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
LibheifVector512Parameters values = parameters.LibheifSixteenLane;
Vector512<float> luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector512<float> blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector512<float> redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector512<float> half = Vector512.Create(0.5F);
// Sixteen independent samples use the same float32 ordering as the narrower paths. The closed operator
// keeps this compatibility arithmetic outside the row dispatch while allowing an exact scalar fallback.
Vector512<int> red = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.RedCr * redDifference) + half));
Vector512<int> green = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half));
Vector512<int> blue = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.BlueCb * blueDifference) + half));
r = Vector512.Clamp(red, default, values.Maximum) >> values.OutputShift;
g = Vector512.Clamp(green, default, values.Maximum) >> values.OutputShift;
b = Vector512.Clamp(blue, default, values.Maximum) >> values.OutputShift;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in ConversionParameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
LibheifVector256Parameters values = parameters.LibheifEightLane;
Vector256<float> luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector256<float> blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector256<float> redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector256<float> half = Vector256.Create(0.5F);
// Eight YUV tuples remain planar across the YMM arithmetic. The expression association matches the
// pinned scalar source, including the two successive green additions before truncation.
Vector256<int> red = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.RedCr * redDifference) + half));
Vector256<int> green = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half));
Vector256<int> blue = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.BlueCb * blueDifference) + half));
r = Vector256.Clamp(red, default, values.Maximum) >> values.OutputShift;
g = Vector256.Clamp(green, default, values.Maximum) >> values.OutputShift;
b = Vector256.Clamp(blue, default, values.Maximum) >> values.OutputShift;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in ConversionParameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
LibheifVector128Parameters values = parameters.LibheifFourLane;
Vector128<float> luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale;
Vector128<float> blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale;
Vector128<float> redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale;
Vector128<float> half = Vector128.Create(0.5F);
// Truncate after the explicit half-unit bias to mirror C++ float-to-int conversion. Clipping in integer
// lanes then preserves the source-precision boundary before the common eight-bit reduction shift.
Vector128<int> red = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.RedCr * redDifference) + half));
Vector128<int> green = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half));
Vector128<int> blue = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.BlueCb * blueDifference) + half));
r = Vector128.Clamp(red, default, values.Maximum) >> values.OutputShift;
g = Vector128.Clamp(green, default, values.Maximum) >> values.OutputShift;
b = Vector128.Clamp(blue, default, values.Maximum) >> values.OutputShift;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(ushort y, ushort cb, ushort cr, in ConversionParameters parameters, out byte r, out byte g, out byte b)
{
LibheifParameters values = parameters.LibheifScalar;
float luma = (y - values.LumaOffset) * values.LumaScale;
float blueDifference = (cb - values.ChromaMidpoint) * values.ChromaScale;
float redDifference = (cr - values.ChromaMidpoint) * values.ChromaScale;
// libheif's clip_f_u16 adds one half, truncates toward zero, and then clips. RGB is rounded before
// the high-bit-depth plane is reduced, so moving the shift into the floating-point scale changes bytes.
int red = (int)(luma + (values.RedCr * redDifference) + 0.5F);
int green = (int)(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + 0.5F);
int blue = (int)(luma + (values.BlueCb * blueDifference) + 0.5F);
r = (byte)(Numerics.Clamp(red, 0, values.Maximum) >> values.OutputShift);
g = (byte)(Numerics.Clamp(green, 0, values.Maximum) >> values.OutputShift);
b = (byte)(Numerics.Clamp(blue, 0, values.Maximum) >> values.OutputShift);
}
}
}

82
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides pinned-libheif monochrome presentation. The luma code value is reduced directly to eight bits and copied
/// to all RGB components; signaled luma-range expansion is intentionally absent because libheif's direct monochrome
/// operation does not apply it.
/// </content>
internal static partial class HeifYuvToRgb8Converter
{
/// <summary>
/// Implements pinned-libheif monochrome conversion for scalar and SIMD lanes.
/// </summary>
private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb8Operator
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector512<int> y,
Vector512<int> cb,
Vector512<int> cr,
in ConversionParameters parameters,
out Vector512<int> r,
out Vector512<int> g,
out Vector512<int> b)
{
Vector512<int> value = y >> parameters.LibheifSixteenLane.OutputShift;
r = value;
g = value;
b = value;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector256<int> y,
Vector256<int> cb,
Vector256<int> cr,
in ConversionParameters parameters,
out Vector256<int> r,
out Vector256<int> g,
out Vector256<int> b)
{
Vector256<int> value = y >> parameters.LibheifEightLane.OutputShift;
r = value;
g = value;
b = value;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(
Vector128<int> y,
Vector128<int> cb,
Vector128<int> cr,
in ConversionParameters parameters,
out Vector128<int> r,
out Vector128<int> g,
out Vector128<int> b)
{
Vector128<int> value = y >> parameters.LibheifFourLane.OutputShift;
r = value;
g = value;
b = value;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert(ushort y, ushort cb, ushort cr, in ConversionParameters parameters, out byte r, out byte g, out byte b)
{
byte value = (byte)(y >> parameters.LibheifScalar.OutputShift);
r = value;
g = value;
b = value;
}
}
}

113
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Operator.cs → src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Operator.cs

@ -13,51 +13,39 @@ namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// pixels; each native chroma sample is duplicated into the two lanes covered by horizontal subsampling before the /// pixels; each native chroma sample is duplicated into the two lanes covered by horizontal subsampling before the
/// closed color operator runs. All products remain in signed 32-bit lanes until clipped RGB values are narrowed. /// closed color operator runs. All products remain in signed 32-bit lanes until clipped RGB values are narrowed.
/// </content> /// </content>
internal static partial class HeifYuv420ToRgb8Converter internal static partial class HeifYuvToRgb8Converter
{ {
/// <summary> /// <summary>
/// Defines fixed-point color arithmetic for scalar and SIMD lanes. /// Defines fixed-point color arithmetic for scalar and SIMD lanes.
/// </summary> /// </summary>
private interface IHeifYuv420ToRgb8Operator private interface IHeifYuvToRgb8Operator
{ {
/// <summary> /// <summary>
/// Converts one full-range YCbCr sample to eight-bit RGB. /// Converts sixteen YCbCr samples to eight-bit RGB lanes.
/// </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> /// </summary>
/// <param name="y">The luma lanes.</param> /// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param> /// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param> /// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The four-lane fixed-point parameters.</param> /// <param name="parameters">The image conversion parameters.</param>
/// <param name="r">The converted red lanes.</param> /// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param> /// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param> /// <param name="b">The converted blue lanes.</param>
public static abstract void Convert( public static abstract void Convert(
Vector128<int> y, Vector512<int> y,
Vector128<int> cb, Vector512<int> cb,
Vector128<int> cr, Vector512<int> cr,
in Vector128Parameters parameters, in ConversionParameters parameters,
out Vector128<int> r, out Vector512<int> r,
out Vector128<int> g, out Vector512<int> g,
out Vector128<int> b); out Vector512<int> b);
/// <summary> /// <summary>
/// Converts eight full-range YCbCr samples to eight-bit RGB lanes. /// Converts eight YCbCr samples to eight-bit RGB lanes.
/// </summary> /// </summary>
/// <param name="y">The luma lanes.</param> /// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param> /// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param> /// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The eight-lane fixed-point parameters.</param> /// <param name="parameters">The image conversion parameters.</param>
/// <param name="r">The converted red lanes.</param> /// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param> /// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param> /// <param name="b">The converted blue lanes.</param>
@ -65,29 +53,41 @@ internal static partial class HeifYuv420ToRgb8Converter
Vector256<int> y, Vector256<int> y,
Vector256<int> cb, Vector256<int> cb,
Vector256<int> cr, Vector256<int> cr,
in Vector256Parameters parameters, in ConversionParameters parameters,
out Vector256<int> r, out Vector256<int> r,
out Vector256<int> g, out Vector256<int> g,
out Vector256<int> b); out Vector256<int> b);
/// <summary> /// <summary>
/// Converts sixteen full-range YCbCr samples to eight-bit RGB lanes. /// Converts four YCbCr samples to eight-bit RGB lanes.
/// </summary> /// </summary>
/// <param name="y">The luma lanes.</param> /// <param name="y">The luma lanes.</param>
/// <param name="cb">The blue-difference lanes.</param> /// <param name="cb">The blue-difference lanes.</param>
/// <param name="cr">The red-difference lanes.</param> /// <param name="cr">The red-difference lanes.</param>
/// <param name="parameters">The sixteen-lane fixed-point parameters.</param> /// <param name="parameters">The image conversion parameters.</param>
/// <param name="r">The converted red lanes.</param> /// <param name="r">The converted red lanes.</param>
/// <param name="g">The converted green lanes.</param> /// <param name="g">The converted green lanes.</param>
/// <param name="b">The converted blue lanes.</param> /// <param name="b">The converted blue lanes.</param>
public static abstract void Convert( public static abstract void Convert(
Vector512<int> y, Vector128<int> y,
Vector512<int> cb, Vector128<int> cb,
Vector512<int> cr, Vector128<int> cr,
in Vector512Parameters parameters, in ConversionParameters parameters,
out Vector512<int> r, out Vector128<int> r,
out Vector512<int> g, out Vector128<int> g,
out Vector512<int> b); out Vector128<int> b);
/// <summary>
/// Converts one 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 image conversion 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 ConversionParameters parameters, out byte r, out byte g, out byte b);
} }
/// <summary> /// <summary>
@ -100,6 +100,7 @@ internal static partial class HeifYuv420ToRgb8Converter
/// <param name="red">The destination red samples.</param> /// <param name="red">The destination red samples.</param>
/// <param name="green">The destination green samples.</param> /// <param name="green">The destination green samples.</param>
/// <param name="blue">The destination blue samples.</param> /// <param name="blue">The destination blue samples.</param>
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
/// <param name="parameters">The fixed-point matrix coefficients.</param> /// <param name="parameters">The fixed-point matrix coefficients.</param>
private static void ConvertRow<TOperator>( private static void ConvertRow<TOperator>(
ReadOnlySpan<ushort> luma, ReadOnlySpan<ushort> luma,
@ -108,8 +109,9 @@ internal static partial class HeifYuv420ToRgb8Converter
Span<byte> red, Span<byte> red,
Span<byte> green, Span<byte> green,
Span<byte> blue, Span<byte> blue,
int subsamplingX,
in ConversionParameters parameters) in ConversionParameters parameters)
where TOperator : struct, IHeifYuv420ToRgb8Operator where TOperator : struct, IHeifYuvToRgb8Operator
{ {
ref ushort lumaBase = ref MemoryMarshal.GetReference(luma); ref ushort lumaBase = ref MemoryMarshal.GetReference(luma);
ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue); ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue);
@ -128,10 +130,15 @@ internal static partial class HeifYuv420ToRgb8Converter
for (; x <= oneVectorFromEnd; x += Vector512<int>.Count) for (; x <= oneVectorFromEnd; x += Vector512<int>.Count)
{ {
Vector512<int> y = LoadVector512(ref Unsafe.Add(ref lumaBase, x)); Vector512<int> y = LoadVector512(ref Unsafe.Add(ref lumaBase, x));
Vector512<int> cb = LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); Vector512<int> cb = subsamplingX == 0
Vector512<int> cr = LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1)); ? LoadVector512(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector512<int> cr = subsamplingX == 0
? LoadVector512(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.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, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b);
HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x)); HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x)); HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x)); HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -145,10 +152,15 @@ internal static partial class HeifYuv420ToRgb8Converter
for (; x <= oneVectorFromEnd; x += Vector256<int>.Count) for (; x <= oneVectorFromEnd; x += Vector256<int>.Count)
{ {
Vector256<int> y = LoadVector256(ref Unsafe.Add(ref lumaBase, x)); Vector256<int> y = LoadVector256(ref Unsafe.Add(ref lumaBase, x));
Vector256<int> cb = LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); Vector256<int> cb = subsamplingX == 0
Vector256<int> cr = LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1)); ? LoadVector256(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
TOperator.Convert(y, cb, cr, in parameters.EightLane, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b); Vector256<int> cr = subsamplingX == 0
? LoadVector256(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.Convert(y, cb, cr, in parameters, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b);
HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x)); HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x)); HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x)); HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -162,10 +174,15 @@ internal static partial class HeifYuv420ToRgb8Converter
for (; x <= oneVectorFromEnd; x += Vector128<int>.Count) for (; x <= oneVectorFromEnd; x += Vector128<int>.Count)
{ {
Vector128<int> y = LoadVector128(ref Unsafe.Add(ref lumaBase, x)); Vector128<int> y = LoadVector128(ref Unsafe.Add(ref lumaBase, x));
Vector128<int> cb = LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); Vector128<int> cb = subsamplingX == 0
Vector128<int> cr = LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1)); ? LoadVector128(ref Unsafe.Add(ref chromaBlueBase, x))
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1));
Vector128<int> cr = subsamplingX == 0
? LoadVector128(ref Unsafe.Add(ref chromaRedBase, x))
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1));
TOperator.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, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b);
HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x)); HeifByteSampleConverter.Store(r, ref Unsafe.Add(ref redBase, x));
HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x)); HeifByteSampleConverter.Store(g, ref Unsafe.Add(ref greenBase, x));
HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x)); HeifByteSampleConverter.Store(b, ref Unsafe.Add(ref blueBase, x));
@ -176,9 +193,9 @@ internal static partial class HeifYuv420ToRgb8Converter
{ {
TOperator.Convert( TOperator.Convert(
Unsafe.Add(ref lumaBase, x), Unsafe.Add(ref lumaBase, x),
Unsafe.Add(ref chromaBlueBase, x >> 1), Unsafe.Add(ref chromaBlueBase, x >> subsamplingX),
Unsafe.Add(ref chromaRedBase, x >> 1), Unsafe.Add(ref chromaRedBase, x >> subsamplingX),
in parameters.Scalar, in parameters,
out Unsafe.Add(ref redBase, x), out Unsafe.Add(ref redBase, x),
out Unsafe.Add(ref greenBase, x), out Unsafe.Add(ref greenBase, x),
out Unsafe.Add(ref blueBase, x)); out Unsafe.Add(ref blueBase, x));

540
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs

@ -0,0 +1,540 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
namespace SixLabors.ImageSharp.Formats.Heif.Components;
/// <content>
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
/// </content>
internal static partial class HeifYuvToRgb8Converter
{
/// <summary>
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
/// </summary>
private readonly struct ConversionParameters
{
/// <summary>
/// The scalar fixed-point coefficients.
/// </summary>
public readonly FixedPointParameters FixedPointScalar;
/// <summary>
/// The four-lane SIMD coefficients.
/// </summary>
public readonly Vector128Parameters FixedPointFourLane;
/// <summary>
/// The eight-lane SIMD coefficients.
/// </summary>
public readonly Vector256Parameters FixedPointEightLane;
/// <summary>
/// The sixteen-lane SIMD coefficients.
/// </summary>
public readonly Vector512Parameters FixedPointSixteenLane;
/// <summary>
/// The scalar pinned-libheif conversion parameters.
/// </summary>
public readonly LibheifParameters LibheifScalar;
/// <summary>
/// The four-lane pinned-libheif conversion parameters.
/// </summary>
public readonly LibheifVector128Parameters LibheifFourLane;
/// <summary>
/// The eight-lane pinned-libheif conversion parameters.
/// </summary>
public readonly LibheifVector256Parameters LibheifEightLane;
/// <summary>
/// The sixteen-lane pinned-libheif conversion parameters.
/// </summary>
public readonly LibheifVector512Parameters LibheifSixteenLane;
/// <summary>
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
/// </summary>
/// <param name="parameters">The shared floating-point conversion parameters.</param>
/// <param name="bitDepth">The common source component precision.</param>
public ConversionParameters(in HeifColorConversionParameters parameters, int bitDepth)
{
FixedPointParameters scalar = new(in parameters);
this.FixedPointScalar = scalar;
this.FixedPointFourLane = new(in scalar);
this.FixedPointEightLane = new(in scalar);
this.FixedPointSixteenLane = new(in scalar);
LibheifParameters libheif = new(in parameters, bitDepth);
this.LibheifScalar = libheif;
this.LibheifFourLane = new(in libheif);
this.LibheifEightLane = new(in libheif);
this.LibheifSixteenLane = new(in libheif);
}
}
/// <summary>
/// Stores the scalar fixed-point coefficients resolved for one image.
/// </summary>
private readonly struct FixedPointParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="FixedPointParameters"/> struct.
/// </summary>
/// <param name="parameters">The shared floating-point conversion parameters.</param>
public FixedPointParameters(in HeifColorConversionParameters parameters)
{
float scale = 1 << CoefficientShift;
// Rounding each image-invariant coefficient once gives the integer kernel eight fractional bits.
// The signed green coefficients retain the exact addition and rounding order used by every SIMD lane.
this.RedCr = (int)MathF.Round(parameters.RedChromaScale * scale, MidpointRounding.AwayFromZero);
this.GreenCb = -(int)MathF.Round(parameters.GreenBlueChromaScale * scale, MidpointRounding.AwayFromZero);
this.GreenCr = -(int)MathF.Round(parameters.GreenRedChromaScale * scale, MidpointRounding.AwayFromZero);
this.BlueCb = (int)MathF.Round(parameters.BlueChromaScale * scale, MidpointRounding.AwayFromZero);
}
/// <summary>
/// Gets the red contribution from centered Cr.
/// </summary>
public int RedCr { get; }
/// <summary>
/// Gets the green contribution from centered Cb.
/// </summary>
public int GreenCb { get; }
/// <summary>
/// Gets the green contribution from centered Cr.
/// </summary>
public int GreenCr { get; }
/// <summary>
/// Gets the blue contribution from centered Cb.
/// </summary>
public int BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for four-lane conversion.
/// </summary>
private readonly struct Vector128Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector128Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector128.Create(HeifYuvToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector128.Create(HeifYuvToRgb8Converter.RoundingBias);
this.Maximum = Vector128.Create((int)byte.MaxValue);
this.RedCr = Vector128.Create(parameters.RedCr);
this.GreenCb = Vector128.Create(parameters.GreenCb);
this.GreenCr = Vector128.Create(parameters.GreenCr);
this.BlueCb = Vector128.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector128<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector128<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector128<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector128<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector128<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector128<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector128<int> BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for eight-lane conversion.
/// </summary>
private readonly struct Vector256Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector256Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector256.Create(HeifYuvToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector256.Create(HeifYuvToRgb8Converter.RoundingBias);
this.Maximum = Vector256.Create((int)byte.MaxValue);
this.RedCr = Vector256.Create(parameters.RedCr);
this.GreenCb = Vector256.Create(parameters.GreenCb);
this.GreenCr = Vector256.Create(parameters.GreenCr);
this.BlueCb = Vector256.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector256<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector256<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector256<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector256<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector256<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector256<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector256<int> BlueCb { get; }
}
/// <summary>
/// Broadcasts the fixed-point coefficients for sixteen-lane conversion.
/// </summary>
private readonly struct Vector512Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar fixed-point coefficients.</param>
public Vector512Parameters(in FixedPointParameters parameters)
{
this.ChromaMidpoint = Vector512.Create(HeifYuvToRgb8Converter.ChromaMidpoint);
this.RoundingBias = Vector512.Create(HeifYuvToRgb8Converter.RoundingBias);
this.Maximum = Vector512.Create((int)byte.MaxValue);
this.RedCr = Vector512.Create(parameters.RedCr);
this.GreenCb = Vector512.Create(parameters.GreenCb);
this.GreenCr = Vector512.Create(parameters.GreenCr);
this.BlueCb = Vector512.Create(parameters.BlueCb);
}
/// <summary>
/// Gets the neutral chroma code-value lanes.
/// </summary>
public Vector512<int> ChromaMidpoint { get; }
/// <summary>
/// Gets the fixed-point rounding-bias lanes.
/// </summary>
public Vector512<int> RoundingBias { get; }
/// <summary>
/// Gets the maximum eight-bit sample lanes.
/// </summary>
public Vector512<int> Maximum { get; }
/// <summary>
/// Gets the red Cr coefficient lanes.
/// </summary>
public Vector512<int> RedCr { get; }
/// <summary>
/// Gets the green Cb coefficient lanes.
/// </summary>
public Vector512<int> GreenCb { get; }
/// <summary>
/// Gets the green Cr coefficient lanes.
/// </summary>
public Vector512<int> GreenCr { get; }
/// <summary>
/// Gets the blue Cb coefficient lanes.
/// </summary>
public Vector512<int> BlueCb { get; }
}
/// <summary>
/// Stores the scalar coefficients and range values used by pinned libheif 1.23.1.
/// </summary>
private readonly struct LibheifParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="LibheifParameters"/> struct.
/// </summary>
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
/// <param name="bitDepth">The common source component precision.</param>
public LibheifParameters(in HeifColorConversionParameters parameters, int bitDepth)
{
this.LumaOffset = parameters.IsFullRange ? 0F : parameters.LumaBias;
this.LumaScale = parameters.IsFullRange ? 1F : 1.1689F;
this.ChromaMidpoint = parameters.ChromaBias;
this.ChromaScale = parameters.IsFullRange ? 1F : 1.1429F;
if (parameters.MatrixCoefficients == CicpMatrixCoefficients.Unspecified)
{
// libheif falls back to these literal Rec.601 coefficients when no matrix is signaled.
this.RedCr = 1.402F;
this.GreenCb = -0.344136F;
this.GreenCr = -0.714136F;
this.BlueCb = 1.772F;
}
else
{
float kr = parameters.Kr;
float kb = parameters.Kb;
this.RedCr = 2F * (-kr + 1F);
this.GreenCb = 2F * kb * (-kb + 1F) / (kb + kr - 1F);
this.GreenCr = 2F * kr * (-kr + 1F) / (kb + kr - 1F);
this.BlueCb = 2F * (-kb + 1F);
}
this.Maximum = (1 << bitDepth) - 1;
this.OutputShift = bitDepth - 8;
}
/// <summary>
/// Gets the luma code-value offset removed before limited-range expansion.
/// </summary>
public float LumaOffset { get; }
/// <summary>
/// Gets the luma range-expansion factor.
/// </summary>
public float LumaScale { get; }
/// <summary>
/// Gets the neutral chroma code value.
/// </summary>
public float ChromaMidpoint { get; }
/// <summary>
/// Gets the chroma range-expansion factor.
/// </summary>
public float ChromaScale { get; }
/// <summary>
/// Gets the red contribution from Cr.
/// </summary>
public float RedCr { get; }
/// <summary>
/// Gets the green contribution from Cb.
/// </summary>
public float GreenCb { get; }
/// <summary>
/// Gets the green contribution from Cr.
/// </summary>
public float GreenCr { get; }
/// <summary>
/// Gets the blue contribution from Cb.
/// </summary>
public float BlueCb { get; }
/// <summary>
/// Gets the largest RGB code value at the source precision.
/// </summary>
public int Maximum { get; }
/// <summary>
/// Gets the right shift reducing source-precision RGB to eight bits.
/// </summary>
public int OutputShift { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for four-lane conversion.
/// </summary>
private readonly struct LibheifVector128Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="LibheifVector128Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public LibheifVector128Parameters(in LibheifParameters parameters)
{
this.LumaOffset = Vector128.Create(parameters.LumaOffset);
this.LumaScale = Vector128.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector128.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector128.Create(parameters.ChromaScale);
this.RedCr = Vector128.Create(parameters.RedCr);
this.GreenCb = Vector128.Create(parameters.GreenCb);
this.GreenCr = Vector128.Create(parameters.GreenCr);
this.BlueCb = Vector128.Create(parameters.BlueCb);
this.Maximum = Vector128.Create(parameters.Maximum);
this.OutputShift = parameters.OutputShift;
}
/// <summary>Gets the luma offset lanes.</summary>
public Vector128<float> LumaOffset { get; }
/// <summary>Gets the luma scale lanes.</summary>
public Vector128<float> LumaScale { get; }
/// <summary>Gets the chroma-midpoint lanes.</summary>
public Vector128<float> ChromaMidpoint { get; }
/// <summary>Gets the chroma-scale lanes.</summary>
public Vector128<float> ChromaScale { get; }
/// <summary>Gets the red Cr coefficient lanes.</summary>
public Vector128<float> RedCr { get; }
/// <summary>Gets the green Cb coefficient lanes.</summary>
public Vector128<float> GreenCb { get; }
/// <summary>Gets the green Cr coefficient lanes.</summary>
public Vector128<float> GreenCr { get; }
/// <summary>Gets the blue Cb coefficient lanes.</summary>
public Vector128<float> BlueCb { get; }
/// <summary>Gets the maximum RGB code-value lanes.</summary>
public Vector128<int> Maximum { get; }
/// <summary>Gets the output reduction shift.</summary>
public int OutputShift { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for eight-lane conversion.
/// </summary>
private readonly struct LibheifVector256Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="LibheifVector256Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public LibheifVector256Parameters(in LibheifParameters parameters)
{
this.LumaOffset = Vector256.Create(parameters.LumaOffset);
this.LumaScale = Vector256.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector256.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector256.Create(parameters.ChromaScale);
this.RedCr = Vector256.Create(parameters.RedCr);
this.GreenCb = Vector256.Create(parameters.GreenCb);
this.GreenCr = Vector256.Create(parameters.GreenCr);
this.BlueCb = Vector256.Create(parameters.BlueCb);
this.Maximum = Vector256.Create(parameters.Maximum);
this.OutputShift = parameters.OutputShift;
}
/// <summary>Gets the luma offset lanes.</summary>
public Vector256<float> LumaOffset { get; }
/// <summary>Gets the luma scale lanes.</summary>
public Vector256<float> LumaScale { get; }
/// <summary>Gets the chroma-midpoint lanes.</summary>
public Vector256<float> ChromaMidpoint { get; }
/// <summary>Gets the chroma-scale lanes.</summary>
public Vector256<float> ChromaScale { get; }
/// <summary>Gets the red Cr coefficient lanes.</summary>
public Vector256<float> RedCr { get; }
/// <summary>Gets the green Cb coefficient lanes.</summary>
public Vector256<float> GreenCb { get; }
/// <summary>Gets the green Cr coefficient lanes.</summary>
public Vector256<float> GreenCr { get; }
/// <summary>Gets the blue Cb coefficient lanes.</summary>
public Vector256<float> BlueCb { get; }
/// <summary>Gets the maximum RGB code-value lanes.</summary>
public Vector256<int> Maximum { get; }
/// <summary>Gets the output reduction shift.</summary>
public int OutputShift { get; }
}
/// <summary>
/// Broadcasts pinned-libheif coefficients for sixteen-lane conversion.
/// </summary>
private readonly struct LibheifVector512Parameters
{
/// <summary>
/// Initializes a new instance of the <see cref="LibheifVector512Parameters"/> struct.
/// </summary>
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
public LibheifVector512Parameters(in LibheifParameters parameters)
{
this.LumaOffset = Vector512.Create(parameters.LumaOffset);
this.LumaScale = Vector512.Create(parameters.LumaScale);
this.ChromaMidpoint = Vector512.Create(parameters.ChromaMidpoint);
this.ChromaScale = Vector512.Create(parameters.ChromaScale);
this.RedCr = Vector512.Create(parameters.RedCr);
this.GreenCb = Vector512.Create(parameters.GreenCb);
this.GreenCr = Vector512.Create(parameters.GreenCr);
this.BlueCb = Vector512.Create(parameters.BlueCb);
this.Maximum = Vector512.Create(parameters.Maximum);
this.OutputShift = parameters.OutputShift;
}
/// <summary>Gets the luma offset lanes.</summary>
public Vector512<float> LumaOffset { get; }
/// <summary>Gets the luma scale lanes.</summary>
public Vector512<float> LumaScale { get; }
/// <summary>Gets the chroma-midpoint lanes.</summary>
public Vector512<float> ChromaMidpoint { get; }
/// <summary>Gets the chroma-scale lanes.</summary>
public Vector512<float> ChromaScale { get; }
/// <summary>Gets the red Cr coefficient lanes.</summary>
public Vector512<float> RedCr { get; }
/// <summary>Gets the green Cb coefficient lanes.</summary>
public Vector512<float> GreenCb { get; }
/// <summary>Gets the green Cr coefficient lanes.</summary>
public Vector512<float> GreenCr { get; }
/// <summary>Gets the blue Cb coefficient lanes.</summary>
public Vector512<float> BlueCb { get; }
/// <summary>Gets the maximum RGB code-value lanes.</summary>
public Vector512<int> Maximum { get; }
/// <summary>Gets the output reduction shift.</summary>
public int OutputShift { get; }
}
}

207
src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs

@ -0,0 +1,207 @@
// 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.Components;
/// <summary>
/// Converts HEIF YUV planes directly to packed eight-bit RGB pixels.
/// </summary>
internal static partial class HeifYuvToRgb8Converter
{
/// <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 fixed-point 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 SupportsFixedPointConversion(
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>
/// Determines whether the pinned libheif-compatible 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="isMonochrome">Whether the image contains only luma samples.</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 SupportsLibheifConversion(
int subsamplingX,
int subsamplingY,
int lumaBitDepth,
int chromaBitDepth,
bool isMonochrome,
HeifColorConversionMode mode)
=> (isMonochrome || (subsamplingX is 0 or 1 && subsamplingY is 0 or 1))
&& lumaBitDepth == 8
&& (isMonochrome || chromaBitDepth == 8)
&& 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="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="buffer">The reconstructed component-plane buffer.</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 ConvertFixedPoint<TPixel, TBuffer>(
Configuration configuration,
TBuffer buffer,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort>
{
ConversionParameters conversionParameters = new(in parameters, 8);
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 buffer 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 = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width);
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(lumaY >> 1).Slice(sourceX >> 1);
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(lumaY >> 1).Slice(sourceX >> 1);
ConvertRow<FixedPointCoefficientOperator>(
luma,
chromaBlue,
chromaRed,
red,
green,
blue,
1,
in conversionParameters);
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination);
}
}
/// <summary>
/// Converts supported HEVC planes with the arithmetic and nearest-sample traversal used by pinned libheif.
/// </summary>
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
/// <param name="buffer">The reconstructed component-plane buffer.</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 ConvertLibheif<TPixel, TBuffer>(
Configuration configuration,
TBuffer buffer,
ImageFrame<TPixel> image,
in HeifColorConversionParameters parameters,
int sourceX,
int sourceY)
where TPixel : unmanaged, IPixel<TPixel>
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort>
{
ConversionParameters conversionParameters = new(in parameters, buffer.LumaBitDepth);
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;
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width);
if (buffer.IsMonochrome)
{
// Pinned libheif reduces monochrome precision first and copies that code value to RGB. It does not
// apply the signaled limited-range expansion used by its three-component conversion operation.
ConvertRow<LibheifMonochromeOperator>(
luma,
luma,
luma,
red,
green,
blue,
0,
in conversionParameters);
}
else
{
int subsamplingX = buffer.ChromaSubsamplingX;
int chromaY = lumaY >> buffer.ChromaSubsamplingY;
// The HEIF crop boundary validates horizontal offsets in complete chroma-sample units. Slicing once
// therefore preserves libheif's x >> subsampling mapping without a phase branch in the SIMD loop.
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX);
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(chromaY).Slice(sourceX >> subsamplingX);
ConvertRow<LibheifCoefficientOperator>(
luma,
chromaBlue,
chromaRed,
red,
green,
blue,
subsamplingX,
in conversionParameters);
}
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination);
}
}
}

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

@ -36,6 +36,50 @@ internal static class HevcYuvConverter
{ {
HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out HeifColorConversionMode mode); HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out HeifColorConversionMode mode);
HevcPlanarSampleBuffer buffer = new(picture, chromaSampleLocation); HevcPlanarSampleBuffer buffer = new(picture, chromaSampleLocation);
if (HeifYuvToRgb8Converter.SupportsLibheifConversion(
buffer.ChromaSubsamplingX,
buffer.ChromaSubsamplingY,
buffer.LumaBitDepth,
buffer.ChromaBitDepth,
buffer.IsMonochrome,
mode))
{
// libheif 1.23.1 is the pinned HEIC presentation implementation. Its pipeline search selects the
// lower-cost direct YCbCr operation when preferred-only upsampling is disabled, so subsampled chroma is
// nearest-replicated and high-bit-depth RGB is rounded before a separate shift to eight bits.
HeifYuvToRgb8Converter.ConvertLibheif(
configuration,
buffer,
image,
in parameters,
sourceX,
sourceY);
return;
}
if (HeifYuvToRgb16Converter.SupportsLibheifConversion(
buffer.ChromaSubsamplingX,
buffer.ChromaSubsamplingY,
buffer.LumaBitDepth,
buffer.ChromaBitDepth,
buffer.IsMonochrome,
mode))
{
// High-bit-depth conversion retains every rounded source-precision RGB bit in UInt16 storage before
// PixelOperations performs the requested TPixel conversion. This prevents an Rgba32 test from concealing
// precision loss in Rgba64, Rgb48, or floating-point decoder output.
HeifYuvToRgb16Converter.Convert(
configuration,
buffer,
image,
in parameters,
sourceX,
sourceY);
return;
}
HeifPlanarColorConverter.ConvertToRgb<TPixel, HevcPlanarSampleBuffer>( HeifPlanarColorConverter.ConvertToRgb<TPixel, HevcPlanarSampleBuffer>(
configuration, configuration,
buffer, buffer,

867
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs

File diff suppressed because it is too large

256
tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs

@ -99,135 +99,142 @@ public class HeifDecoderTests
/// <summary> /// <summary>
/// Verifies genuine HEIC presentation for the official HEVC profile and Range Extensions matrix against /// Verifies genuine HEIC presentation for the official HEVC profile and Range Extensions matrix against
/// independently decoded FFmpeg output. /// independently pinned presentation references.
/// </summary> /// </summary>
/// <param name="bitstreamPath">The retained official elementary-stream path used to derive the HEIC and reference paths.</param> /// <param name="provider">The genuine HEIC input and matching reference-output naming context.</param>
/// <param name="width">The independently reported presented width.</param> /// <param name="width">The independently reported presented width.</param>
/// <param name="height">The independently reported presented height.</param> /// <param name="height">The independently reported presented height.</param>
/// <param name="bitDepth">The maximum coded component precision.</param> /// <param name="bitDepth">The maximum coded component precision.</param>
[Theory] [Theory]
[InlineData(TestImages.Heif.General8Bit420, 400, 384, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.General8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.General8Bit444, 400, 384, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.General10Bit420Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.General10Bit420, 400, 384, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.General10Bit422Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.General10Bit422, 400, 384, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.General10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.General10Bit444, 400, 384, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.General12Bit420Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.General12Bit420, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.General12Bit422Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.General12Bit422, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.General12Bit444, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.RangeExtensionChromaAngle422Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.RangeExtensionChromaAngle422, 1920, 1080, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.RangeExtensionCrossComponent8Bit444Heic, PixelTypes.Rgba32, 1280, 720, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.RangeExtensionCrossComponent8Bit444, 1280, 720, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.RangeExtensionCrossComponent10Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.RangeExtensionCrossComponent10Bit444, 1920, 1080, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.RangeExtensionCrossComponent12Bit444Heic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.RangeExtensionCrossComponent12Bit444, 2560, 1600, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.ExtendedPrecision8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.ExtendedPrecision8Bit444, 400, 384, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.ExtendedPrecision10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.ExtendedPrecision10Bit444, 400, 384, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.ExtendedPrecision12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.ExtendedPrecision12Bit444, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.RangeExtensionPcm10Bit422Heic, PixelTypes.Rgba32, 416, 240, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.RangeExtensionPcm10Bit422, 416, 240, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.RangeExtensionPcm12Bit444Heic, PixelTypes.Rgba32, 416, 240, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.RangeExtensionPcm12Bit444, 416, 240, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.PersistentRice12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.PersistentRice12Bit444, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.TransformSkipContext8Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.TransformSkipContext8Bit444, 1920, 1080, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.TransformSkipContext10Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.TransformSkipContext10Bit444, 1920, 1080, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.TransformSkipContext12Bit444Heic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit12)]
[InlineData(TestImages.Heif.TransformSkipContext12Bit444, 2560, 1600, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.Main42210AHeic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.Main42210A, 1920, 1080, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.Main42210BHeic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.Main42210B, 2560, 1600, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.HighThroughput8Bit420WavefrontHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.HighThroughput8Bit420Wavefront, 1024, 768, HeifBitDepth.Bit8)] public void DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation(
public void DecodeHevcRangeExtensionStillImageMatchesFfmpeg( TestImageProvider<Rgba32> provider,
string bitstreamPath,
int width, int width,
int height, int height,
HeifBitDepth bitDepth) HeifBitDepth bitDepth)
{ {
string heicPath = $"{bitstreamPath[..^4]}.heic"; using Image<Rgba32> image = provider.GetImage();
string referencePath = $"{bitstreamPath[..^4]}-ffmpeg.png"; HeifMetadata metadata = image.Metadata.GetHeifMetadata();
using Image<Rgba32> expected = TestFile.Create(referencePath).CreateRgba32Image(); image.DebugSave(provider);
using Image<Rgba32> actual = TestFile.Create(heicPath).CreateRgba32Image();
HeifMetadata metadata = actual.Metadata.GetHeifMetadata();
// HEVC leaves the chroma upsampling filter implementation-defined. The per-pixel threshold absorbs image.CompareToReferenceOutput(ImageComparer.Exact, provider);
// rounding-scale filter differences while the exact native-plane tests continue to prove reconstruction.
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(expected, actual);
Assert.Equal(new Size(width, height), actual.Size); Assert.Equal(new Size(width, height), image.Size);
Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod);
Assert.Equal(bitDepth, metadata.BitDepth); Assert.Equal(bitDepth, metadata.BitDepth);
Assert.Null(actual.Metadata.IccProfile); Assert.Null(image.Metadata.IccProfile);
} }
/// <summary> /// <summary>
/// Verifies genuine monochrome HEIC presentation against FFmpeg presentation derived from official native output. /// Verifies genuine monochrome HEIC presentation against pinned libheif output.
/// </summary> /// </summary>
/// <param name="bitstreamPath">The retained official elementary-stream path used to derive the HEIC and reference paths.</param> /// <param name="provider">The image provider.</param>
/// <param name="bitDepth">The coded luma precision.</param> /// <param name="bitDepth">The coded luma precision.</param>
[Theory] [Theory]
[InlineData(TestImages.Heif.General8BitMonochrome, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.General8BitMonochromeHeic, PixelTypes.Rgba32, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.General12BitMonochrome, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba32, HeifBitDepth.Bit12)]
public void DecodeHevcMonochromeStillImageMatchesHmFfmpeg(string bitstreamPath, HeifBitDepth bitDepth) public void DecodeHevcMonochromeStillImageMatchesPinnedLibheif(
TestImageProvider<Rgba32> provider,
HeifBitDepth bitDepth)
{ {
string heicPath = $"{bitstreamPath[..^4]}.heic"; using Image<Rgba32> image = provider.GetImage();
string referencePath = $"{bitstreamPath[..^4]}-hm-ffmpeg.png"; HeifMetadata metadata = image.Metadata.GetHeifMetadata();
using Image<Rgba32> expected = TestFile.Create(referencePath).CreateRgba32Image(); image.DebugSave(provider);
using Image<Rgba32> actual = TestFile.Create(heicPath).CreateRgba32Image();
HeifMetadata metadata = actual.Metadata.GetHeifMetadata();
// The reference explicitly expands the limited-range official luma samples before RGB packing. The exact image.CompareToReferenceOutput(ImageComparer.Exact, provider);
// published luma-plane comparison remains responsible for proving HEVC reconstruction.
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(expected, actual);
Assert.Equal(new Size(400, 384), actual.Size); Assert.Equal(new Size(400, 384), image.Size);
Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod);
Assert.Equal(bitDepth, metadata.BitDepth); Assert.Equal(bitDepth, metadata.BitDepth);
Assert.Null(actual.Metadata.IccProfile); Assert.Null(image.Metadata.IccProfile);
}
/// <summary>
/// Verifies that high-bit-depth monochrome HEIC presentation preserves its source precision.
/// </summary>
/// <param name="provider">The image provider.</param>
[Theory]
[WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba64)]
public void DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision(TestImageProvider<Rgba64> provider)
{
using Image<Rgba64> image = provider.GetImage();
HeifMetadata metadata = image.Metadata.GetHeifMetadata();
image.DebugSave(provider);
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
Assert.Equal(new Size(400, 384), image.Size);
Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod);
Assert.Equal(HeifBitDepth.Bit12, metadata.BitDepth);
Assert.Null(image.Metadata.IccProfile);
} }
/// <summary> /// <summary>
/// Verifies genuine HEIC presentation for the official high-throughput Range Extensions streams against /// Verifies genuine HEIC presentation for the official high-throughput Range Extensions streams against
/// FFmpeg presentation derived from pinned HM reference-decoder output. /// independently pinned presentation references.
/// </summary> /// </summary>
/// <param name="bitstreamPath">The retained official elementary-stream path used to derive the HEIC and reference paths.</param> /// <param name="provider">The genuine HEIC input and matching reference-output naming context.</param>
/// <param name="width">The independently reported presented width.</param> /// <param name="width">The independently reported presented width.</param>
/// <param name="height">The independently reported presented height.</param> /// <param name="height">The independently reported presented height.</param>
/// <param name="bitDepth">The maximum coded component precision.</param> /// <param name="bitDepth">The maximum coded component precision.</param>
[Theory] [Theory]
[InlineData(TestImages.Heif.HighThroughputExtendedPrecision8Bit444, 400, 384, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.HighThroughputExtendedPrecision8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.HighThroughputExtendedPrecision10Bit444, 400, 384, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.HighThroughputExtendedPrecision10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.HighThroughputExtendedPrecision12Bit444, 400, 384, HeifBitDepth.Bit12)] [WithFile(TestImages.Heif.HighThroughput10Bit422TilesWavefrontHeic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)]
[InlineData(TestImages.Heif.HighThroughput10Bit422TilesWavefront, 1920, 1080, HeifBitDepth.Bit10)] [WithFile(TestImages.Heif.HighThroughput8Bit420TilesWavefrontHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.HighThroughput8Bit420TilesWavefront, 1024, 768, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.HighThroughput8Bit420CabacBypassAlignmentHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.HighThroughput8Bit420CabacBypassAlignment, 1024, 768, HeifBitDepth.Bit8)] [WithFile(TestImages.Heif.HighThroughput8Bit420ExtendedPrecisionHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)]
[InlineData(TestImages.Heif.HighThroughput8Bit420ExtendedPrecision, 1024, 768, HeifBitDepth.Bit8)] public void DecodeHevcHighThroughputStillImageMatchesPinnedPresentation(
public void DecodeHevcHighThroughputStillImageMatchesHmFfmpeg( TestImageProvider<Rgba32> provider,
string bitstreamPath,
int width, int width,
int height, int height,
HeifBitDepth bitDepth) HeifBitDepth bitDepth)
{ {
string heicPath = $"{bitstreamPath[..^4]}.heic"; using Image<Rgba32> image = provider.GetImage();
string referencePath = $"{bitstreamPath[..^4]}-hm-ffmpeg.png"; HeifMetadata metadata = image.Metadata.GetHeifMetadata();
using Image<Rgba32> expected = TestFile.Create(referencePath).CreateRgba32Image(); image.DebugSave(provider);
using Image<Rgba32> actual = TestFile.Create(heicPath).CreateRgba32Image();
HeifMetadata metadata = actual.Metadata.GetHeifMetadata();
// The HM native outputs match the published plane digests, and the matching ImageSharp tests prove exact image.CompareToReferenceOutput(ImageComparer.Exact, provider);
// reconstruction. This comparison therefore isolates the shared color-presentation path.
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(expected, actual);
Assert.Equal(new Size(width, height), actual.Size); Assert.Equal(new Size(width, height), image.Size);
Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod);
Assert.Equal(bitDepth, metadata.BitDepth); Assert.Equal(bitDepth, metadata.BitDepth);
Assert.Null(actual.Metadata.IccProfile); Assert.Null(image.Metadata.IccProfile);
} }
/// <summary> /// <summary>
/// Verifies genuine HEIC presentation for unequal luma and chroma precision, which independent HEIF decoders /// Verifies genuine HEIC presentation for unequal luma and chroma precision, which independent HEIF decoders
/// currently reject after reconstructing the native planes. /// currently reject after reconstructing the native planes.
/// </summary> /// </summary>
/// <param name="bitstreamPath">The retained official elementary-stream path used to derive the HEIC path.</param> /// <param name="provider">The retained genuine HEIC input.</param>
[Theory] [Theory]
[InlineData(TestImages.Heif.RangeExtensionLuma12Chroma8)] [WithFile(TestImages.Heif.RangeExtensionLuma12Chroma8Heic, PixelTypes.Rgba32)]
[InlineData(TestImages.Heif.RangeExtensionLuma8Chroma12)] [WithFile(TestImages.Heif.RangeExtensionLuma8Chroma12Heic, PixelTypes.Rgba32)]
public void DecodeHevcRangeExtensionUnequalBitDepthStillImage(string bitstreamPath) public void DecodeHevcRangeExtensionUnequalBitDepthStillImage(TestImageProvider<Rgba32> provider)
{ {
string heicPath = $"{bitstreamPath[..^4]}.heic"; using Image<Rgba32> image = provider.GetImage();
using Image<Rgba32> image = TestFile.Create(heicPath).CreateRgba32Image();
HeifMetadata metadata = image.Metadata.GetHeifMetadata(); HeifMetadata metadata = image.Metadata.GetHeifMetadata();
Assert.Equal(new Size(1920, 1080), image.Size); Assert.Equal(new Size(1920, 1080), image.Size);
@ -240,36 +247,29 @@ public class HeifDecoderTests
/// Verifies representative genuine HEIC Range Extensions presentation with allocator-split buffers and /// Verifies representative genuine HEIC Range Extensions presentation with allocator-split buffers and
/// exactly-once final disposal. /// exactly-once final disposal.
/// </summary> /// </summary>
/// <param name="bitstreamPath">The retained official elementary-stream path used to derive the HEIC and reference paths.</param> /// <param name="provider">The genuine HEIC input and allocator configuration.</param>
/// <param name="referenceSuffix">The suffix identifying the independently produced presentation reference.</param>
/// <param name="width">The independently reported presented width.</param> /// <param name="width">The independently reported presented width.</param>
/// <param name="height">The independently reported presented height.</param> /// <param name="height">The independently reported presented height.</param>
[Theory] [Theory]
[InlineData(TestImages.Heif.General8Bit420, "-ffmpeg.png", 400, 384)] [WithFile(TestImages.Heif.General8Bit420Heic, PixelTypes.Rgba32, 400, 384)]
[InlineData(TestImages.Heif.General10Bit422, "-ffmpeg.png", 400, 384)] [WithFile(TestImages.Heif.General10Bit422Heic, PixelTypes.Rgba32, 400, 384)]
[InlineData(TestImages.Heif.General12Bit444, "-ffmpeg.png", 400, 384)] [WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba32, 400, 384)]
[InlineData(TestImages.Heif.General12BitMonochrome, "-hm-ffmpeg.png", 400, 384)] [WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba32, 400, 384)]
[InlineData(TestImages.Heif.HighThroughput8Bit420ExtendedPrecision, "-hm-ffmpeg.png", 1024, 768)] [WithFile(TestImages.Heif.HighThroughput8Bit420ExtendedPrecisionHeic, PixelTypes.Rgba32, 1024, 768)]
public void DecodeHevcRangeExtensionStillImageWithConstrainedAllocator( public void DecodeHevcRangeExtensionStillImageWithConstrainedAllocator(
string bitstreamPath, TestImageProvider<Rgba32> provider,
string referenceSuffix,
int width, int width,
int height) int height)
{ {
string heicPath = $"{bitstreamPath[..^4]}.heic";
string referencePath = $"{bitstreamPath[..^4]}{referenceSuffix}";
byte[] imageBytes = TestFile.Create(heicPath).Bytes;
TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 4_096 }; TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 4_096 };
allocator.EnableNonThreadSafeLogging(); allocator.EnableNonThreadSafeLogging();
Configuration configuration = Configuration.Default.Clone(); Configuration configuration = Configuration.Default.Clone();
configuration.MemoryAllocator = allocator; configuration.MemoryAllocator = allocator;
DecoderOptions options = new() { Configuration = configuration }; provider.Configuration = configuration;
using (Image<Rgba32> expected = TestFile.Create(referencePath).CreateRgba32Image()) using (Image<Rgba32> image = provider.GetImage())
using (Image<Rgba32> actual = Image.Load<Rgba32>(options, imageBytes))
{ {
Assert.Equal(new Size(width, height), actual.Size); Assert.Equal(new Size(width, height), image.Size);
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(expected, actual);
} }
Assert.NotEmpty(allocator.AllocationLog); Assert.NotEmpty(allocator.AllocationLog);
@ -286,25 +286,63 @@ public class HeifDecoderTests
/// Verifies representative eight-bit subsampled and high-bit-depth full-resolution presentation through every /// Verifies representative eight-bit subsampled and high-bit-depth full-resolution presentation through every
/// available vector width and the scalar fallback. /// available vector width and the scalar fallback.
/// </summary> /// </summary>
[Fact] /// <param name="provider">The genuine HEIC input and matching reference-output naming context.</param>
public void DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths() [Theory]
[WithFile(TestImages.Heif.General8Bit420Heic, PixelTypes.Rgba32)]
[WithFile(TestImages.Heif.HighThroughputExtendedPrecision12Bit444Heic, PixelTypes.Rgba32)]
public void DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths(
TestImageProvider<Rgba32> provider)
=> FeatureTestRunner.RunWithHwIntrinsicsFeature( => FeatureTestRunner.RunWithHwIntrinsicsFeature(
ValidateHevcRangeExtensionPresentation, ValidateHevcRangeExtensionPresentation,
HevcPresentationConfigurations); HevcPresentationConfigurations,
provider);
/// <summary>
/// Verifies high-bit-depth HEVC presentation precision through every available vector width and the scalar fallback.
/// </summary>
/// <param name="provider">The genuine HEIC input and matching reference-output naming context.</param>
[Theory]
[WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba64)]
public void DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths(
TestImageProvider<Rgba64> provider)
{
using Image<Rgba64> image = provider.GetImage();
image.DebugSave(provider);
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
FeatureTestRunner.RunWithHwIntrinsicsFeature(
ValidateHevcHighBitDepthPresentation,
HevcPresentationConfigurations,
provider);
}
private static void ValidateHevcRangeExtensionPresentation() /// <summary>
/// Verifies one HEVC presentation with the feature-runner configuration and the repository reference API.
/// </summary>
/// <param name="providerDump">The serialized input provider and reference-output naming context.</param>
private static void ValidateHevcRangeExtensionPresentation(string providerDump)
{ {
string subsampledPath = TestImages.Heif.General8Bit420[..^4]; TestImageProvider<Rgba32> provider =
using Image<Rgba32> subsampledExpected = TestFile.Create($"{subsampledPath}-ffmpeg.png").CreateRgba32Image(); FeatureTestRunner.DeserializeForXunit<TestImageProvider<Rgba32>>(providerDump);
using Image<Rgba32> subsampledActual = TestFile.Create($"{subsampledPath}.heic").CreateRgba32Image();
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(subsampledExpected, subsampledActual); using Image<Rgba32> image = provider.GetImage();
image.DebugSave(provider);
string fullResolutionPath = TestImages.Heif.HighThroughputExtendedPrecision12Bit444[..^4]; image.CompareToReferenceOutput(ImageComparer.Exact, provider);
using Image<Rgba32> fullResolutionExpected = TestFile.Create($"{fullResolutionPath}-hm-ffmpeg.png").CreateRgba32Image(); }
using Image<Rgba32> fullResolutionActual = TestFile.Create($"{fullResolutionPath}.heic").CreateRgba32Image();
/// <summary>
/// Verifies one high-bit-depth HEVC presentation with the feature-runner configuration and the repository reference API.
/// </summary>
/// <param name="providerDump">The serialized input provider and reference-output naming context.</param>
private static void ValidateHevcHighBitDepthPresentation(string providerDump)
{
TestImageProvider<Rgba64> provider =
FeatureTestRunner.DeserializeForXunit<TestImageProvider<Rgba64>>(providerDump);
ImageComparer.TolerantPercentage(1F, 20).VerifySimilarity(fullResolutionExpected, fullResolutionActual); using Image<Rgba64> image = provider.GetImage();
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
} }
/// <summary> /// <summary>

74
tests/ImageSharp.Tests/TestImages.cs

@ -1304,12 +1304,28 @@ public static class TestImages
public const string General12Bit420 = "Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit"; public const string General12Bit420 = "Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit";
public const string General12Bit422 = "Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit"; public const string General12Bit422 = "Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit";
public const string General12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit"; public const string General12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit";
public const string General8BitMonochromeHeic = "Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.heic";
public const string General8Bit420Heic = "Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.heic";
public const string General8Bit444Heic = "Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.heic";
public const string General10Bit420Heic = "Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.heic";
public const string General10Bit422Heic = "Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.heic";
public const string General10Bit444Heic = "Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.heic";
public const string General12BitMonochromeHeic = "Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.heic";
public const string General12Bit420Heic = "Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.heic";
public const string General12Bit422Heic = "Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.heic";
public const string General12Bit444Heic = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.heic";
public const string RangeExtensionChromaAngle422 = "Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit"; public const string RangeExtensionChromaAngle422 = "Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit";
public const string RangeExtensionCrossComponent8Bit444 = "Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit"; public const string RangeExtensionCrossComponent8Bit444 = "Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit";
public const string RangeExtensionCrossComponent10Bit444 = "Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit"; public const string RangeExtensionCrossComponent10Bit444 = "Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit";
public const string RangeExtensionCrossComponent12Bit444 = "Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit"; public const string RangeExtensionCrossComponent12Bit444 = "Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit";
public const string RangeExtensionLuma12Chroma8 = "Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit"; public const string RangeExtensionLuma12Chroma8 = "Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit";
public const string RangeExtensionLuma8Chroma12 = "Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit"; public const string RangeExtensionLuma8Chroma12 = "Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit";
public const string RangeExtensionChromaAngle422Heic = "Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic";
public const string RangeExtensionCrossComponent8Bit444Heic = "Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.heic";
public const string RangeExtensionCrossComponent10Bit444Heic = "Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.heic";
public const string RangeExtensionCrossComponent12Bit444Heic = "Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.heic";
public const string RangeExtensionLuma12Chroma8Heic = "Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.heic";
public const string RangeExtensionLuma8Chroma12Heic = "Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.heic";
public const string ExtendedPrecision8Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit"; public const string ExtendedPrecision8Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit";
public const string ExtendedPrecision10Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit"; public const string ExtendedPrecision10Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit";
public const string HighThroughputExtendedPrecision8Bit444 = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit"; public const string HighThroughputExtendedPrecision8Bit444 = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit";
@ -1328,11 +1344,30 @@ public static class TestImages
public const string HighThroughput8Bit420Wavefront = "Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit"; public const string HighThroughput8Bit420Wavefront = "Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit";
public const string HighThroughput8Bit420CabacBypassAlignment = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit"; public const string HighThroughput8Bit420CabacBypassAlignment = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit";
public const string HighThroughput8Bit420ExtendedPrecision = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit"; public const string HighThroughput8Bit420ExtendedPrecision = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit";
public const string ExtendedPrecision8Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic";
public const string ExtendedPrecision10Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic";
public const string HighThroughputExtendedPrecision8Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic";
public const string HighThroughputExtendedPrecision10Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic";
public const string HighThroughputExtendedPrecision12Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.heic";
public const string RangeExtensionPcm10Bit422Heic = "Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.heic";
public const string RangeExtensionPcm12Bit444Heic = "Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.heic";
public const string PersistentRice12Bit444Heic = "Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic";
public const string TransformSkipContext8Bit444Heic = "Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.heic";
public const string TransformSkipContext10Bit444Heic = "Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.heic";
public const string TransformSkipContext12Bit444Heic = "Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.heic";
public const string Main42210AHeic = "Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.heic";
public const string Main42210BHeic = "Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.heic";
public const string HighThroughput10Bit422TilesWavefrontHeic = "Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic";
public const string HighThroughput8Bit420TilesWavefrontHeic = "Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic";
public const string HighThroughput8Bit420WavefrontHeic = "Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic";
public const string HighThroughput8Bit420CabacBypassAlignmentHeic = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic";
public const string HighThroughput8Bit420ExtendedPrecisionHeic = "Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic";
public const string IntraPredictionB = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit"; public const string IntraPredictionB = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit";
public const string IntraPredictionBReference = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv"; public const string IntraPredictionBReference = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv";
public const string ConstrainedIntraPredictionA = "Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit"; public const string ConstrainedIntraPredictionA = "Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit";
public const string DeltaQuantizationParameterA = "Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit"; public const string DeltaQuantizationParameterA = "Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit";
public const string ExtendedPrecision12Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit"; public const string ExtendedPrecision12Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit";
public const string ExtendedPrecision12Bit444Heic = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic";
public const string ChromaQuantizationAdjustment12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit"; public const string ChromaQuantizationAdjustment12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit";
public const string LosslessA = "Heif/Hevc/Conformance/LS_A_Orange_2.bit"; public const string LosslessA = "Heif/Hevc/Conformance/LS_A_Orange_2.bit";
public const string QuantizationMatrixA = "Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit"; public const string QuantizationMatrixA = "Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit";
@ -1413,38 +1448,27 @@ public static class TestImages
public const string Av1Progressive8BitAvif = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b.avif"; public const string Av1Progressive8BitAvif = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b.avif";
public const string Av1Progressive8BitPayload = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b.bit"; public const string Av1Progressive8BitPayload = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b.bit";
public const string Av1Progressive8BitReference = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b-libaom-y4m.yuv"; public const string Av1Progressive8BitReference = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b-libaom-y4m.yuv";
public const string Av1Progressive8BitPresentationReference = "Heif/Av1/Conformance/libavif-progressive-draw-points-8b.png";
public const string Av1ScaledReferenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.avif"; public const string Av1ScaledReferenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.avif";
public const string Av1ScaledReferenceSelectedLayerAvif = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-lsel0.avif"; public const string Av1ScaledReferenceSelectedLayerAvif = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-lsel0.avif";
public const string Av1ScaledReferencePayload = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.bit"; public const string Av1ScaledReferencePayload = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.bit";
public const string Av1ScaledReferenceBaseNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-base-libaom.yuv"; public const string Av1ScaledReferenceBaseNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-base-libaom.yuv";
public const string Av1ScaledReferenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-libaom.y4m"; public const string Av1ScaledReferenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-libaom.y4m";
public const string Av1ScaledReferencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.png";
public const string Av1ScaledReferenceSelectedLayerPresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-lsel0-libavif.png";
public const string Av1AverageCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-average-compound.avif"; public const string Av1AverageCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-average-compound.avif";
public const string Av1AverageCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-average-compound-libaom.y4m"; public const string Av1AverageCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-average-compound-libaom.y4m";
public const string Av1AverageCompoundSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-average-compound-libavif.png";
public const string Av1DistanceWeightedCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound.avif"; public const string Av1DistanceWeightedCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound.avif";
public const string Av1DistanceWeightedCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libaom.y4m"; public const string Av1DistanceWeightedCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libaom.y4m";
public const string Av1DistanceWeightedCompoundSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libavif.png";
public const string Av1WedgeCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-wedge-compound.avif"; public const string Av1WedgeCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-wedge-compound.avif";
public const string Av1WedgeCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-wedge-compound-libaom.y4m"; public const string Av1WedgeCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-wedge-compound-libaom.y4m";
public const string Av1WedgeCompoundSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-wedge-compound-libavif.png";
public const string Av1DifferenceWeightedCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound.avif"; public const string Av1DifferenceWeightedCompoundSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound.avif";
public const string Av1DifferenceWeightedCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound-libaom.y4m"; public const string Av1DifferenceWeightedCompoundSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound-libaom.y4m";
public const string Av1DifferenceWeightedCompoundSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound-libavif.png";
public const string Av1InterIntraSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-inter-intra.avif"; public const string Av1InterIntraSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-inter-intra.avif";
public const string Av1InterIntraSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-inter-intra-libaom.y4m"; public const string Av1InterIntraSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-inter-intra-libaom.y4m";
public const string Av1InterIntraSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-inter-intra-libavif.png";
public const string Av1ObmcSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-obmc.avif"; public const string Av1ObmcSequenceAvif = "Heif/Av1/Conformance/libavif-webp-logo-obmc.avif";
public const string Av1ObmcSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-obmc-libaom.y4m"; public const string Av1ObmcSequenceNativeReference = "Heif/Av1/Conformance/libavif-webp-logo-obmc-libaom.y4m";
public const string Av1ObmcSequencePresentationReference = "Heif/Av1/Conformance/libavif-webp-logo-obmc-libavif.png";
public const string Av1LocalWarpSequenceAvif = "Heif/Av1/Conformance/libavif-rotating-grid-local-warp.avif"; public const string Av1LocalWarpSequenceAvif = "Heif/Av1/Conformance/libavif-rotating-grid-local-warp.avif";
public const string Av1LocalWarpSequenceNativeReference = "Heif/Av1/Conformance/libavif-rotating-grid-local-warp-libaom.y4m"; public const string Av1LocalWarpSequenceNativeReference = "Heif/Av1/Conformance/libavif-rotating-grid-local-warp-libaom.y4m";
public const string Av1LocalWarpSequencePresentationReference = "Heif/Av1/Conformance/libavif-rotating-grid-local-warp-libavif.png";
public const string Av1GlobalWarpSequenceAvif = "Heif/Av1/Conformance/libavif-rotating-grid-global-warp.avif"; public const string Av1GlobalWarpSequenceAvif = "Heif/Av1/Conformance/libavif-rotating-grid-global-warp.avif";
public const string Av1GlobalWarpSequenceNativeReference = "Heif/Av1/Conformance/libavif-rotating-grid-global-warp-libaom.y4m"; public const string Av1GlobalWarpSequenceNativeReference = "Heif/Av1/Conformance/libavif-rotating-grid-global-warp-libaom.y4m";
public const string Av1GlobalWarpSequencePresentationReference = "Heif/Av1/Conformance/libavif-rotating-grid-global-warp-libavif.png";
public const string Av1OfficialAllIntraSequence = "Heif/Av1/Conformance/libaom-av1-1-b8-02-allintra.ivf"; public const string Av1OfficialAllIntraSequence = "Heif/Av1/Conformance/libaom-av1-1-b8-02-allintra.ivf";
public const string Av1OfficialAllIntraSequenceNativeReference = "Heif/Av1/Conformance/libaom-av1-1-b8-02-allintra-libaom.y4m"; public const string Av1OfficialAllIntraSequenceNativeReference = "Heif/Av1/Conformance/libaom-av1-1-b8-02-allintra-libaom.y4m";
public const string Av1OfficialEightBitMinimumQuantizerSequence = "Heif/Av1/Conformance/libaom-av1-1-b8-00-quantizer-00.ivf"; public const string Av1OfficialEightBitMinimumQuantizerSequence = "Heif/Av1/Conformance/libaom-av1-1-b8-00-quantizer-00.ivf";
@ -1488,97 +1512,69 @@ public static class TestImages
public const string Av1Cdef8BitPayload = "Heif/Av1/Conformance/libaom-cdef-kodim23-8b.bit"; public const string Av1Cdef8BitPayload = "Heif/Av1/Conformance/libaom-cdef-kodim23-8b.bit";
public const string Av1Cdef8BitReference = "Heif/Av1/Conformance/libaom-cdef-kodim23-8b-libaom.yuv"; public const string Av1Cdef8BitReference = "Heif/Av1/Conformance/libaom-cdef-kodim23-8b-libaom.yuv";
public const string Av1Cdef8BitAvif = "Heif/Av1/Conformance/libavif-cdef-kodim23-8b.avif"; public const string Av1Cdef8BitAvif = "Heif/Av1/Conformance/libavif-cdef-kodim23-8b.avif";
public const string Av1Cdef8BitPresentationReference = "Heif/Av1/Conformance/libavif-cdef-kodim23-8b.png";
public const string Av1Cdef10BitPayload = "Heif/Av1/Conformance/libaom-cdef-cosmos-10b.bit"; public const string Av1Cdef10BitPayload = "Heif/Av1/Conformance/libaom-cdef-cosmos-10b.bit";
public const string Av1Cdef10BitReference = "Heif/Av1/Conformance/libaom-cdef-cosmos-10b-libaom.yuv"; public const string Av1Cdef10BitReference = "Heif/Av1/Conformance/libaom-cdef-cosmos-10b-libaom.yuv";
public const string Av1Cdef10BitAvif = "Heif/Av1/Conformance/libavif-cdef-cosmos-10b.avif"; public const string Av1Cdef10BitAvif = "Heif/Av1/Conformance/libavif-cdef-cosmos-10b.avif";
public const string Av1Cdef10BitPresentationReference = "Heif/Av1/Conformance/libavif-cdef-cosmos-10b.png";
public const string Av1Cdef12BitPayload = "Heif/Av1/Conformance/libaom-cdef-cosmos-12b.bit"; public const string Av1Cdef12BitPayload = "Heif/Av1/Conformance/libaom-cdef-cosmos-12b.bit";
public const string Av1Cdef12BitReference = "Heif/Av1/Conformance/libaom-cdef-cosmos-12b-libaom.yuv"; public const string Av1Cdef12BitReference = "Heif/Av1/Conformance/libaom-cdef-cosmos-12b-libaom.yuv";
public const string Av1Cdef12BitAvif = "Heif/Av1/Conformance/libavif-cdef-cosmos-12b.avif"; public const string Av1Cdef12BitAvif = "Heif/Av1/Conformance/libavif-cdef-cosmos-12b.avif";
public const string Av1Cdef12BitPresentationReference = "Heif/Av1/Conformance/libavif-cdef-cosmos-12b.png";
public const string Av1Profile8BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-8b-400.avif"; public const string Av1Profile8BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-8b-400.avif";
public const string Av1Profile8BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv"; public const string Av1Profile8BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv";
public const string Av1Profile8BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-400.png";
public const string Av1Profile8Bit420Avif = "Heif/Av1/Conformance/libavif-profile-8b-420.avif"; public const string Av1Profile8Bit420Avif = "Heif/Av1/Conformance/libavif-profile-8b-420.avif";
public const string Av1Profile8Bit420Reference = "Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv"; public const string Av1Profile8Bit420Reference = "Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv";
public const string Av1Profile8Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-420.png";
public const string Av1Profile8Bit422Avif = "Heif/Av1/Conformance/libavif-profile-8b-422.avif"; public const string Av1Profile8Bit422Avif = "Heif/Av1/Conformance/libavif-profile-8b-422.avif";
public const string Av1Profile8Bit422Reference = "Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv"; public const string Av1Profile8Bit422Reference = "Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv";
public const string Av1Profile8Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-422.png";
public const string Av1Profile8Bit444Avif = "Heif/Av1/Conformance/libavif-profile-8b-444.avif"; public const string Av1Profile8Bit444Avif = "Heif/Av1/Conformance/libavif-profile-8b-444.avif";
public const string Av1Profile8Bit444Reference = "Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv"; public const string Av1Profile8Bit444Reference = "Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv";
public const string Av1Profile8Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-444.png";
public const string Av1Profile10BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-10b-400.avif"; public const string Av1Profile10BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-10b-400.avif";
public const string Av1Profile10BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv"; public const string Av1Profile10BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv";
public const string Av1Profile10BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-400.png";
public const string Av1Profile10Bit420Avif = "Heif/Av1/Conformance/libavif-profile-10b-420.avif"; public const string Av1Profile10Bit420Avif = "Heif/Av1/Conformance/libavif-profile-10b-420.avif";
public const string Av1Profile10Bit420Reference = "Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv"; public const string Av1Profile10Bit420Reference = "Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv";
public const string Av1Profile10Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-420.png";
public const string Av1Profile10Bit422Avif = "Heif/Av1/Conformance/libavif-profile-10b-422.avif"; public const string Av1Profile10Bit422Avif = "Heif/Av1/Conformance/libavif-profile-10b-422.avif";
public const string Av1Profile10Bit422Reference = "Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv"; public const string Av1Profile10Bit422Reference = "Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv";
public const string Av1Profile10Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-422.png";
public const string Av1Profile10Bit444Avif = "Heif/Av1/Conformance/libavif-profile-10b-444.avif"; public const string Av1Profile10Bit444Avif = "Heif/Av1/Conformance/libavif-profile-10b-444.avif";
public const string Av1Profile10Bit444Reference = "Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv"; public const string Av1Profile10Bit444Reference = "Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv";
public const string Av1Profile10Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-444.png";
public const string Av1Profile12BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-12b-400.avif"; public const string Av1Profile12BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-12b-400.avif";
public const string Av1Profile12BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv"; public const string Av1Profile12BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv";
public const string Av1Profile12BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-400.png";
public const string Av1Profile12Bit420Avif = "Heif/Av1/Conformance/libavif-profile-12b-420.avif"; public const string Av1Profile12Bit420Avif = "Heif/Av1/Conformance/libavif-profile-12b-420.avif";
public const string Av1Profile12Bit420Reference = "Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv"; public const string Av1Profile12Bit420Reference = "Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv";
public const string Av1Profile12Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-420.png";
public const string Av1Profile12Bit422Avif = "Heif/Av1/Conformance/libavif-profile-12b-422.avif"; public const string Av1Profile12Bit422Avif = "Heif/Av1/Conformance/libavif-profile-12b-422.avif";
public const string Av1Profile12Bit422Reference = "Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv"; public const string Av1Profile12Bit422Reference = "Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv";
public const string Av1Profile12Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-422.png";
public const string Av1Profile12Bit444Avif = "Heif/Av1/Conformance/libavif-profile-12b-444.avif"; public const string Av1Profile12Bit444Avif = "Heif/Av1/Conformance/libavif-profile-12b-444.avif";
public const string Av1Profile12Bit444Reference = "Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv"; public const string Av1Profile12Bit444Reference = "Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv";
public const string Av1Profile12Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-444.png";
public const string Av1Palette8BitPayload = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444.bit"; public const string Av1Palette8BitPayload = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444.bit";
public const string Av1Palette8BitReference = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444-libaom.yuv"; public const string Av1Palette8BitReference = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444-libaom.yuv";
public const string Av1Palette8BitAvif = "Heif/Av1/Conformance/libavif-palette-draw-points-8b.avif"; public const string Av1Palette8BitAvif = "Heif/Av1/Conformance/libavif-palette-draw-points-8b.avif";
public const string Av1Palette8BitPresentationReference = "Heif/Av1/Conformance/libavif-palette-draw-points-8b.png";
public const string Av1IntraBlockCopy8BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-8b-444.avif"; public const string Av1IntraBlockCopy8BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-8b-444.avif";
public const string Av1IntraBlockCopy8BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-8b-444-libaom-y4m.yuv"; public const string Av1IntraBlockCopy8BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-8b-444-libaom-y4m.yuv";
public const string Av1IntraBlockCopy8BitPresentationReference = "Heif/Av1/Conformance/libavif-intrabc-abc-8b-444.png";
public const string Av1IntraBlockCopy10BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-10b-444.avif"; public const string Av1IntraBlockCopy10BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-10b-444.avif";
public const string Av1IntraBlockCopy10BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-10b-444-libaom-y4m.yuv"; public const string Av1IntraBlockCopy10BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-10b-444-libaom-y4m.yuv";
public const string Av1IntraBlockCopy10BitPresentationReference = "Heif/Av1/Conformance/libavif-intrabc-abc-10b-444.png";
public const string Av1IntraBlockCopy12BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-12b-444.avif"; public const string Av1IntraBlockCopy12BitAvif = "Heif/Av1/Conformance/libavif-intrabc-abc-12b-444.avif";
public const string Av1IntraBlockCopy12BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-12b-444-libaom-y4m.yuv"; public const string Av1IntraBlockCopy12BitReference = "Heif/Av1/Conformance/libavif-intrabc-abc-12b-444-libaom-y4m.yuv";
public const string Av1IntraBlockCopy12BitPresentationReference = "Heif/Av1/Conformance/libavif-intrabc-abc-12b-444.png";
public const string Av1Lossless8BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-8b-444.avif"; public const string Av1Lossless8BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-8b-444.avif";
public const string Av1Lossless8BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-8b-444-libaom.yuv"; public const string Av1Lossless8BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-8b-444-libaom.yuv";
public const string Av1Lossless8BitPresentationReference = "Heif/Av1/Conformance/libavif-lossless-circle-8b-444.png";
public const string Av1Lossless10BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-10b-444.avif"; public const string Av1Lossless10BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-10b-444.avif";
public const string Av1Lossless10BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-10b-444-libaom.yuv"; public const string Av1Lossless10BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-10b-444-libaom.yuv";
public const string Av1Lossless10BitPresentationReference = "Heif/Av1/Conformance/libavif-lossless-circle-10b-444.png";
public const string Av1Lossless12BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-12b-444.avif"; public const string Av1Lossless12BitAvif = "Heif/Av1/Conformance/libavif-lossless-circle-12b-444.avif";
public const string Av1Lossless12BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-12b-444-libaom.yuv"; public const string Av1Lossless12BitReference = "Heif/Av1/Conformance/libavif-lossless-circle-12b-444-libaom.yuv";
public const string Av1Lossless12BitPresentationReference = "Heif/Av1/Conformance/libavif-lossless-circle-12b-444.png";
public const string Av1SuperResolution8BitPayload = "Heif/Av1/Conformance/libaom-superres-kodim23-8b.bit"; public const string Av1SuperResolution8BitPayload = "Heif/Av1/Conformance/libaom-superres-kodim23-8b.bit";
public const string Av1SuperResolution8BitReference = "Heif/Av1/Conformance/libaom-superres-kodim23-8b-libaom.yuv"; public const string Av1SuperResolution8BitReference = "Heif/Av1/Conformance/libaom-superres-kodim23-8b-libaom.yuv";
public const string Av1SuperResolution8BitAvif = "Heif/Av1/Conformance/libavif-superres-kodim23-8b.avif"; public const string Av1SuperResolution8BitAvif = "Heif/Av1/Conformance/libavif-superres-kodim23-8b.avif";
public const string Av1SuperResolution8BitPresentationReference = "Heif/Av1/Conformance/libavif-superres-kodim23-8b.png";
public const string Av1SuperResolution10BitPayload = "Heif/Av1/Conformance/libaom-superres-cosmos-10b.bit"; public const string Av1SuperResolution10BitPayload = "Heif/Av1/Conformance/libaom-superres-cosmos-10b.bit";
public const string Av1SuperResolution10BitReference = "Heif/Av1/Conformance/libaom-superres-cosmos-10b-libaom.yuv"; public const string Av1SuperResolution10BitReference = "Heif/Av1/Conformance/libaom-superres-cosmos-10b-libaom.yuv";
public const string Av1SuperResolution10BitAvif = "Heif/Av1/Conformance/libavif-superres-cosmos-10b.avif"; public const string Av1SuperResolution10BitAvif = "Heif/Av1/Conformance/libavif-superres-cosmos-10b.avif";
public const string Av1SuperResolution10BitPresentationReference = "Heif/Av1/Conformance/libavif-superres-cosmos-10b.png";
public const string Av1SuperResolution12BitPayload = "Heif/Av1/Conformance/libaom-superres-cosmos-12b.bit"; public const string Av1SuperResolution12BitPayload = "Heif/Av1/Conformance/libaom-superres-cosmos-12b.bit";
public const string Av1SuperResolution12BitReference = "Heif/Av1/Conformance/libaom-superres-cosmos-12b-libaom.yuv"; public const string Av1SuperResolution12BitReference = "Heif/Av1/Conformance/libaom-superres-cosmos-12b-libaom.yuv";
public const string Av1SuperResolution12BitAvif = "Heif/Av1/Conformance/libavif-superres-cosmos-12b.avif"; public const string Av1SuperResolution12BitAvif = "Heif/Av1/Conformance/libavif-superres-cosmos-12b.avif";
public const string Av1SuperResolution12BitPresentationReference = "Heif/Av1/Conformance/libavif-superres-cosmos-12b.png";
public const string Av1Restoration8BitPayload = "Heif/Av1/Conformance/libaom-restoration-kodim23-8b.bit"; public const string Av1Restoration8BitPayload = "Heif/Av1/Conformance/libaom-restoration-kodim23-8b.bit";
public const string Av1Restoration8BitReference = "Heif/Av1/Conformance/libaom-restoration-kodim23-8b-libaom.yuv"; public const string Av1Restoration8BitReference = "Heif/Av1/Conformance/libaom-restoration-kodim23-8b-libaom.yuv";
public const string Av1Restoration8BitAvif = "Heif/Av1/Conformance/libavif-restoration-kodim23-8b.avif"; public const string Av1Restoration8BitAvif = "Heif/Av1/Conformance/libavif-restoration-kodim23-8b.avif";
public const string Av1Restoration8BitPresentationReference = "Heif/Av1/Conformance/libavif-restoration-kodim23-8b.png";
public const string Av1Restoration10BitPayload = "Heif/Av1/Conformance/libaom-restoration-cosmos-10b.bit"; public const string Av1Restoration10BitPayload = "Heif/Av1/Conformance/libaom-restoration-cosmos-10b.bit";
public const string Av1Restoration10BitReference = "Heif/Av1/Conformance/libaom-restoration-cosmos-10b-libaom.yuv"; public const string Av1Restoration10BitReference = "Heif/Av1/Conformance/libaom-restoration-cosmos-10b-libaom.yuv";
public const string Av1Restoration10BitAvif = "Heif/Av1/Conformance/libavif-restoration-cosmos-10b.avif"; public const string Av1Restoration10BitAvif = "Heif/Av1/Conformance/libavif-restoration-cosmos-10b.avif";
public const string Av1Restoration10BitPresentationReference = "Heif/Av1/Conformance/libavif-restoration-cosmos-10b.png";
public const string Av1Restoration12BitPayload = "Heif/Av1/Conformance/libaom-restoration-cosmos-12b.bit"; public const string Av1Restoration12BitPayload = "Heif/Av1/Conformance/libaom-restoration-cosmos-12b.bit";
public const string Av1Restoration12BitReference = "Heif/Av1/Conformance/libaom-restoration-cosmos-12b-libaom.yuv"; public const string Av1Restoration12BitReference = "Heif/Av1/Conformance/libaom-restoration-cosmos-12b-libaom.yuv";
public const string Av1Restoration12BitAvif = "Heif/Av1/Conformance/libavif-restoration-cosmos-12b.avif"; public const string Av1Restoration12BitAvif = "Heif/Av1/Conformance/libavif-restoration-cosmos-12b.avif";
public const string Av1Restoration12BitPresentationReference = "Heif/Av1/Conformance/libavif-restoration-cosmos-12b.png";
public const string Av1RestorationSuperResolution8BitPayload = "Heif/Av1/Conformance/libaom-restoration-superres-kodim23-8b.bit"; public const string Av1RestorationSuperResolution8BitPayload = "Heif/Av1/Conformance/libaom-restoration-superres-kodim23-8b.bit";
public const string Av1RestorationSuperResolution8BitReference = "Heif/Av1/Conformance/libaom-restoration-superres-kodim23-8b-libaom.yuv"; public const string Av1RestorationSuperResolution8BitReference = "Heif/Av1/Conformance/libaom-restoration-superres-kodim23-8b-libaom.yuv";
public const string Av1RestorationSuperResolution10BitPayload = "Heif/Av1/Conformance/libaom-restoration-superres-abc-10b-422.bit"; public const string Av1RestorationSuperResolution10BitPayload = "Heif/Av1/Conformance/libaom-restoration-superres-abc-10b-422.bit";

0
tests/Images/Input/Heif/Av1/Conformance/libavif-lossless-circle-10b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-10b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-lossless-circle-12b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-12b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-lossless-circle-8b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeLosslessMatchesPinnedLibavifPresentation_Rgba32_libavif-lossless-circle-8b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-400.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-420.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-422.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-10b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-400.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-420.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-422.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-12b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-400.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-420.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-422.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProfileMatrixMatchesPinnedLibavifPresentation_Rgba32_libavif-profile-8b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-progressive-draw-points-8b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeProgressiveSingleReferenceMatchesPinnedReferences_Rgba32_libavif-progressive-draw-points-8b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-rotating-grid-global-warp-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifGlobalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-global-warp.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-rotating-grid-local-warp-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-local-warp.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-difference-weighted-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifObmcSequenceMatchesPinnedReferences_Rgba32_libavif-webp-logo-obmc.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-average-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequenceWithEqualAverageCompoundMatchesPinnedReferences_Rgba32_libavif-webp-logo-average-compound.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-difference-weighted-compound.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-obmc-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-distance-weighted-compound.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-inter-intra-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-inter-intra.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-wedge-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-wedge-compound.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-scaled-reference.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeScaledReferenceMatchesPinnedReferences_Rgba32_libavif-webp-logo-scaled-reference.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-scaled-reference-lsel0-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeSelectedProgressiveSpatialLayerMatchesPinnedReferences_Rgba32_libavif-webp-logo-scaled-reference-lsel0.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-cdef-cosmos-10b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-cosmos-10b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-cdef-cosmos-12b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-cosmos-12b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-cdef-kodim23-8b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithActiveCdefMatchesPinnedLibavifPresentation_Rgba32_libavif-cdef-kodim23-8b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-intrabc-abc-10b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-10b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-intrabc-abc-12b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-12b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-intrabc-abc-8b-444.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation_Rgba32_libavif-intrabc-abc-8b-444.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-restoration-cosmos-10b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-cosmos-10b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-restoration-cosmos-12b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-cosmos-12b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-restoration-kodim23-8b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithLoopRestorationMatchesPinnedLibavifPresentation_Rgba32_libavif-restoration-kodim23-8b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-palette-draw-points-8b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithPaletteMatchesPinnedLibavifPresentation_Rgba32_libavif-palette-draw-points-8b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-superres-cosmos-10b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-cosmos-10b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-superres-cosmos-12b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-cosmos-12b.png

0
tests/Images/Input/Heif/Av1/Conformance/libavif-superres-kodim23-8b.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeWithSuperResolutionMatchesPinnedLibavifPresentation_Rgba32_libavif-superres-kodim23-8b.png

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a
size 445507

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:42c898b491383da95bf01676a8e8f7aa221a171f4b211f08ce5fa6a1c313bfb1
size 254922

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5752cc38c38054018850157c1b11805cae28048a58f422389de7a37240af3593
size 259024

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb558d383a95d9dcbcf39637e7295f69881aa75b99134c84e98b02405849f956
size 2270060

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:988480de65a19b98f7df3d3298bd432beb324b7e7fd4a67370d37bba13526a53
size 599400

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:704acdf5dabe6d6550373d9f32216fdd0ff17549a6bafba870fab353a0138150
size 597546

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82e48bd7d67c6d6bf724f1dd4a07d4ba5ba3532ebab387bd152aca2e54354fa7
size 596229

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137
size 263001

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137
size 263001

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:490756396b21f4aeacc40634483f40c0c42f77d8f70005449e09682729f6e52a
size 83380

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5a974be508d2f5122ea7e7182da0c4b5313049b5229e1cd4fec77bb57c2b759f
size 249788

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b4e6e260ffe16dc728d0064cfa4852730426f14ed6ee16ae074272deee3cff9
size 157543

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b38aee5d910a934868a8ddba5756363ac2f6155c756912f310d2e1a7ef2ccf5
size 4683537

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5af1901f1bbd30d2dd98f437cad37b458f2b2defad5b0948959952daf7a48511
size 2422557

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8c77bdce5adbf111756bd6f07d5e8550384cb9327e5326dca6a704085a662757
size 5152549

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ccb446aec97dc88379861345dd6066b121341c63d0f5fb2ef9a4b5211266e795
size 952927

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8fbc99537fa7e121f41a217a196875f95053662ef3ff21c453b8c203e64eb2ba
size 255982

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b2eaee6ec269a2658d6a7e7e3be47d2cba8b2d158478606b40cd266efbecfd3
size 250386

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5b5d91da29e9ad97cfd64d0b32023e770f7035b51ca37da1825294928d3a2030
size 221441

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f93b25daf35a51c5d06e13260406d6206cc7fda532812a43c7c2c7fcdcee2ee8
size 337393

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60beaf4ceb7ee35e356225783e04734b8b5e364f93448f716f8ade10f92e2ac4
size 339585

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9f72db003768c465aae57bd4414cf0a7ca844ac0989a7065ba1f423e131b8a8
size 327991

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:52d44eea9a312d6dfc38ff0f52172e6ccd2aa7ee11ec4427c8dc22a223a12830
size 455775

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3951b586abcbcc5522f574cbeca8aeb4ab96e6da848b8b2a46823bb6fb51f43e
size 476713

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a
size 445507

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a06102b72c546060d1693244e5a9f6de9f5924a634e27450dbde189ca46da4f
size 159244

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e57731b800d6a7c0e34d3b07ab7ba5690f2b66141c2865c64dc3fdc5afecbcb7
size 247858

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b8b3953335f607c9691c626452dabdf68595017a7eeb94e9bee96c340bf9b0a
size 313388

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21614e3259ce3ff3565d729600e8aa268691f3450bc91eb2bfb0900864576cf3
size 5872698

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:007694be6991762d3b7637fa2b6215191a470888695d9fbc098b21058f66a949
size 10716965

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7d1d7749d15635514d824d7f52aeb92ac7933feaa98d2c59db9eaebbfd17b183
size 246199

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b525da1d3cbc0fede549913eed0f721baf85a4a2c2bc56a1a6d42e326b2ef9ef
size 6251753

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a96601049c2e6dc5808d30297a5c1fcc7060a3c0bae1ce0c13f7d4d8ae7c2b9e
size 10856528

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:de182fc4852448839fc9090ace49fe375401408aed131c632dcd9b28f42b3dcd
size 3486224

3
tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19a83def25a4deea548a1328c69fd8f1c771d32d5bf86c9571c5368cafad80c1
size 552593

4
tests/Images/Input/Heif/Av1/Conformance/README.md

@ -1,6 +1,6 @@
# AV1 reconstruction conformance fixtures # AV1 reconstruction conformance fixtures
These fixtures provide independent reference output for AV1 reconstruction and AVIF presentation tests. ImageSharp output is compared exactly with the retained native YUV planes and presented PNG files; the tests do not use a tolerance. These fixtures provide independent reference output for AV1 reconstruction and AVIF presentation tests. ImageSharp output is compared exactly with the retained native YUV planes and presented PNG files; the tests do not use a tolerance. Native fixtures remain beside their inputs here, while the PNG files use the repository reference-output naming contract under `tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests`.
## Provenance ## Provenance
@ -20,7 +20,7 @@ The reference builds use `AOM_TARGET_CPU=generic` and disable libyuv. Native rec
- `-libaom.yuv` files contain headerless planar Y, U, and V reference samples. Samples above eight bits are stored as little-endian 16-bit values. - `-libaom.yuv` files contain headerless planar Y, U, and V reference samples. Samples above eight bits are stored as little-endian 16-bit values.
- `-libaom-y4m.yuv` files retain the Y4M header together with the native planar frame. - `-libaom-y4m.yuv` files retain the Y4M header together with the native planar frame.
- `-libaom.y4m` files retain the Y4M header together with the native sequence frames selected for comparison. - `-libaom.y4m` files retain the Y4M header together with the native sequence frames selected for comparison.
- `.png` files contain the eight-bit RGBA presentation reference produced by the pinned scalar libavif build. - Reference-output `.png` files contain the eight-bit RGBA presentation produced by the pinned scalar libavif build. Their names combine the public test method, `Rgba32`, and the input AVIF basename so `CompareToReferenceOutput` resolves them directly.
## Coverage ## Coverage

3
tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0-ffmpeg.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b3d467add4b0a41575228af86011a4f0d003003df267c639ca7dab4a290997a2
size 2729225

3
tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0-ffmpeg.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d94be2ff029467ba3f0cc6e48c735966edf9c57424c0175fceed14a5d9434dbb
size 2427308

3
tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0-ffmpeg.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f27738eb42338db6db0ab3e8b392ac5407e51473ffceefd00f318b06b124d249
size 5167261

3
tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0-ffmpeg.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e4b107ab968f826a9d710be90255842af0315e475516b3b5cf3361dcd226fd81
size 953520

3
tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended-hm-ffmpeg.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf61f91cbc21d8172d4000dcffb2e5a6e1d56877cc354465548f00bf106b8923
size 255832

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save