From 78a74d448a0c8de9ef746d94a075f64b16623d95 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 30 Aug 2026 22:55:57 +1000 Subject: [PATCH] Remove unsupported patented codec implementation --- HEIF_IMPLEMENTATION_PLAN.md | 1134 +++-------- ImageSharp.sln | 6 - THIRD-PARTY-NOTICES.TXT | 252 --- .../HeifColorConversionParameters.cs | 2 +- ...b16Converter.LibheifCoefficientOperator.cs | 151 -- ...gb16Converter.LibheifMonochromeOperator.cs | 83 - .../HeifYuvToRgb16Converter.Operator.cs | 293 --- .../HeifYuvToRgb16Converter.Parameters.cs | 347 ---- .../HeifYuvToRgb16Converter.cs | 114 -- ...gb8Converter.LibheifCoefficientOperator.cs | 125 -- ...Rgb8Converter.LibheifMonochromeOperator.cs | 82 - .../HeifYuvToRgb8Converter.Parameters.cs | 281 +-- .../ColorConverters/HeifYuvToRgb8Converter.cs | 96 +- src/ImageSharp/Formats/Heif/Heif4CharCode.cs | 50 - src/ImageSharp/Formats/Heif/Heif4CharCode.tt | 10 - .../Formats/Heif/HeifCompressionFactory.cs | 2 - .../Formats/Heif/HeifCompressionMethod.cs | 5 - src/ImageSharp/Formats/Heif/HeifConstants.cs | 36 +- .../Formats/Heif/HeifDecoderCore.cs | 73 +- .../Formats/Heif/HeifEncoderCore.cs | 11 +- src/ImageSharp/Formats/Heif/HeifFileType.cs | 2 +- src/ImageSharp/Formats/Heif/HeifItem.cs | 7 - .../Formats/Heif/HeifSequenceParser.cs | 198 +- .../Formats/Heif/HeifSequenceTrack.cs | 6 - .../Heif/Hevc/Color/HevcPlanarSampleBuffer.cs | 81 - .../Heif/Hevc/Color/HevcYuvConverter.cs | 179 -- .../Formats/Heif/Hevc/HevcBitReader.cs | 230 --- .../Formats/Heif/Hevc/HevcCabacContext.cs | 88 - .../Formats/Heif/Hevc/HevcCabacContexts.cs | 319 --- .../Formats/Heif/Hevc/HevcCabacDecoder.cs | 396 ---- .../Heif/Hevc/HevcCabacSyntaxReader.cs | 575 ------ .../Heif/Hevc/HevcChromaSampleLocation.cs | 40 - .../Heif/Hevc/HevcCodecConfiguration.cs | 381 ---- .../Formats/Heif/Hevc/HevcCodedBlockFlags.cs | 53 - .../Formats/Heif/Hevc/HevcCodingTreeState.cs | 217 -- .../Hevc/HevcCoefficientCodingParameters.cs | 346 ---- .../Heif/Hevc/HevcCoefficientDecoder.cs | 422 ---- .../Heif/Hevc/HevcCoefficientScanOrder.cs | 150 -- .../Heif/Hevc/HevcCoefficientScanType.cs | 25 - ...DeblockingFilter.HorizontalEdgeOperator.cs | 64 - .../Hevc/HevcDeblockingFilter.Operator.cs | 70 - ...vcDeblockingFilter.VerticalEdgeOperator.cs | 65 - .../Formats/Heif/Hevc/HevcDeblockingFilter.cs | 470 ----- .../Formats/Heif/Hevc/HevcDeblockingState.cs | 131 -- .../Heif/Hevc/HevcImageItemBitstream.cs | 144 -- .../Heif/Hevc/HevcIntraPredictionMode.cs | 35 - .../Heif/Hevc/HevcIntraPredictionState.cs | 394 ---- .../HevcIntraPredictor.AngularOperator.cs | 346 ---- .../Hevc/HevcIntraPredictor.DcOperator.cs | 108 - .../Heif/Hevc/HevcIntraPredictor.Operator.cs | 39 - .../Hevc/HevcIntraPredictor.PlanarOperator.cs | 271 --- .../Hevc/HevcIntraPredictor.References.cs | 180 -- .../Formats/Heif/Hevc/HevcIntraPredictor.cs | 387 ---- .../Formats/Heif/Hevc/HevcInverseQuantizer.cs | 412 ---- ...rseTransformer.DiscreteCosine16Operator.cs | 49 - ...rseTransformer.DiscreteCosine32Operator.cs | 49 - ...erseTransformer.DiscreteCosine4Operator.cs | 49 - ...erseTransformer.DiscreteCosine8Operator.cs | 49 - ...nverseTransformer.DiscreteSine4Operator.cs | 42 - .../Hevc/HevcInverseTransformer.Operations.cs | 547 ----- .../Hevc/HevcInverseTransformer.Operator.cs | 34 - .../Heif/Hevc/HevcInverseTransformer.cs | 244 --- .../Formats/Heif/Hevc/HevcNalUnit.cs | 148 -- .../Formats/Heif/Hevc/HevcNalUnitHeader.cs | 78 - .../Heif/Hevc/HevcParameterSetSyntax.cs | 186 -- .../Formats/Heif/Hevc/HevcPictureBuffer.cs | 235 --- .../Hevc/HevcPictureDecoder.Deblocking.cs | 396 ---- .../Hevc/HevcPictureDecoder.Prediction.cs | 232 --- ...HevcPictureDecoder.SampleAdaptiveOffset.cs | 250 --- .../Hevc/HevcPictureDecoder.TransformTree.cs | 564 ------ .../Heif/Hevc/HevcPictureDecoder.Traversal.cs | 409 ---- .../Formats/Heif/Hevc/HevcPictureDecoder.cs | 363 ---- .../Heif/Hevc/HevcPictureParameterSet.cs | 558 ----- src/ImageSharp/Formats/Heif/Hevc/HevcPlane.cs | 25 - .../Formats/Heif/Hevc/HevcProfileTierLevel.cs | 115 -- .../Heif/Hevc/HevcQuantizationParameters.cs | 111 - .../Heif/Hevc/HevcReconstructionState.cs | 235 --- .../Formats/Heif/Hevc/HevcResidualDpcmMode.cs | 25 - ...structor.LeftShiftTransformSkipOperator.cs | 32 - .../HevcResidualReconstructor.Operator.cs | 47 - ...tructor.RightShiftTransformSkipOperator.cs | 35 - .../Heif/Hevc/HevcResidualReconstructor.cs | 578 ------ ...SampleAdaptiveOffsetFilter.BandOperator.cs | 51 - ...SampleAdaptiveOffsetFilter.EdgeOperator.cs | 68 - ...HevcSampleAdaptiveOffsetFilter.Operator.cs | 72 - .../Hevc/HevcSampleAdaptiveOffsetFilter.cs | 581 ------ .../HevcSampleAdaptiveOffsetParameters.cs | 464 ----- .../Formats/Heif/Hevc/HevcScalingList.cs | 352 ---- .../Heif/Hevc/HevcSequenceParameterSet.cs | 573 ------ .../Hevc/HevcShortTermReferencePictureSet.cs | 188 -- .../Heif/Hevc/HevcSliceSegmentHeader.cs | 494 ----- .../Formats/Heif/Hevc/HevcSliceType.cs | 25 - .../HevcSupplementalEnhancementInformation.cs | 349 ---- .../Formats/Heif/Hevc/HevcTileLayout.cs | 196 -- .../Hevc/HevcTransformComponentGeometry.cs | 59 - .../Heif/Hevc/HevcTransformUnitGeometry.cs | 158 -- .../Heif/Hevc/HevcVideoParameterSet.cs | 156 -- .../Hevc/HevcVideoUsabilityInformation.cs | 247 --- .../Formats/Heif/HevcHeifItemDecoder.cs | 337 --- src/ImageSharp/Formats/Heif/Readme.md | 4 +- .../Heif/HevcCoefficientScanBenchmarks.cs | 58 - .../Heif/HevcColorConversionBenchmarks.cs | 186 -- .../Heif/HevcIntraPredictionBenchmarks.cs | 116 -- ...HevcIntraReferencePreparationBenchmarks.cs | 154 -- .../Heif/HevcInverseQuantizationBenchmarks.cs | 105 - .../Heif/HevcInverseTransformBenchmarks.cs | 90 - .../HevcResidualReconstructionBenchmarks.cs | 91 - .../Formats/Heif/HeifDecoderTests.cs | 319 +-- .../Formats/Heif/HeifEncoderTests.cs | 2 +- .../Formats/Heif/HeifMetadataTests.cs | 45 - .../Formats/Heif/HeifSequenceParserTests.cs | 91 +- .../Heif/Hevc/HevcCabacDecoderTests.cs | 268 --- .../Heif/Hevc/HevcCoefficientDecoderTests.cs | 287 --- .../Heif/Hevc/HevcIntraPredictorTests.cs | 426 ---- .../Hevc/HevcIntraReferenceSamplesTests.cs | 228 --- .../Heif/Hevc/HevcInverseQuantizerTests.cs | 187 -- .../Heif/Hevc/HevcInverseTransformerTests.cs | 263 --- .../Heif/Hevc/HevcPictureDecoderTests.cs | 1799 ----------------- .../Hevc/HevcQuantizationParametersTests.cs | 82 - .../Hevc/HevcResidualReconstructorTests.cs | 301 --- .../Formats/Heif/Hevc/HevcTileLayoutTests.cs | 65 - .../Hevc/HevcTransformUnitGeometryTests.cs | 74 - .../Heif/Hevc/HevcYuvConverterTests.cs | 304 --- tests/ImageSharp.Tests/TestImages.cs | 126 -- ...ths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png | 3 - ...44_16_INTRA_10BIT_RExt_Sony_1_extended.png | 3 - ...444_16_INTRA_8BIT_RExt_Sony_1_extended.png | 3 - ..._HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png | 3 - ..._HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png | 3 - ..._HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png | 3 - ...E_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png | 3 - ...ion_Rgba64_GENERAL_12b_400_RExt_Sony_1.png | 3 - ...eif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png | 3 - ...heif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png | 3 - ...44_16_INTRA_12BIT_RExt_Sony_1_extended.png | 3 - ...dths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png | 3 - ...IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png | 3 - ...tion_Rgba32_CCP_10bit_RExt_QCOM_frame0.png | 3 - ...tion_Rgba32_CCP_12bit_RExt_QCOM_frame0.png | 3 - ...ation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png | 3 - ...44_16_INTRA_10BIT_RExt_Sony_1_extended.png | 3 - ...EC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png | 3 - ...444_16_INTRA_8BIT_RExt_Sony_1_extended.png | 3 - ...ion_Rgba32_GENERAL_10b_420_RExt_Sony_1.png | 3 - ...ion_Rgba32_GENERAL_10b_422_RExt_Sony_1.png | 3 - ...ion_Rgba32_GENERAL_10b_444_RExt_Sony_2.png | 3 - ...ion_Rgba32_GENERAL_12b_420_RExt_Sony_1.png | 3 - ...ion_Rgba32_GENERAL_12b_422_RExt_Sony_1.png | 3 - ...ion_Rgba32_GENERAL_12b_444_RExt_Sony_2.png | 3 - ...tion_Rgba32_GENERAL_8b_444_RExt_Sony_2.png | 3 - ...tation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png | 3 - ...entation_Rgba32_IPCM_B_RExt_NEC_frame0.png | 3 - ...gba32_Main_422_10_A_RExt_Sony_2_frame0.png | 3 - ...gba32_Main_422_10_B_RExt_Sony_2_frame0.png | 3 - ...32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png | 3 - ...ba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png | 3 - ...ba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png | 3 - ...gba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png | 3 - ...P_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png | 3 - ...evcStillImage_Rgba32_dwsample-heic-640.png | 3 - .../DecodeHevcStillImage_Rgba32_image1.png | 3 - .../DecodeHevcStillImage_Rgba32_image2.png | 3 - .../DecodeHevcStillImage_Rgba32_image3.png | 3 - .../DecodeHevcStillImage_Rgba32_image4.png | 3 - .../Decode_Rgba32_IMG-20230508-0053.png | 3 - ...IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit | 3 - ...PRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic | 3 - .../Bitdepth_A_RExt_Sony_1_frame0.bit | 3 - .../Bitdepth_A_RExt_Sony_1_frame0.heic | 3 - .../Bitdepth_B_RExt_Sony_1_frame0.bit | 3 - .../Bitdepth_B_RExt_Sony_1_frame0.heic | 3 - .../CCP_10bit_RExt_QCOM_frame0.bit | 3 - .../CCP_10bit_RExt_QCOM_frame0.heic | 3 - .../CCP_12bit_RExt_QCOM_frame0.bit | 3 - .../CCP_12bit_RExt_QCOM_frame0.heic | 3 - .../Conformance/CCP_8bit_RExt_QCOM_frame0.bit | 3 - .../CCP_8bit_RExt_QCOM_frame0.heic | 3 - .../Hevc/Conformance/CIP_A_Panasonic_3.bit | 3 - .../Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit | 3 - .../Heif/Hevc/Conformance/DBLK_A_SONY_3.bit | 3 - .../Conformance/DELTAQP_A_BRCM_4_frame0.bit | 3 - .../Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit | 3 - .../Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit | 3 - .../Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit | 3 - .../Hevc/Conformance/ENTP_A_Qualcomm_1.bit | 3 - .../Hevc/Conformance/ENTP_C_Qualcomm_1.bit | 3 - ...44_16_INTRA_10BIT_RExt_Sony_1_extended.bit | 3 - ...4_16_INTRA_10BIT_RExt_Sony_1_extended.heic | 3 - ...44_16_INTRA_12BIT_RExt_Sony_1_extended.bit | 3 - ...4_16_INTRA_12BIT_RExt_Sony_1_extended.heic | 3 - ...444_16_INTRA_8BIT_RExt_Sony_1_extended.bit | 3 - ...44_16_INTRA_8BIT_RExt_Sony_1_extended.heic | 3 - ...44_16_INTRA_10BIT_RExt_Sony_1_extended.bit | 3 - ...4_16_INTRA_10BIT_RExt_Sony_1_extended.heic | 3 - ...EC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit | 3 - ...C_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic | 3 - ...444_16_INTRA_8BIT_RExt_Sony_1_extended.bit | 3 - ...44_16_INTRA_8BIT_RExt_Sony_1_extended.heic | 3 - .../GENERAL_10b_420_RExt_Sony_1.bit | 3 - .../GENERAL_10b_420_RExt_Sony_1.heic | 3 - .../GENERAL_10b_420_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_10b_422_RExt_Sony_1.bit | 3 - .../GENERAL_10b_422_RExt_Sony_1.heic | 3 - .../GENERAL_10b_422_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_10b_444_RExt_Sony_2.bit | 3 - .../GENERAL_10b_444_RExt_Sony_2.heic | 3 - .../GENERAL_10b_444_RExt_Sony_2_frame0.yuv | 3 - .../GENERAL_12b_400_RExt_Sony_1.bit | 3 - .../GENERAL_12b_400_RExt_Sony_1.heic | 3 - .../GENERAL_12b_400_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_12b_420_RExt_Sony_1.bit | 3 - .../GENERAL_12b_420_RExt_Sony_1.heic | 3 - .../GENERAL_12b_420_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_12b_422_RExt_Sony_1.bit | 3 - .../GENERAL_12b_422_RExt_Sony_1.heic | 3 - .../GENERAL_12b_422_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_12b_444_RExt_Sony_2.bit | 3 - .../GENERAL_12b_444_RExt_Sony_2.heic | 3 - .../GENERAL_12b_444_RExt_Sony_2_frame0.yuv | 3 - .../GENERAL_12b_444_RExt_Sony_2_idr7.bit | 3 - .../GENERAL_8b_400_RExt_Sony_1.bit | 3 - .../GENERAL_8b_400_RExt_Sony_1.heic | 3 - .../GENERAL_8b_400_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_8b_420_RExt_Sony_1.bit | 3 - .../GENERAL_8b_420_RExt_Sony_1.heic | 3 - .../GENERAL_8b_420_RExt_Sony_1_frame0.yuv | 3 - .../GENERAL_8b_444_RExt_Sony_2.bit | 3 - .../GENERAL_8b_444_RExt_Sony_2.heic | 3 - .../GENERAL_8b_444_RExt_Sony_2_frame0.yuv | 3 - .../Conformance/IPCM_A_RExt_NEC_2_frame0.bit | 3 - .../Conformance/IPCM_A_RExt_NEC_2_frame0.heic | 3 - .../Conformance/IPCM_B_RExt_NEC_frame0.bit | 3 - .../Conformance/IPCM_B_RExt_NEC_frame0.heic | 3 - .../Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit | 3 - .../Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv | 3 - .../Heif/Hevc/Conformance/LS_A_Orange_2.bit | 3 - .../Main_422_10_A_RExt_Sony_2_frame0.bit | 3 - .../Main_422_10_A_RExt_Sony_2_frame0.heic | 3 - .../Main_422_10_B_RExt_Sony_2_frame0.bit | 3 - .../Main_422_10_B_RExt_Sony_2_frame0.heic | 3 - .../PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit | 3 - .../PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic | 3 - .../Conformance/QMATRIX_A_RExt_Sony_1.bit | 3 - .../Input/Heif/Hevc/Conformance/README.md | 113 -- .../Heif/Hevc/Conformance/RQT_A_HHI_4.bit | 3 - .../Heif/Hevc/Conformance/RQT_B_HHI_4.bit | 3 - .../Heif/Hevc/Conformance/RQT_C_HHI_4.bit | 3 - .../Heif/Hevc/Conformance/RQT_D_HHI_4.bit | 3 - .../Heif/Hevc/Conformance/RQT_E_HHI_4.bit | 3 - .../Hevc/Conformance/SAO_A_MediaTek_4.bit | 3 - .../Conformance/SAO_A_RExt_MediaTek_1.bit | 3 - .../Hevc/Conformance/STRUCT_A_Samsung_7.bit | 3 - .../Hevc/Conformance/STRUCT_B_Samsung_7.bit | 3 - .../Heif/Hevc/Conformance/TILES_A_Cisco_2.bit | 3 - .../Heif/Hevc/Conformance/TILES_B_Cisco_1.bit | 3 - .../TSCTX_10bit_I_RExt_SHARP_1_frame0.bit | 3 - .../TSCTX_10bit_I_RExt_SHARP_1_frame0.heic | 3 - .../TSCTX_12bit_I_RExt_SHARP_1_frame0.bit | 3 - .../TSCTX_12bit_I_RExt_SHARP_1_frame0.heic | 3 - .../TSCTX_8bit_I_RExt_SHARP_1_frame0.bit | 3 - .../TSCTX_8bit_I_RExt_SHARP_1_frame0.heic | 3 - .../Hevc/Conformance/TUSIZE_A_Samsung_1.bit | 3 - ..._HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit | 3 - ...HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic | 3 - ..._HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit | 3 - ...HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic | 3 - ..._HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit | 3 - ...HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic | 3 - ...E_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit | 3 - ..._HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic | 3 - .../Conformance/WPP_A_ericsson_MAIN_2.bit | 3 - .../Conformance/WPP_B_ericsson_MAIN_2.bit | 3 - .../Conformance/WPP_C_ericsson_MAIN_2.bit | 3 - .../Conformance/WPP_D_ericsson_MAIN_2.bit | 3 - .../Conformance/WPP_E_ericsson_MAIN_2.bit | 3 - .../Conformance/WPP_F_ericsson_MAIN_2.bit | 3 - ...P_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit | 3 - ..._HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic | 3 - .../Input/Heif/Hevc/dwsample-tile-hm.yuv | 3 - .../Images/Input/Heif/Hevc/dwsample-tile.hvc1 | 3 - .../Images/Input/Heif/Hevc/dwsample-tile.hvcc | 3 - .../Input/Heif/Hevc/image1-tile1-hm.yuv | 3 - .../Images/Input/Heif/Hevc/image1-tile1.hvc1 | 3 - .../Images/Input/Heif/Hevc/image1-tile1.hvcc | 3 - .../Input/Heif/Hevc/image1-tile2-hm.yuv | 3 - .../Images/Input/Heif/Hevc/image1-tile2.hvc1 | 3 - tests/Images/Input/Heif/Hevc/image2-tile.hvcc | 3 - .../Input/Heif/Hevc/image2-tile1-hm.yuv | 3 - .../Images/Input/Heif/Hevc/image2-tile1.hvc1 | 3 - .../Input/Heif/Hevc/image2-tile7-hm.yuv | 3 - .../Images/Input/Heif/Hevc/image2-tile7.hvc1 | 3 - tests/Images/Input/Heif/IMG-20230508-0053.hif | 3 - .../Images/Input/Heif/dwsample-heic-640.heic | 3 - tests/Images/Input/Heif/image1.heic | 3 - tests/Images/Input/Heif/image2.heic | 3 - tests/Images/Input/Heif/image3.heic | 3 - tests/Images/Input/Heif/image4.heic | 3 - 297 files changed, 281 insertions(+), 26058 deletions(-) delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/Color/HevcPlanarSampleBuffer.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCabacContext.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCabacDecoder.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCabacSyntaxReader.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcChromaSampleLocation.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCodedBlockFlags.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCodingTreeState.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientCodingParameters.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientDecoder.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanOrder.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanType.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.HorizontalEdgeOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.VerticalEdgeOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingState.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcImageItemBitstream.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionMode.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.AngularOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.DcOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.PlanarOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine16Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine32Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine4Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine8Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteSine4Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcNalUnit.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcNalUnitHeader.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcParameterSetSyntax.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Deblocking.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.SampleAdaptiveOffset.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPictureParameterSet.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcPlane.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcReconstructionState.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcResidualDpcmMode.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.LeftShiftTransformSkipOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.RightShiftTransformSkipOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.BandOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.EdgeOperator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.Operator.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetParameters.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcScalingList.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSequenceParameterSet.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcShortTermReferencePictureSet.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSliceType.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcSupplementalEnhancementInformation.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcTileLayout.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcTransformComponentGeometry.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcTransformUnitGeometry.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs delete mode 100644 src/ImageSharp/Formats/Heif/Hevc/HevcVideoUsabilityInformation.cs delete mode 100644 src/ImageSharp/Formats/Heif/HevcHeifItemDecoder.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcCoefficientScanBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcColorConversionBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraPredictionBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseQuantizationBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseTransformBenchmarks.cs delete mode 100644 tests/ImageSharp.Benchmarks/Codecs/Heif/HevcResidualReconstructionBenchmarks.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCabacDecoderTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCoefficientDecoderTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraPredictorTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseQuantizerTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseTransformerTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcResidualReconstructorTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTileLayoutTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTransformUnitGeometryTests.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcYuvConverterTests.cs delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_dwsample-heic-640.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image1.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image2.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image3.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image4.png delete mode 100644 tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_SONY_3.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2_frame0.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/README.md delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/SAO_A_MediaTek_4.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/SAO_A_RExt_MediaTek_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit delete mode 100644 tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic delete mode 100644 tests/Images/Input/Heif/Hevc/dwsample-tile-hm.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/dwsample-tile.hvc1 delete mode 100644 tests/Images/Input/Heif/Hevc/dwsample-tile.hvcc delete mode 100644 tests/Images/Input/Heif/Hevc/image1-tile1-hm.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/image1-tile1.hvc1 delete mode 100644 tests/Images/Input/Heif/Hevc/image1-tile1.hvcc delete mode 100644 tests/Images/Input/Heif/Hevc/image1-tile2-hm.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/image1-tile2.hvc1 delete mode 100644 tests/Images/Input/Heif/Hevc/image2-tile.hvcc delete mode 100644 tests/Images/Input/Heif/Hevc/image2-tile1-hm.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/image2-tile1.hvc1 delete mode 100644 tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv delete mode 100644 tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 delete mode 100644 tests/Images/Input/Heif/IMG-20230508-0053.hif delete mode 100644 tests/Images/Input/Heif/dwsample-heic-640.heic delete mode 100644 tests/Images/Input/Heif/image1.heic delete mode 100644 tests/Images/Input/Heif/image2.heic delete mode 100644 tests/Images/Input/Heif/image3.heic delete mode 100644 tests/Images/Input/Heif/image4.heic diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md index e2feec34a..b68cac8ab 100644 --- a/HEIF_IMPLEMENTATION_PLAN.md +++ b/HEIF_IMPLEMENTATION_PLAN.md @@ -1,914 +1,248 @@ -# HEIF family implementation plan +# AVIF and AV1 implementation plan ## Goal -Complete a production-quality, fully managed HEIF family image-format implementation for ImageSharp. The implementation must support still and animated HEIC, HIF/HEIF, and AVIF image files, interoperate with independent encoders and decoders, follow the existing ImageSharp architecture and code style, reuse existing ImageSharp infrastructure wherever its semantics match, and use SIMD for measured hot paths without maintaining a separate behavior model. +Complete a production-quality, fully managed AV1 codec and its bounded AVIF/HEIF image container integration for ImageSharp. The finished work must decode and encode still images and bounded image sequences, preserve source precision, use ImageSharp memory ownership, and provide SIMD-first hot paths with one behaviorally identical scalar fallback. -HEIF is the shared ISO BMFF-derived container. HEIC carries HEVC image items, AVIF carries AV1 image items, and `.hif`/`.heif` are container extensions whose payload codec must be determined from brands and item types rather than the filename. The completed implementation will support HEVC, AV1, and legacy JPEG image items. Other registered HEIF payload codecs must not be advertised unless they are implemented and independently verified. +This plan is the authoritative delivery checklist. A source file, unit test, build, self-roundtrip, or local implementation is not completion evidence by itself. -## Completion boundary +## Source authority -This plan has one PR completion gate. The phases below are dependency order and internal verification points only; none is a separately releasable or merge-complete subset. The PR is not complete until the complete matrix is implemented and independently verified. +- AV1 codec syntax, tables, fixed-point arithmetic, prediction, transforms, entropy behavior, filters, and lifecycle behavior must be ported and checked only against libaom commit 03087864cf4bea6abb0d28f95cf7843511413d8f. +- The AV1 specification is the normative behavioral description. It does not authorize copying an implementation from another codec library. +- Existing ImageSharp and JPEG code is the architecture, allocator, SIMD dispatch, pixel conversion, and test-API pattern. It is not an alternate AV1 algorithm source. +- Production code must not load, invoke, install, or fall back to a native codec. +- External artifacts may be retained only as test inputs or expected outputs with recorded provenance. They must never become an implementation source. -Full completion includes: +## Status notation -- [ ] XML documentation covers every type and contract in the HEIF implementation, including the AV1 and HEVC codec internals, and inline comments explain non-obvious container layouts, bitstream rules, numerical algorithms, SIMD choices, and interoperability constraints. -- [ ] HEIF, HEIC, and AVIF brands and item types are detected without relying on file extensions. -- [ ] HEVC/HEIC and AV1/AVIF decode and encode are complete for every image behavior included in the final scope. -- [ ] Legacy JPEG image-item decode and encode are standards compliant for HEIF/HIF files. -- [ ] Every bit depth and chroma format permitted by the exposed HEVC and AV1 profiles is supported, including 8, 10, and 12-bit and monochrome, YUV 4:2:0, 4:2:2, and 4:4:4 paths. -- [ ] Full- and limited-range conversion supports every valid signaled color-primary, transfer-characteristic, matrix-coefficient, and chroma-sample-position combination, including identity RGB signaling. -- [ ] Every normative AV1 compression tool permitted by the final image scope decodes without an unsupported branch or silently skipped reconstruction. -- [ ] The AV1 encoder implements real lossy and lossless compression with working quality and effort controls, complete mode decision, prediction, transform, quantization, entropy coding, and legal in-loop filter decisions. -- [ ] Every normative HEVC compression tool permitted by the final image scope decodes across the exposed profiles, including the range-extension tools required for high bit depth and 4:2:2/4:4:4. -- [ ] The HEVC encoder implements real lossy and lossless compression with working quality and effort controls, complete coding-tree, prediction, transform, quantization, CABAC, deblocking, and sample-adaptive-offset decisions. -- [ ] Primary images, alpha auxiliary images, image grids, ICC and CICP color information, Exif, XMP, pixel aspect ratio, clean aperture, rotation, and mirroring are complete. -- [ ] AVIF interoperability is independently verified with libavif and libaom, and HEIC interoperability is independently verified with a separately selected HEVC/HEIF implementation. +- [x] Verified: the current behavior has exact evidence from the pinned reference and the evidence proves the production contract. +- [~] Locally implemented, checkpoint open: production source exists, but current-tree verification is missing or a known audit issue invalidates the checkpoint. +- [ ] Remaining: the production behavior is absent, incomplete, or has not reached its required implementation boundary. -Checkboxes may be marked complete only when the implementation and the verification required by the corresponding phase exit gate are both recorded. Source presence, compilation, self-roundtrip, or a temporary vertical slice is not completion evidence. +## Current source reconciliation -## Delivery dashboard +Reconciled with the worktree on 2026-08-30. -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 `.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 `35e65e9546dd78847d4de7cae082e26229b3a744` closes the Phase 4 HEVC CABAC checkpoint without a production correction. Exact comparison with HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` confirms the context-initialization equation, all 178 retained intra-picture initialization values, both 128-entry probability-state transition tables, the 64-by-4 least-probable-symbol range table, the 32-entry normalization table, arithmetic decision and refill paths, ordinary and aligned bypass paths, termination, alignment validation, and PCM restart. Focused tests cover QP 0, 22, and 51 initialization, every reachable packed context state, both arithmetic outcomes, bypass, termination, invalid alignment, and PCM suspension/restart. A fresh Release run passes all 26 CABAC and production complete-slice cases without failures or skips: ten official Sony GENERAL Range Extensions first pictures match their exact native planes and published digests, five real HEIC item payloads match HM output exactly, and both constructor-failure and constrained-allocation ownership cases remain balanced. Roslynk reports zero compiler errors, scoped StyleCop and whitespace verification are clean, and `git diff --check` is clean. - -Commit `66d1f3c41b0baeaa98726b57d5feaeba50874f16` closes the Phase 4 HEVC coding-tree traversal checkpoint. Exact comparison with HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` covers boundary-forced coding-tree splits, split-context derivation, quantization-group QP references, intra partition-size syntax, PCM gating and CABAC restart, minimum and maximum transform-size split inference, chroma coded-block-flag inheritance including 4:2:2 sub-transform units, and first-residual ordering for luma and chroma QP updates. Official ITU RQT_A through RQT_E, STRUCT_A and STRUCT_B, and TUSIZE_A bitstreams match the archive's complete-sequence MD5 values through pinned HM; every RQT decoded-picture hash SEI also reports OK. Pinned-HM analyser output proves minimum-CU intra partition alternatives at 8, 16, and 32 samples, luma prediction sizes 4 through 64, coding-tree split syntax at 16 through 64, transform-subdivision syntax at 8 through 32, and luma transform blocks at 4 through 32, while TUSIZE_A covers 16- and 32-sample luma and 8-, 16-, and 32-sample chroma transforms. Eight production still-item tests compare the first picture's native Y, Cb, and Cr planes exactly with pinned HM. A fresh Release run passes all 150 focused HEVC decoder cases without failures or skips, the `net11.0` source build completes with zero warnings and errors, Roslynk reports zero compiler errors, scoped formatting and StyleCop verification are clean, and `git diff --check` is clean. - -Commit `9bc5b9842` closes the Phase 4 HEVC intra-prediction checkpoint without a production correction. Exact comparison with HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` confirms reference availability and substitution, normal three-tap and strong bilinear smoothing, filter-selection thresholds, planar, DC, and all angular equations, luma edge filters, negative-angle reference extension, 4:2:2 chroma-angle remapping, luma most-probable-mode syntax, chroma-mode syntax, and combined 4:4:4 NxN chroma traversal. The official one-picture `IPRED_B_Nokia_3` Main Still Picture stream exercises all 35 modes at every published luma and chroma conformance size, for 245 mode-and-size combinations, and its complete native YUV output matches the published reference exactly. The independently coded first picture from official `CIP_A_Panasonic_3` verifies the enabled constrained-intra PPS path and exact pinned-HM decoded-plane hashes; it does not claim coverage of constrained inter-neighbor exclusion in the following B picture, which is outside the bounded still decoder. `FeatureTestRunner` verifies the predictor oracle and both reference-filter paths through normal, AVX-512-disabled, AVX-disabled, and scalar execution. A fresh Release run passes all 149 focused HEVC decoder cases without failures or skips, both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, scoped StyleCop and whitespace verification are clean, and `git diff --check` is clean. - -Commit `97538abd29cb69df0ecf3e30a4ee3b40b586c070` closes the independently coded Phase 4 residual-reconstruction checkpoint. Five official HEVC v1 and Range Extensions pictures match the native Y, Cb, and Cr plane hashes produced by pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9`. The retained pictures cover coding-unit luma-QP deltas, scaling lists, transform skip, transform-skip contexts and rotation, implicit residual DPCM, extended-precision processing at 12-bit 4:4:4, coding-unit chroma-QP adjustment, and complete transform-and-quantization bypass. The `DELTAQP_A_BRCM_4` comparison exposed and corrected the PCM-neighbor invariant: PCM coding units skip intra-mode syntax but remain most-probable-mode neighbors, so the allocator-backed luma-mode map now starts at HM's required DC default. The five new exact-picture cases and all 169 focused HEVC decoder cases pass without failures or skips. Both Release source targets and the scoped Release test project build with zero warnings and errors, Roslynk reports zero compiler errors, the retained fixture hashes and existing LFS attributes match their recorded provenance without an attributes change, scoped whitespace verification is clean, and `git diff --check` is clean. Explicit inter residual-DPCM syntax remains unimplemented and is not claimed by this checkpoint. - -Commit `fcd16c862c437e3249c7e693012c717512985163` closes the bounded HEVC supplemental-presentation checkpoint. The item parser now consumes prefix-SEI messages in access-unit order, supports extended payload types and sizes, skips unknown payloads, rejects misplaced prefix messages and malformed trailing bits, and maps pinned-HM display orientation, no-display, mastering-display color volume, content light level, alternative transfer characteristics, ambient viewing environment, and content color volume into the bounded ImageSharp result. Codec orientation is applied after item scaling and auxiliary-alpha composition but before container crop, rotation, and mirroring, so a real public HEIC with alpha matches its independent reference PNG at every final RGBA sample after the injected transform. Metadata conflicts and no-display fail at the item boundary, while optional video timing, persistence, and post-processing messages without an ImageSharp still-image result or exposed metadata mapping remain intentionally ignored. `FeatureTestRunner` verifies normal and scalar presentation paths through an 8 KiB constrained tracked allocator with balanced exactly-once returns. All 195 focused HEVC cases pass without failures or skips, both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, scoped StyleCop and whitespace verification are clean, `.gitattributes` is unchanged, and `git diff --check` is clean. - -Commit `9d7348efd6496b54cef7ec27e42352641439c400` completes the independently coded HEVC Range Extensions native-plane profile and tool matrix. Twenty-five selected official H.265.1 pictures extend the existing ten Sony GENERAL pictures with Main RExt and High Throughput RExt coverage for cross-component prediction, unequal luma/chroma precision, persistent Rice adaptation, extended precision, PCM, transform-skip contexts, chroma-angle adjustment, coding-unit chroma-QP adjustment, tiles, wavefront entry points, and CABAC bypass alignment. Every selected Y, Cb, and Cr plane matches its normative decoded-picture-hash SEI or a first reference picture whose complete output matches the archive's published MD5. The matrix exposed and corrected PCM sample restoration from signaled PCM precision to reconstructed component precision and the chroma-deblocking rule that excludes slice and coding-unit chroma-QP adjustments from `tc` derivation; dead per-coding-unit deblocking-offset storage was removed. The exact 25-case matrix and all 220 focused HEVC cases pass in `net10.0` Release, both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, all 24 retained fixture SHA-256 values and staged LFS pointers match the documented existing attributes, `.gitattributes` is unchanged, and `git diff --check` is clean. This checkpoint proves native reconstruction for the independently coded Phase 4 scope; it does not prove the separate every-profile HEIC container-presentation exit gate. - -Commit `1c58d855f70b024170ced9eb0a7005f0f9c955ad` records the complete official motion-vector conformance checkpoint. The official IVF has SHA-1 `F064290D7FCD3B3DE19020E8AEC6C43C88D3A505`, matching the pinned libaom test-data manifest, and SHA-256 `222A9050059B254DAB17CFB802FF829C778E3F93AF18961A622C8268576C1395`; its pinned-libaom Y4M has SHA-256 `D97AC78C81782CF1507549368047769DC677DBE205706458D1EE9C807DE6EC78`. Both source targets build with zero warnings and errors, the `net10.0` test-project analyzer build completes with zero errors and 1,014 pre-existing repository warnings, Roslynk reports zero compiler errors, 3,985 focused `net10.0` cases pass without failures or skips, and `git diff --check` is clean. - -Commit `019ac5648b4380a6ff4865072c96fde29ce09fad` records the complete selected-spatial-layer presentation checkpoint. An essential `lsel` selecting the genuine 40x40 base layer of the committed 40x40-to-80x80 progressive AVIF returns that exact pinned-libaom native reconstruction, then scales its YUV444 planes to the 80x80 `ispe` extent before color conversion and matches pinned libavif's RGBA output exactly. Retained reconstructed planes remain at coded dimensions; presentation planes use group-safe allocator-owned rows and are returned exactly once under a 1,024-byte constrained allocator. Portable `Vector128` and scalar two-times filters match pinned libyuv edge, quarter-sample, and rounding results at 8 and 12 bits through `FeatureTestRunner`. Both source targets build with zero warnings and errors, the `net10.0` test-project analyzer build completes with zero errors and 1,014 pre-existing repository warnings, Roslynk reports zero compiler errors, 156 focused reconstruction and color cases pass without failures or skips, and `git diff --check` is clean. - -Commit `beb2ab86ce5e4052c667df30af8a48a860814031` records the complete progressive color and auxiliary-alpha conformance checkpoint. Both pinned progressive AVIF fixtures pass their exact native-plane and pinned-libavif presentation comparisons through normal dispatch, AVX-512-disabled, AVX-disabled, and scalar `FeatureTestRunner` configurations. The YUV444-alpha fixture also compares every composed alpha sample with the independent final native auxiliary plane. Both complete production presentation paths pass under a 1,024-byte constrained tracked allocator with every allocation returned exactly once. Both source targets build with zero warnings and errors, the `net10.0` test-project analyzer build completes with zero errors and 1,014 pre-existing repository warnings, Roslynk reports zero compiler errors, 156 focused reconstruction and color cases pass without failures or skips, and `git diff --check` is clean. - -Commit `ef93d584511055f8e91e8d801662c44a5b0984d9` records the complete twelve-bit inverse-transform arithmetic checkpoint. ADST4 retains pinned libaom's signed 32-bit sine products and factorized sums and widens only its terminal rounding; Identity4 and Identity16 widen their fixed-point product and rounding bias only for the 20-bit twelve-bit row stage. The established 8/10-bit and twelve-bit column paths remain unchanged. Exact boundary vectors cover both 128-bit and 256-bit operators through normal, AVX-512-disabled, AVX-disabled, and scalar `FeatureTestRunner` configurations. Both source targets build with zero warnings and errors, the `net10.0` test-project analyzer build completes with zero errors and 1,014 pre-existing repository warnings, Roslynk reports zero compiler errors, 510 focused inverse-transform cases and 44 production reconstruction cases pass without failures or skips, and `git diff --check` is clean. - -Commit `5ba048be3667064f219ee230be7c8976927ea200` records the complete, verified official syntax-coverage and predictor-architecture checkpoint. SharedInfrastructure commit `a835a9d74e82b2d32b580a7902eb2699ebc47098` is pushed to `origin/main`; the ImageSharp gitlink and synchronized root attributes assign IVF and Y4M fixtures to Git LFS. The official all-intra, CDF-update, and temporal motion-field IVF files have SHA-1 `A9F7EA6312A533CC6426A6145EDD190D45813C37`, `AFCA5502A489692B0A3C120370B0F43B8FC572A1`, and `B48A717C7C003B8DD23C3C2CAED1AC673380FDB3`, exactly matching the pinned libaom manifest. Their SHA-256 values are `5FCD265FD9F9BDD0D3179340B4C4532F1422CA5E5D97741C7481B84CB5DC122F`, `14A3DBF537B6BF15EFC003182D9916D61438C93624A8BD26E6E3AE7EAF33EA82`, and `B59BF9586D8546DFDA81DFEC4EE4E32CEB502C9D22412AB0B63A2ABB534A1F14`; the pinned-libaom Y4M references have SHA-256 `1211EBEFBC9CCEF9ED19BE4CCE3F807D69FFFE338E95CCA1B5F4CA8023482175`, `4FBFF73FF0DE2D9084DAE557D1D4BD677B0486516525BF4D327D2D795D5A7779`, and `F7DB607694818C19E62FD9A27F53E1A3E2D00B72C39C0430C1B26399CC76777D`. Exact native-plane comparison covers all 39 all-intra frames, every intra mode, seven selected transform types, both tile-local and frame-end adaptive CDF publication, all four temporal motion-field frames under normal/scalar dispatch, and constrained tracked motion-field allocation with exactly-once returns. Every distinct AV1 predictor traversal now owns a family-named JPEG-style static operator contract and SIMD traversal instead of nesting separate predictors beneath broad intra/inter families. Compound reference convolution, equal averaging, distance weighting, alpha-mask blending, difference-weighted mask construction, and each intermediate reconstruction or mask traversal have separate family owners and matching `.Operator.cs` contracts; inter-intra mask construction has its own non-operator builder. Filtered intra-block-copy horizontal, vertical, and bilinear phases own separate family-named types, tap-exact static operator contracts, and family-owned SIMD/scalar traversals; the broad dispatcher retains only whole-sample copy and phase routing. Both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, 102 final-tree focused predictor and reconstruction cases pass without failures or skips, and `git diff --check` is clean. - -Commit `ea2fa484716944b86fadac5eac3eef23874ac25b` records the complete official extreme-displacement intra-block-copy checkpoint. The two-frame 1920x1080 YUV420 IVF has SHA-1 `C7F336958E7AF6162C20DDC84D67C7DFA9826910`, exactly matching the pinned libaom manifest, and SHA-256 `0252716FE7A7C7F1EBB1EB23920A6737F45C10DFBA942304BD339AE42B5C576C`; its pinned-libaom Y4M has SHA-256 `8CF7851E112172C62799F8BFDF895505742A3CA77E339C12E547AD2C47AF41B3`. The source corrects the spatial scan's four-mode-info-unit step threshold from 16-sample blocks to libaom's 64-sample boundary, clamps the ranked displacement stack before reference selection, and treats intra-block-copy neighbors as inter for transform-size context. Exact production native-plane comparison covers both complete official frames under normal and scalar `FeatureTestRunner` dispatch, while the existing independent 8/10/12-bit native-plane and presentation matrix remains clean. Both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, 170 focused intra-block-copy, frame-buffer, reference-MV, inter-mode, and transform-size tests pass without failures or skips, and `git diff --check` is clean. - -Commit `27ebcd54dc732aff8c665ee1ab61e72ed9621b42` records the official two-spatial-layer L2T1 default-operating-point checkpoint. The manifest-matching 159,037-byte IVF and pinned-libaom Y4M prove all eight 1280x720 YUV420 frames exactly under normal and scalar dispatch; constrained tracked allocation verifies balanced exactly-once returns. The existing decoder required no production correction. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, all five focused compact-sequence cases pass without failures or skips, and `git diff --check` is clean. - -Commit `a5e4cc6cc6cf87ee02b41cfe4ca71d1ef3411ed7` records the official one-spatial-layer, two-temporal-layer L1T2 checkpoint. The manifest-matching 48,883-byte IVF and pinned-libaom Y4M prove all eight 640x360 YUV420 frames exactly under normal and scalar dispatch; constrained tracked allocation verifies balanced exactly-once returns. The existing decoder required no production correction. The scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, both focused cases pass without failures or skips, and `git diff --check` is clean. - -Commit `03737411e62155df8e998c37475ceeebf4f053bf` records the official two-spatial-layer, two-temporal-layer L2T2 checkpoint. The manifest-matching 119,907-byte IVF and pinned-libaom Y4M prove all eight 1280x720 YUV420 frames exactly under normal and scalar dispatch; constrained tracked allocation verifies balanced exactly-once returns. The existing decoder required no production correction. The scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, both focused cases pass without failures or skips, and `git diff --check` is clean. - -Commit `15613b01c0bc098fd9926b886216558e0d26eff9` records the official ten-frame active-film-grain checkpoint. The manifest-matching 28,634-byte IVF and pinned-libaom grained Y4M prove every displayed 352x288 YUV420 frame exactly under normal and scalar dispatch; the differing skip-grain digest and explicit coverage prove synthesis is active. The checkpoint corrected the AV1 default loop-filter reference-delta order so Golden is -1 and Backward is 0, restoring exact dependent-frame deblocking before display-only grain. Constrained tracked allocation returns every allocation exactly once. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, 28 focused cases pass without failures or skips, and `git diff --check` is clean. - -Commit `da9a46b3d72fd682ea9293af3f88fde26defb222` records the official eight-bit monochrome checkpoint. The manifest-matching 10,000-byte IVF and pinned-libaom raw luma reference prove all ten displayed 320x180 YUV400 frames exactly under normal and scalar dispatch. The shared compact-sequence oracle validates native monochrome without manufacturing chroma and keeps every prior YUV420 fixture exact. Constrained tracked allocation returns every allocation exactly once. The existing decoder required no production correction. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, all 17 official-sequence cases pass without failures or skips, and `git diff --check` is clean. - -Commit `e6f0ef84fc8273a7d50fdfc1489f54f058a2a424` records the OBU framing verification checkpoint. The bounded parser was re-audited against pinned libaom across length fields, operating points, header ordering, padding, alignment, tile-group boundaries, tile sizes, and ordered coverage. Corrected test oracles now address the actual item-relative tile bytes and avoid reflection invocation of span-valued properties. Both Release source builds remain clean, the scoped analyzer build and Roslynk report zero errors, all 119 focused cases pass, and `git diff --check` is clean. - -Commit `fc9320262cc08dd5daf41f231998403b804b98da` closes the tile, mode, segmentation, delta-quantization, transform, coefficient, and inverse-quantization checkpoint. The production traversal and entropy order were re-audited against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. The stale coefficient round-trip oracle now compares the parser's retained entropy-scan order rather than raster order and disposes its encoded buffer owner. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, all 947 focused cases pass without failures or skips, documentation review is complete, and `git diff --check` is clean. - -Commit `42384454eb45b6107608ed33379c7a69b101d533` closes the AV1 intra-prediction checkpoint. Pinned-source review confirms conventional mode and angle syntax, palette and filter-intra eligibility and ordering, chroma-from-luma eligibility and alpha mapping, reference availability, edge preparation, and predictor dispatch. Every predictor family retains the JPEG-style static operator architecture. All 93 focused cases pass without failures or skips, including the exact 39-frame official all-intra native sequence, exact palette native and presentation fixtures, and the exact 12-profile matrix. The existing production implementation required no correction. - -Commit `6b3a5a5e1849de7881b929f46a5744fa3efe3fd6` closes the AV1 inter-frame prediction parent. Every completed reference-state, syntax, motion-derivation, translational, compound, inter-intra, OBMC, scaled, local-warp, global-warp, layered-presentation, film-grain, and monochrome child remains connected to the production path. All 342 focused inter-frame cases pass without failures or skips, including exact independent native-plane and presentation comparisons, normal and scalar dispatch, constrained allocation, and exactly-once ownership evidence. The existing production implementation required no correction. - -Status meanings: - -- **Complete:** the implementation and its phase exit evidence are recorded. -- **In progress:** usable implementation exists, but one or more required behaviors or verification gates remain open. -- **Implemented locally; unverified:** source exists in the working tree, but its checkbox remains open until the required Release and independent evidence pass. -- **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 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 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 | -| --- | --- | --- | --- | --- | -| 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. | -| 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 | **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. | -| 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. | -| 8 | Hardening and release readiness | Not started | Focused tests, independent fixtures, and representative SIMD benchmarks exist for completed slices. | Close the complete verification matrix, fuzz and malformed-input coverage, all Release/API/package checks, documentation and notices, and remove every placeholder or unsupported advertised path. | +- [~] The bounded container reader, still-image path, sequence parser, AV1 decoder, color pipeline, presentation pipeline, and broad AV1 test suite exist locally. +- [~] The inter-frame decoder contains implementations for single-reference prediction, compound references, inter-intra prediction, selectable compound blending, OBMC, scaled references, local warped motion, and global motion. These downstream paths must not be called verified until the single-reference checkpoint below is corrected and rerun. +- [~] Loop filtering, CDEF, super-resolution, restoration, film grain, layered presentation, alpha composition, and color conversion exist locally. Shared-source cleanup changed the current tree, so final production-path verification is open. +- [~] AV1 writer primitives, forward transforms, symbol encoding, and tile-writing source exist locally, but they are not connected to the public encoder. +- [ ] The public AV1 encoder is not implemented. HeifEncoderCore.Encode throws NotSupportedException when AV1 is selected. +- [x] Removed codec production code, registrations, tests, benchmarks, fixtures, reference outputs, downloaded tools, downloaded source trees, and notices have been manually deleted and verified by the cleanup evidence below. +- [ ] The complete decoder and encoder release matrix is not complete. ## Immediate execution queue -- [x] Finish the libaom-shaped AV1 forward-transform architecture, measured production dispatch, inverse-tier correction, suffix cleanup, `FeatureTestRunner` matrix, and focused Release verification recorded below. -- [x] Close the base AV1 profile matrix with exact native-plane and presented-image comparisons for 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 fixtures under normal, AVX2, 128-bit, and scalar dispatch. -- [x] Accept the AV1-ISOBMFF final low-overhead OBU form that omits its payload-size field and uses the bounded image-item remainder; focused Release coverage reconstructs a valid combined frame in that form. -- [x] Complete layered AV1 image-item dependency reconstruction and verify default final-layer output against the pinned libavif progressive fixtures. - - [x] Parse and associate `a1op`, `lsel`, and `a1lx` through the bounded image-item property model, including normative essential flags, duplicate handling, exact property lengths, and the four-layer limit. - - [x] Validate `a1lx` layer boundaries against the logical item size and restrict concrete `lsel` decoding to the cumulative payload through the selected spatial layer without copying item bytes. - - [x] Apply the selected `a1op` operating-point mask while consuming extended OBUs and validate the selected index against the parsed sequence header. - - [x] Store the eight fixed reference-validity, order-hint, and map-index tables inline on the frame header, retaining complete multi-bit order hints without per-header array allocations. - - [x] Enforce the bounded frame lifecycle across temporal delimiters, frame headers, tile coverage, and payload completion, publishing parser reference state only after successful decoder completion. The real two-layer libavif-derived lifecycle fixture verifies frame completion, retained-slot occupancy, reference resolution, and inherited dimensions through a fake tile lifecycle; it does not verify inter reconstruction or pixels. - - [x] Retain each successfully reconstructed reference as the sole owner of its ungrained sample planes, frame header, frame information, and published entropy snapshot; refresh only the selected slots, extend reference borders once after in-loop filtering, preserve a separate grained presentation buffer when required, and reset the store at an accepted sequence boundary. - - [x] Parse full and short reference signaling against retained-slot occupancy separately from frame-ID validity, resolve the primary-reference slot, implement `frame_size_with_refs`, and read high-precision-motion-vector, interpolation-filter, switchable-motion-mode, and reference-frame-motion-vector flags. - - [x] Initialize each frame's working CDF graph from the resolved primary snapshot, inherit segmentation feature data and an unchanged contiguous segmentation map, inherit loop-filter delta state, parse and inherit global-motion parameters, and initialize/project the retained per-8x8 temporal motion field. - - [x] Match libaom's bounded range-decoder consumed-bit accounting and tile trailing-bit validation so implicit zero refill cannot conceal truncated entropy data. Validate before publishing the selected CDF, reset decoder state after failure, and unwind every successful frame/block workspace rent when a later constructor allocation fails. A real truncated palette tile, decoder reuse, parser lifecycle, and allocator-identity tests cover these boundaries. - - [x] Consume the retained segmentation map through `seg_id_predicted` when temporal segmentation is enabled, including all three neighbor CDF contexts, pre-skip/post-skip ordering, skipped-block spatial inference, compatible retained-map geometry, and current-map updates. - - [x] Implement the complete inter-frame entropy, mode, motion-vector, compound-prediction, inter-prediction, and warped/global-motion paths permitted by the image profile. - - [x] Derive the frame-level skip-mode reference pair from mapped order hints, including modulo wraparound and the two-forward fallback, then decode the common inter-frame block prefix and intra-coded-block branch with retained CDF state and block-size luma contexts. - - [x] Implement allocation-free SIMD-first translational single-reference interpolation for regular, smooth, sharp, and bilinear filters across 8/10/12-bit samples. The predictor mirrors JPEG's closed static operator architecture, descends through `Vector512`, `Vector256`, and `Vector128` before scalar fallback, and passes the exact independent convolution oracle through `FeatureTestRunner`. - - [x] Close the single-reference inter path before advancing to another mode. - - [x] Decode single-reference selection and own the exact adaptive reference and inter-mode distributions in the frame entropy context. - - [x] Derive the fixed-capacity spatial and temporal reference-MV stack with normative ordering, precision, clamping, global fallback, and DRL selection without per-block allocation. - - [x] Audit the disputed interpolation omission against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`: `av1_is_interp_needed()` calls `is_nontrans_global_motion()`, whose global-motion-type test returns false only for `TRANSLATION`. An identity `GLOBALMV` block of sufficient size therefore omits switchable-filter symbols, matching the current `!= Translation` predicate. The separate `is_global_mv_block()` `> Translation` test governs a different global-motion classification. - - [x] Audit the disputed spatial extension bound against the same pinned libaom: both loops in `setup_ref_mv_list()` stop at `MAX_MV_REF_CANDIDATES`, which is two. `MAX_REF_MV_STACK_SIZE`, which is eight, is the total stack capacity used by other candidate paths. The current spatial-loop bound matches the pinned source. - - [x] Decode NEW motion-vector differences with the independent normal-motion-vector entropy context and validate the final component range. - - [x] Decode or infer both directional interpolation filters after finalized motion-mode syntax, including fixed, switchable, dual-filter, and no-symbol paths. - - [x] Reconstruct one complete single-reference block through the retained padded plane and existing SIMD-first predictor before transform-unit residual traversal. - - [x] Consume inter-intra and motion-mode syntax in normative order when those modes are not selected, including the exact `Block8x8` through `Block32x32` inter-intra enum interval and binary/ternary motion-mode CDF selection. - - [x] Use counted frame/reference ownership for allocator-owned retained and temporal motion fields, with allocation tracking for initialization, retained-slot aliases, failure unwinding, presentation ownership, decoder-result ownership, and exactly-once final disposal. - - [x] Request the existing contiguous ImageSharp allocation contract for every padded AV1 frame plane. Constrained-allocator coverage verifies complete-plane block reconstruction without copying or per-block allocation. - - [x] Complete source review of the new motion-mode and single-reference tests. - - [x] Pass the exact single-reference checkpoint's Release verification: `net10.0` and `net11.0` source builds complete with zero warnings and errors; the `net10.0` test-project analyzer build completes with zero errors and pre-existing repository warnings; 293 focused `net10.0` entropy, candidate, motion, interpolation, lifecycle, reconstruction, ownership, and `FeatureTestRunner` cases pass with zero failures or skips; and `git diff --check` is clean. - - [x] Correct the progressive dependent-frame extraction and compare the final frame's first three native planes with pinned libaom output and its final RGBA presentation with pinned libavif exactly. The unmodified AVIF has the recorded SHA-256 and stores the primary color item's 55-byte base extent at offset 511 and 17-byte dependent extent at offset 583. The corrected logical `.bit` payload decodes as two YUV444 frames with pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`; the retained two-frame YUV444-alpha reference and final PNG come from pinned libavif linked to that build. The production test selects the second native frame, requires inter-coded blocks, and passes exact native and presentation comparisons through `FeatureTestRunner`. - - [x] Decode all compound reference-pair trees and inter modes, retain paired spatial/temporal reference-MV candidates through DRL selection, and reconstruct two translational predictors with allocation-free equal-weight averaging across 8/10/12-bit samples without changing the single-reference predictor contract or rounding model. - - [x] Verify the equal-average production branch with the pinned 19-frame libavif YUV444 sequence. The final native Y, U, and V planes match pinned libaom exactly, final RGBA presentation matches pinned libavif exactly, production `DecodeBlock()` is covered at 8/10/12 bit, constrained plane allocation passes, and allocator tracking returns every retained and temporal motion-field buffer exactly once. - - [x] Decode selected inter-intra prediction and selectable distance-weighted, wedge, and difference-weighted compound blending. Normative-order syntax, direct 8/10/12-bit production reconstruction, SIMD/scalar arithmetic, four pinned bounded sequences, exact final native planes and presentation, constrained contiguous planes, and balanced allocator returns pass. Both source targets build with zero warnings and errors; the test project builds with zero errors and pre-existing repository warnings; 25 direct focused cases and six `FeatureTestRunner` cases pass without failures or skips; and `git diff --check` is clean. - - [x] Decode and reconstruct above and left OBMC through allocator-owned prediction workspaces, exact pinned masks, and the existing SIMD-first masked blender. Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` records direct 8/10/12-bit production reconstruction, 4:2:0 and 4:2:2 overlap geometry, a real 19-frame YUV444 sequence containing more than one hundred traced OBMC blocks, exact final pinned-libaom native planes, exact pinned-libavif presentation through normal and scalar `FeatureTestRunner` configurations, constrained tracked allocation, zero-warning `net10.0` and `net11.0` source builds, a zero-error analyzer build with 1,013 pre-existing warnings, 14 direct focused cases plus the feature-isolated comparison, and clean staged `git diff --check`. - - [x] Decode and independently verify scaled-reference prediction through exact Q14/Q10 arithmetic, allocation-free SIMD/scalar convolution, 8/10/12-bit production reconstruction, scaled OBMC, active inter deblocking, constrained allocation, and a genuine size-changing bounded AVIF. - - [x] Decode local warped motion through pinned integer projection and allocation-free SIMD-first affine filtering. A genuine two-frame bounded AVIF selects traced multi-sample warped blocks and passes exact final native-plane and presentation comparisons, constrained allocation, and normal/scalar dispatch. Commit `0b2db86809e26757460e719a6b59c0fe58bb0586` records the checkpoint. - - [x] Decode non-translational global motion through the committed affine filter. A genuine two-frame bounded AVIF selects seven traced rotation/zoom `GLOBALMV` blocks and passes exact final native-plane and presentation comparisons, constrained allocation, and normal/scalar dispatch. Direct 8/10/12-bit production reconstruction covers both matrices of compound `GLOBAL_GLOBALMV`. Commit `c5637ea0187df35b385bf43e2fe85cd955f01099` records the checkpoint. - - [x] Decode the official `av1-1-b8-05-mv.ivf` sequence through every ordinary inter mode, all three motion modes, and every regular/smooth/sharp dual-filter pair; reconstruct sub-8x8 chroma and allocation-free no-round compound intermediates; and compare all four shown 352x288 YUV420 frames exactly with pinned-libaom output under normal/scalar dispatch and constrained tracked allocation. The official IVF SHA-1 matches the pinned libaom manifest. Both Release source targets build with zero warnings and errors; the `net10.0` test-project analyzer build completes with zero errors and 1,014 pre-existing repository warnings; Roslynk reports zero compiler errors; 3,983 focused decoder/predictor cases plus two complete official-sequence cases pass without failures or skips; and `git diff --check` is clean. Commit `1c58d855f70b024170ced9eb0a7005f0f9c955ad` records the checkpoint. - - [x] Verify every connected inter mode and filter with exact independent content. The official pinned-libaom sequence supplies complete bit-mask coverage for all twelve ordinary inter modes, all three motion modes, and all nine regular/smooth/sharp dual-filter pairs. The committed average, selectable compound, inter-intra, OBMC, scaled-reference, local-warp, and global-warp AVIF fixtures separately require every connected reconstruction variant and pass exact native-plane comparison; the presentation-bearing fixtures also pass exact pinned-libavif output. - - [x] Return the explicitly selected spatial layer or the final displayed layer, keeping reference reconstruction separate from display-only film grain. The essential-`lsel` production fixture reconstructs the selected 40x40 YUV444 base exactly, scales native component planes to the 80x80 item extent with pinned-libyuv integer rounding, and matches pinned-libavif RGBA presentation under normal/scalar dispatch. The committed final-layer fixture and film-grain matrix remain exact. Constrained tracked allocation returns every short-lived presentation plane exactly once. Commit `019ac5648b4380a6ff4865072c96fde29ce09fad` records the checkpoint. - - [x] Verify color and auxiliary-alpha output exactly against both pinned libavif progressive fixtures under normal SIMD dispatch and all required `FeatureTestRunner` fallbacks. Both fixtures pass exact public presentation under normal, AVX-512-disabled, AVX-disabled, and scalar dispatch; the alpha-bearing fixture additionally matches every composed alpha sample with its pinned native auxiliary plane. Both full production paths pass under a 1,024-byte constrained tracked allocator with exactly-once returns. Both Release source targets build with zero warnings and errors, the test-project analyzer build completes with zero errors and 1,014 pre-existing warnings, Roslynk reports zero compiler errors, and all 156 focused reconstruction and color cases pass without failures or skips. -- [x] Correct the audited 12-bit inverse ADST4, Identity4, and Identity16 SIMD arithmetic by widening only the libaom-widened multiply/accumulate operations, with exact conformant-range vectors and `FeatureTestRunner` coverage. Both 128-bit and 256-bit operators match pinned outputs through every required hardware fallback; 510 focused inverse-transform cases, 44 production reconstruction cases, both zero-warning source builds, the zero-error analyzer build, zero Roslynk compiler errors, and clean `git diff --check` complete the verification. -- [x] Verify the official libaom all-intra, CDF-update, and temporal motion-field sequences through exact native output, selected syntax coverage, normal/scalar dispatch, constrained tracked allocation, and the family-owned JPEG-style predictor operator architecture. -- [x] Complete the AV1 still-image unsupported-branch inventory and extend the profile-matrix gate with exact independent compression-tool fixtures. - - [x] Add the missing official ten-bit sequence evidence from libaom's pinned test-data manifest. All ten active-film-grain YUV420 frames and all ten monochrome frames match the pinned generic libaom native Y4M samples exactly under normal and scalar `FeatureTestRunner` dispatch, and both complete sequences pass through a 2 KiB constrained tracked allocator with balanced exactly-once returns. The three new focused cases and all 20 official-sequence regression cases pass without failures or skips, the scoped Release test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and `git diff --check` is clean. The existing production decoder required no correction. Commit `deefeba2340932909aa3e27516d965a294cf6faa` records the checkpoint. - - [x] Add official minimum- and maximum-quantizer dependent-frame evidence for the eight- and ten-bit libaom matrices. The four retained streams and pinned generic-libaom Y4M references pass exact native comparison under normal and scalar `FeatureTestRunner` dispatch, and the combined sequences pass through a 2,560-byte row-aligned constrained tracked allocator with balanced exactly-once returns. All three new focused cases and all 23 official-sequence regression cases pass without failures or skips, the scoped Release test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and `git diff --check` is clean. The existing production decoder required no correction. Commit `68cbe2fb9483215db36977a06a2dcac04e2d90bc` records the checkpoint. - - [x] Add exact official frame-size evidence at all four corners of libaom's 196-to-226-pixel matrix. Both frames from each retained stream match the pinned generic-libaom native Y4M samples under normal and scalar `FeatureTestRunner` dispatch, and all four sequences pass through a 1 KiB constrained tracked allocator with balanced exactly-once returns. Both new focused cases and all 25 official-sequence regression cases pass without failures or skips, the scoped Release test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and `git diff --check` is clean. The existing production decoder required no correction. Commit `ccdcddc92e3f7c45eb0a964569a9c8d2756c0526` records the checkpoint. -- [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 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] 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. -- [ ] **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 HEVC/HEIC still encoding. -- [ ] Implement and independently verify animated AVIF and HEIC encoding through the bounded image-sequence surface. -- [ ] Complete the public API, documentation, provenance, hardening, performance, and release gates, then run the full Release matrix. - -Performance, allocation, documentation, and independent test work are part of each delivery stage. They are not deferred cleanup phases, and the next queued stage must not begin while the current stage has unverified code or an open correctness gate. - -## Detailed implementation ledger - -- [ ] **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] 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] Verify canonical-sRGB compaction and metadata skipping independently from ICC preservation and color conversion. - - [x] Replace the AV1 RGB/YUV arithmetic layer with JPEG's closed-generic static operator pattern in both directions: frame-scoped operator selection, in-place planar decode, RGB inputs with `out` component planes for encode, `Vector512`/`Vector256`/`Vector128` traversal, one scalar tail, and JPEG's optimized byte-plane pack/unpack contracts for eight-bit pixels. - - [x] Keep codec row scheduling sequential and reuse frame-scoped allocator-backed buffers. Reuse JPEG's optimized `UnpackIntoRgbPlanes` contract for 8-bit encoding and pooled `Rgb48` staging for 10/12-bit encoding so high-precision input is not truncated through an eight-bit or `Vector4` intermediate. - - [x] Implement SIMD range expansion, chroma reconstruction, matrix/transfer conversion, clamping, and RGB packing for coefficient YCbCr, identity GBR, YCgCo, constant luminance, SMPTE ST 2085, and PQ/HLG ICtCp, with focused SIMD-lane/scalar-tail parity coverage. - - [x] Implement the H.273 V4 IPT-C2 and reversible YCgCo-Re/YCgCo-Ro transforms in both directions as closed SIMD operators, including their reduced RGB precision, signed integer lifting, full- and limited-range behavior, and 4:4:4 constraint. - - [x] Verify IPT-C2 against independently calculated matrix values and verify YCgCo-Re/YCgCo-Ro against exact 8/10/12-bit code values, limited-range black/white values, and SIMD-lane/scalar-tail parity. - - [x] Implement SIMD 4:2:0 and 4:2:2 encoder downsampling with odd-width and odd-height tails, and verify the stored 8/12-bit chroma samples against independently encoded full-resolution planes. - - [x] Move the H.273 parameter resolver, closed color operators, transfer functions, sample loading/storage, chroma filtering, and RGB packing into one shared HEIF color pipeline used by both AV1 and HEVC rather than maintaining codec-specific arithmetic copies. - - [x] Drive AV1 and HEVC through the same `IHeifPlanarSampleBuffer` 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 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] Implement allocation-free reconstructed-plane reference collection and normative substitution for complete, empty, and partially available borders, including asymmetric 4:2:2 availability units and the HEVC mode/size/component filter-selection rules. - - [x] Implement allocation-free SIMD-first HEVC inverse quantization for flat and custom scaling lists, 4/8/16/32 transforms, transform-skip scaling rules, extended precision, signed normalization shifts, and transform-range clipping. Store compact and transform-sized scaling matrices in one contiguous parameter-set allocation so the hot path can widen consecutive weights without coordinate division. - - [x] Derive each reference-unit availability flag from coding-tree, slice, tile, constrained-intra, and picture-boundary state, then connect prediction to transform-unit traversal and the bounded item decoder lifecycle. - - [x] Register the bounded `hvc1` still-image decoder, reconstruct the selected PPS/SPS picture, crop the conformance window directly in the pooled row converter, and pass the native planes through the shared SIMD-first H.273 pipeline. - - [x] Compare native HEVC planes byte-for-byte with HM 18 output for four item payloads extracted from independently produced HEIC files: two 8-bit 4:2:0 grid tiles, a cropped 8-bit 4:2:0 grid tile, and an 8-bit 4:4:4 wavefront tile. - - [x] Compare complete HEIC presentation output 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] 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. - - [ ] Complete the independently generated AV1 and HEVC conformance matrix for every supported color-signaling combination before closing the Phase 5 color exit gate. - - [x] Apply ICC conversion only after the SIMD YUV/CICP stage, alpha composition, grid assembly, and presentation transforms have produced the presented RGB image; retain ImageSharp's shared ICC converter and optimize reusable bulk kernels rather than creating a HEIF-specific color-management implementation. - - [ ] Verify ICC preservation, conversion, compaction, and metadata skipping for grids, alpha-composited images, every presented sequence frame, and the completed HEVC path. - - [x] Independently encode AVIF primary, grid, alpha-auxiliary, and two-frame sequence vectors with libavif 1.4.2/libaom 3.14.1; verify exact profile preservation, non-sRGB conversion, canonical-sRGB compaction, non-sRGB compact preservation, metadata skipping, every presented frame, and unchanged composed alpha. - - [ ] Repeat the complete ICC preservation, conversion, compaction, and skipping matrix for HEVC primary, grid, alpha-auxiliary, and sequence paths. Exact ICC-bearing still-image presentation is covered, but it does not close these separate color-management contracts. - - [ ] Benchmark representative 8/10/12-bit end-to-end AVIF and HEIC color pipelines with and without ICC conversion, recording absolute timings and allocations before marking Phase 5 complete. - - [x] Add permanent frame-wide `Av1ColorConversionBenchmarks` coverage for sequential 1920x1080 YUV 4:2:0 conversion in both directions at 8, 10, and 12 bits, including managed-allocation reporting. - - [x] Record the .NET 10 short-run AV1 baseline on 2026-08-25. SIMD decode measured 4.025/2.803/2.742 ms and encode measured 3.750/2.555/2.450 ms at 8/10/12 bits with 488-744 B allocated per frame. Forced-scalar decode measured 31.15/29.47/28.40 ms and encode measured 19.19/15.77/15.14 ms, making the normal SIMD pipeline 7.7-10.5 times faster for decode and 5.1-6.2 times faster for encode on this machine. - - [x] Add permanent frame-wide `HevcColorConversionBenchmarks` coverage and record the .NET 10 short-run plane-conversion baseline on 2026-08-26. SIMD decode measured 1.141/2.640/2.668 ms and encode measured 2.089/2.481/2.412 ms at 8/10/12 bits with 504-712 B allocated per frame. Forced-scalar decode measured 13.42/28.35/27.60 ms and encode measured 14.82/14.67/14.45 ms, making the normal SIMD pipeline 10.3-11.8 times faster for decode and 5.9-7.1 times faster for encode on this machine. - - [x] Re-verify the unified AV1/HEVC driver with all 127 focused color and exact HEIC presentation cases under normal SIMD dispatch and with all hardware intrinsics disabled. Re-run all 12 permanent 1920x1080 plane-conversion benchmarks; BenchmarkDotNet could not hold the configured power plan and the CPU changed performance state during warmup, so do not replace the controlled baselines above with those throttled sustained means. - - [ ] Add representative ICC-enabled AVIF cases and the equivalent HEVC cases after the HEVC reconstruction path is complete. -- [x] **Completed:** remove production `ToArray()` materializations from the HEIF implementation and make every retained copy an explicit ownership decision. - - [x] Store AV1 palette colors and HEVC short-term reference-picture state inline at their normative fixed bounds. - - [x] Retain only the AV1 configuration sequence-header payload required for item/sample equivalence instead of every configuration OBU. - - [x] Allocate HEVC emulation-prevention positions once at the exact validated count without a growing list plus array copy. - - [x] Use ImageSharp's pooled `ChunkedMemoryStream` and direct chunk writes for the legacy JPEG item payload. - - [x] Keep exact owned arrays only where ICC, Exif, XMP, or retained property state must outlive pooled parser buffers. -- [x] **Completed:** eliminate per-call scratch allocation across every AV1 forward and inverse 1-D transform, replace shared mutable transform scratch with operation-owned workspace, and complete the libaom-shaped SIMD-first transform paths with scalar fallback. - - [x] Inventory the existing hot-path scratch use: 15 forward/inverse transformer types contain 30 `stackalloc int[...]` sites. - - [x] Refactor every affected forward transform to reuse caller-owned workspace and its output buffer for staged ping-pong storage. - - [x] Refactor every affected inverse transform to reuse caller-owned workspace and its output buffer for staged ping-pong storage. - - [x] Remove the shared mutable `Av1ForwardTransformer.TemporaryCoefficientsBuffer` and give each concurrent transform operation exclusive workspace ownership. - - [x] Replace the instance-transformer interface with stateless forward and inverse operator structs following the JPEG color-transform static-generic operator pattern. - - [x] Select transform type, size, bit depth, and ISA once at the 2-D block boundary rather than dispatching through an interface for every row and column. - - [x] Port the DCT4/8/16/32/64, ADST4/8/16, and identity4/8/16/32 stage networks from the pinned libaom scalar and Highway sources into one static-generic operator architecture. - - [x] Implement paired add/subtract and whole-butterfly primitives for scalar, `Vector128`, `Vector256`, and `Vector512` values, including saturated packed arithmetic and shared widening work at each supported SIMD width. - - [x] Implement the complete libaom two-dimensional load, flip, shift, axis-transform, transpose, rectangle-normalization, promotion, and 64-point coefficient-retention pipeline without per-block allocation. The axis driver passes independent input and output strides directly to the two fixed stage buffers, and the 64x64 row transform retires only the retained 32 coefficients without a transform-sized copy pass. - - [x] Port the applicable libaom bulk inverse-transform kernels using the same tables, rounding, saturation, and clipping rules as the scalar oracle. - - [x] Use normal ImageSharp descending-width dispatch and require the actual packed arithmetic ISA when selecting packed `Vector512` traversal. - - [x] Document scratch ownership, stage-buffer alternation, fixed-point rounding, lane layout, transposition, and scalar fallback decisions at their implementation points. - - [x] Remove the separate SIMD files, width-specific forward contracts, and sample-representation suffixes so each transform operator owns one behavior model. - - [x] Verify every 1-D operator representation and every valid 2-D size/type/bit-depth combination through `FeatureTestRunner` with normal hardware, AVX-512 disabled, AVX disabled, and all hardware intrinsics disabled. - - The focused Release run passes all 511 forward and inverse transform cases. The suite covers DCT, ADST, and identity operators, packed overflow-sensitive inputs, padded 2-D input strides, all valid transform configurations, 8/10/12-bit dispatch, inverse reconstruction, and the zero-allocation block contract. - - [x] Verify DCT4/8/16/32/64, ADST4/8/16, and identity4/8/16/32 independently against the analytical transform oracle and coefficient-error bound used by the pinned libaom forward-transform tests. - - [x] Benchmark the production 32x32 DCT path after the complete paired stage port with preferred 256-bit and 512-bit widths. - - On the measured .NET 10 AVX-512 host, the packed 8-bit path measured 856.6 nanoseconds at 256 bits and 639.8 nanoseconds at 512 bits, making AVX-512BW 25.3% faster. The expanded 12-bit path measured 976.2 nanoseconds at 256 bits and 1019.8 nanoseconds at 512 bits, making the runtime-preferred 256-bit width 4.5% faster. BenchmarkDotNet reported no managed allocation. Production therefore follows Highway's AVX-512BW capability dispatch for packed stages and the runtime-preferred width for expanded stages. -- [ ] **Queued:** restore bounded animated HEIC and AVIF image-sequence scope, including the required image-level and per-frame metadata contracts, without introducing unrelated ISO BMFF surfaces. - - [x] Reconcile the top-level still-image-only scope with the required animated HEIC and AVIF completion boundary before sequence implementation begins. - - [x] Define the ImageSharp image-level sequence metadata and per-frame metadata contracts, including observable timing, repetition, color, alpha, orientation, and profile behavior. - - `HeifMetadata` now carries repetition and root-frame animation behavior through `FormatConnectingMetadata`. `HeifFrameMetadata` carries frame duration through `FormatConnectingFrameMetadata`; frame-local color, alpha, orientation, and profile state remain owned by the existing `ImageFrameMetadata` contract. The focused Release suite passes all 40 encoder, image metadata, and frame metadata tests. - - [x] Identify and document the minimum normative ISO BMFF track, sample-description, sample-location, dependency, and timing syntax required by conforming HEIC and AVIF image sequences. - - The bounded syntax matrix below is derived from ISO/IEC 23008-12:2022 clauses 7 through 10, the codec-specific HEVC Annex B, AVIF 1.2 section 3, AV1-ISOBMFF 1.2 section 2, and the pinned libavif `read.c` and `write.c` sequence paths. - - [ ] Parse and write only that bounded image-sequence syntax; do not add audio, arbitrary media, editing, fragmentation, streaming, or general presentation APIs. - - [x] Share one bounded 32/64-bit box-header reader between the existing item path and the image-sequence path, including short-read handling and parent-boundary validation. - - [x] Select one enabled `pict` master track without materializing unrelated tracks, accepting conforming image-sequence tracks that omit `track_in_movie`, then parse its `mvhd`/`tkhd`/`mdhd`/`hdlr`, self-contained `dref`, `av01` or `hvc1` sample entry, codec configuration, mandatory `ccst`, and bounded repetition edit. - - [x] Resolve `stsc`, `stco`/`co64`, `stsz`/`stz2`, `stts`, and `stss` into one exact value-type descriptor array capped by `DecoderOptions.MaxFrames`; validate complete run/count syntax through one allocator-owned sequential scratch buffer. - - Release verification passes the libavif-shaped two-sample parser fixture, the one-frame retention boundary, and a sample offset/length beyond the file. Pinned upstream libavif files independently verify 8-bit and 12-bit sequence identification, alpha-track and Exif/XMP retention, infinite and finite repetition, and safe omission of an unrelated audio track. The parser performs no per-entry allocation and does not buffer `moov`, `mdat`, unrelated track payloads, or complete attacker-sized tables. - - [x] Parse HEVC composition offsets and hidden samples from `ctts`/`cslg`, while rejecting the `ctts` box prohibited for AV1 tracks. - - Release verification covers signed and unsigned composition-offset syntax, hidden-sample visibility, composition-time calculation, required `cslg` and edit-list signaling, complete run counts, and the AV1 prohibition without buffering either table. - - [x] Parse optional direct dependencies from `refs` sample groups into a compact retained-sample dependency graph. - - The parser validates complete `sbgp` runs and `sgpd` entry lengths, versions, indices, positive identifier uniqueness, decode order, sync-sample independence, and `ccst` reference limits. It uses pooled value-type sort indexes plus one exact flat reference array, with no per-sample collections or attacker-sized table materialization. - - [x] Parse track presentation, color, and HDR properties through the same typed property parser used by still-image items. - - Selected visual sample entries now retain ICC and CICP color profiles, `pasp`, `clap`, `irot`, `imir`, `clli`, `mdcv`, `cclv`, `amve`, `reve`, and `ndwt`. Fixed-size properties use the parser's existing scratch buffer; only retained ICC bytes use bounded pooled staging before entering ImageSharp's existing profile model. - - [x] Parse bounded sequence metadata items. - - Selected image tracks now retain implicitly associated Exif and unencoded `application/rdf+xml` XMP items from bounded `iinf`/`infe` and `iloc` declarations, including file-relative and `idat`-relative extents. Item identifiers and extent descriptors use allocator-owned temporary storage; only the exact retained profile payloads survive parsing. `DecoderOptions.SkipMetadata` bypasses metadata parsing, `SegmentIntegrityHandling` follows the shared ancillary/image-data policy, and structural errors that prevent safe track parsing remain fatal. The focused Release sequence-parser suite passes all 15 tests. - - [ ] Connect the parsed sequence index to HEIF detection, Identify, frame decode, alpha matching, and frame metadata without changing still-image source selection. - - [x] Recognize supported `avis`, `hevc`, and `hevx` sequence brands while continuing to reject layered HEVC and JPEG sequence brands. - - [x] Identify bounded sequence dimensions, frame count, timing, repetition, codec precision, color, HDR, pixel aspect ratio, Exif, and XMP state. - - [x] Decode all-sync independently decodable AV1 samples into directly adopted ImageSharp frames without cloning complete pixel buffers. - - [x] Match auxiliary alpha samples by exact decode duration, visibility, and presentation time, and validate premultiplication track identity. - - [x] Require unity movie and track matrices so image presentation remains on the optimized `clap`/`irot`/`imir` path without a movie compositor. - - [x] Apply the shared `DecoderOptions` contract consistently to still items, nested payload codecs, grids, metadata properties, and sequence samples. - - `Strict` rejects recoverable ancillary and image-data errors, `IgnoreAncillary` suppresses only ancillary failures, and `IgnoreImageData` additionally permits failed image properties or samples to be omitted. `SkipMetadata` avoids optional property and item-payload validation, while cancellation and the caller configuration flow into nested JPEG and AV1 decoders. Target scaling and ICC conversion remain presentation-level operations after item or grid composition. The focused Release matrix passes all 19 new still-image policy cases, all 3 new sequence-sample cases, and the complete 37-test sequence-parser suite; the Release test-project build completes with zero errors. - - [x] Apply the same recovery boundary to still-image item relationships, coded payloads, primary-item thumbnail fallback, and optional alpha composition. - - Unknown item-reference types are skipped within their validated child boundaries. `cdsc` failures follow ancillary policy and are not parsed when metadata is skipped. `dimg`, `auxl`, `prem`, and `thmb` failures follow image-data policy. `IgnoreImageData` can omit an unreadable alpha plane or recover from a failed primary payload through a valid registered thumbnail, but decoding still fails when no color presentation remains. Real AVIF fixtures cover corrupt alpha payloads, corrupt alpha relationships, malformed Exif relationships, and strict, ancillary-only, image-data, and metadata-skipping behavior. - - [ ] Complete reference-dependent AV1 and HEVC sample reconstruction and independent sequence vectors. - - [ ] Write the same bounded movie, track, sample-description, location, dependency, timing, repetition, alpha, and metadata syntax from ImageSharp frames. - - [ ] Decode frame dependencies, durations, repetition, frame-local auxiliary images, and frame-local metadata into the existing ImageSharp multi-frame model. - - [ ] Encode ImageSharp frames, durations, repetition, frame-local auxiliary images, and frame-local metadata as independently decodable HEIC and AVIF image sequences. - - [ ] Add malformed-sequence boundary coverage and independently verify animated inputs and outputs with pinned HEIC and AVIF implementations. -- [ ] **Queued:** complete the AV1 and HEVC encoding paths and remove the explicit `HeifEncoderCore` encoding stubs only after each emitted payload passes independent decode. - - [ ] Complete every AV1 encoder task and exit gate in Phase 6; a smallest-valid fixed coding subset is only an intermediate milestone. - - [ ] Complete every HEVC encoder task and exit gate in Phase 7; a smallest-valid fixed coding subset is only an intermediate milestone. - - [ ] Connect both payload encoders to the bounded HEIF writer with the selected bit depth, chroma layout, range, color signaling, alpha, metadata, and animation state. - - [ ] Replace each `NotSupportedException` branch only when the corresponding payload is accepted by the pinned independent decoder and the ImageSharp decoder. - - [ ] Verify that every public quality, effort, lossless, bit-depth, chroma, alpha, and metadata option changes or constrains the encoded output exactly as documented. -- [x] **Completed:** define and document the HEIF encoder option contract using the established ImageSharp encoder pattern. - - [x] Confirm that `IHeifEncoderOptions` has only one concrete implementation and remove the unnecessary interface. - - [x] Document the default, valid range, special values, invalid-value behavior, and format-dependent restrictions of every retained option using observable API behavior only. - - [x] Pass `HeifEncoder` directly to `HeifEncoderCore`, matching the JPEG, PNG, and WebP encoder-core contracts and avoiding interface dispatch. - - [x] Verify construction-time range validation and legacy-JPEG codec-boundary restrictions with focused tests before the Phase 1 API-review gate is marked complete. -- [x] **Completed:** lock public HEIF image metadata to the supported component bit-depth contract. - - [x] Replace the unrestricted integer bit depth with `HeifBitDepth` and preserve the 8-bit default. - - [x] Resolve format-connecting component precision to the nearest supported 8/10/12-bit output without widening the public value domain. - - [x] Reject HEVC configuration records outside the exposed 8/10/12-bit profile matrix at the external parse boundary. - - [x] Verify defaults, cloning, format-connecting conversion, pixel-type projection, and current HEIC/HIF/AVIF Identify results. - - Release build: 0 errors. Focused metadata and Identify tests: 42 passed, 0 failed. - -Gain maps, progressive/layered images, sample transforms, and experimental extension brands require explicit conformance and API decisions. They do not create permission to omit any valid color, compression, or bit-depth path from the PR. The container reader must skip unsupported optional extensions safely and reject an unsupported essential property with a useful error. - -## Container scope - -The container implementation is a deliberately narrow HEIF image-format reader and writer, not a general ISO BMFF framework. Implement only the box syntax and relationships required for supported HEIF image items and bounded HEIC/AVIF image sequences. - -In scope are the file type, metadata, item location/data, item information, item properties, item references, primary-item selection, `idat`/`mdat` payload storage, grids, auxiliary alpha, presentation transforms, color properties, and Exif/XMP paths required by still images. Image sequences additionally include only the brands, tracks, sample descriptions, sample locations, decode dependencies, timing, repetition, and frame-local metadata required to map HEIC and AVIF sequences to ImageSharp frames. - -Still files produce one presented primary image. Supported image sequences produce one ordered ImageSharp frame collection with bounded duration, repetition, frame dependency, alpha, color, orientation, and profile state. Supporting items are decoded only when required to construct or describe those presented frames. The implementation does not expose arbitrary HEIF image collections, bursts, pages, audio, or non-image media. - -Out of scope are audio, arbitrary video, edit lists, fragments, streaming profiles, general presentation APIs, and reusable movie, track, or sample-table object models. Track, sample, timing, and inter-frame codec state may exist only inside the bounded HEIC/AVIF sequence decoder or encoder that owns it. Unknown optional boxes remain bounded and skippable; unsupported essential image or sequence syntax must fail with a useful image-format error. - -Implementation rule: do not introduce a reusable general-purpose ISO BMFF box hierarchy, track model, or media parser. Add box syntax directly to the bounded HEIF model only when a supported still image, image-sequence frame, relationship, property, metadata path, or conformance fixture requires it. Each addition must name the image behavior it enables and have a focused image-format test. - -An ISO BMFF construct may be added only when all of the following are true: - -1. A conforming supported still image or HEIC/AVIF image sequence requires it to produce or describe presented ImageSharp frames. -2. Its owning image item and its effect on the decoded or encoded image are explicit. -3. It can be parsed or written as bounded state owned by the HEIF image-item or image-sequence path without adding a general media abstraction. -4. Independent still-image or animated-image fixtures exercise the behavior it enables. - -Encountering a box in libavif, ISO BMFF, or a third-party file is not by itself a reason to port it. Constructs not required by the supported still-image or bounded image-sequence presentation must be skipped when optional or rejected when essential. - -Codec-configuration rule: parse `av1C` and `hvcC` as properties of coded image items and as the bounded decoder configurations referenced by supported image-sequence sample descriptions. Validate profile, level, bit depth, chroma layout, and parameter-set/OBU declarations against the associated item or sequence samples. Do not expose visual sample entries, sample descriptions, layer selection, sample groups, or timing as general public or reusable ISO BMFF models. - -AV1 sequence headers and HEVC VPS/SPS/PPS structures remain in scope because they are codec syntax required by image items and image-sequence frames. Retained reference-frame state is permitted only for the lifetime of a bounded HEIC/AVIF sequence decode or encode operation. - -### Bounded image-sequence syntax - -The sequence reader and writer may retain only the following syntax and the resolved image behavior listed beside it: - -| Syntax | Required image behavior | -| --- | --- | -| `ftyp` sequence and structural brands | Recognize `avis` AV1 sequences and the non-layered `hevc`/`hevx` HEVC sequence profiles. `avio` can additionally signal an all-sync AV1 sequence. Layered `hevm`/`hevs`, JPEG `jpgs`, arbitrary video brands, and brands for unimplemented codecs remain unsupported until their image payload and presentation requirements are implemented. | -| `moov`/`mvhd`, `trak`/`tkhd`, and `mdia`/`mdhd`/`hdlr` | Select an enabled `pict` master image-sequence track without requiring the optional `track_in_movie` flag; retain its displayed dimensions, media time scale, media duration, and movie-time-scale track duration. Require unity movie and track matrices because arbitrary movie-canvas composition is outside image-format scope; use `clap`/`irot`/`imir` for supported image presentation. Inspect unrelated tracks only far enough to identify and skip their non-image handlers. | -| `minf`/`dinf`/`dref` and `stbl` | Accept only self-contained sample data references and own the bounded sample-table state for one selected image sequence plus an optional linked auxiliary-alpha sequence. No reusable data-reference, media-information, or sample-table API is created. | -| `stsd` and one supported visual sample entry | Require exactly one `av01` entry for AVIF or one non-layered `hvc1` entry for HEIC. Retain only its dimensions, codec configuration (`av1C` or `hvcC`), image presentation/color/HDR properties, and mandatory version-zero `ccst` coding constraints. Reject an unsupported essential configuration rather than treating it as generic video. | -| `stsc`, `stco`/`co64`, and `stsz`/`stz2` | Resolve each declared image sample directly to a validated file offset and length. Expand run tables once into a compact frame-owned descriptor array bounded by `DecoderOptions.MaxFrames`; never buffer the movie or complete `mdat`. | -| `stts` | Expand decode-order sample durations against the media time scale into exact per-frame rational delays. A sequence with advisory timing still maps every visible sample to an ImageSharp frame. | -| optional `ctts` version 0/1 and `cslg` | Retain only composition offsets and the HEIF hidden-sample condition required to determine presented frame order and visibility. No general composition timeline is exposed. | -| optional `stss` | Mark the one-based random-access samples; absence means every sample is a sync sample. The first retained frame must have a valid decode path from a sync sample. | -| optional `sgpd`/`sbgp` with grouping type `refs` | Resolve direct-reference sample identifiers for bounded dependency validation and random access. Other sample-group types are ignored unless a later image-format requirement explicitly brings them into scope. | -| optional `edts`/`elst` | Interpret only normal-rate image-sequence presentation and the HEIF repetition flag. Reject edits that require arbitrary media splicing, rate changes, or a general edit timeline. | -| optional `tref` entries `auxl`, `prem`, `thmb`, and `cdsc` | Link only image alpha, premultiplication, image thumbnails, and image metadata. Auxiliary samples are matched to color frames by their decoded time; audio and every unrelated reference type are ignored. | -| optional track-level `meta` and image metadata linkage | Apply sequence-wide Exif/XMP profiles from the bounded track metadata model. Per-frame metadata is limited to supported metadata items/groups or codec metadata that can be mapped to `ImageFrameMetadata`; generic timed-metadata tracks are not exposed. | - -The implementation must validate uniqueness, versions, entry counts, run totals, one-based indices, integer products, cumulative durations, offsets, and lengths before creating retained arrays. Temporary box payloads use the configured `MemoryAllocator`; persistent sample descriptors are allocated once at the validated retained-frame count. Parsing must skip unrelated boxes by their bounded length and must not copy an entire `moov` or `mdat` payload. - -## Reference hierarchy - -Use the references in this order when behavior differs: - -1. The published ISO BMFF, HEIF, HEVC, AV1, AV1-ISOBMFF, and AVIF requirements are normative. -2. The official [AOM AV1 Codec Library](https://aomedia.googlesource.com/aom/) is the primary implementation reference for AV1 decode, encode, high-bit-depth behavior, tests, and optimized scalar/SIMD algorithms. Pin one reviewed commit before porting. Use its architecture-specific paths to shape the managed SIMD-first production architecture and its scalar C paths to define the equivalent fallback behavior. -3. The local `D:\GitHub\AOMediaCodec\libavif` checkout is the AVIF container, metadata, color-conversion, grid, alpha, and interoperability oracle. At inspection time it identifies itself as 1.4.2-devel. -4. The official HM reference software at commit `9c1f298659ab0cee9dc13d23d0304221575410b9` is the primary scalar implementation reference for HEVC syntax, reconstruction, and the complete Range Extensions profile matrix. Android `libhevc` v1.6.0 at commit `c83a76b084498d55f252f48b2e3786804cdf24b7` is a secondary production-C comparison for its supported common 8-bit 4:2:0 paths; its decoder rejects other chroma formats and bit depths and therefore is not the feature-coverage authority. The HEVC specification remains normative, and an independent HEIC container implementation is still required as an interoperability oracle. Do not copy from GPL or otherwise incompatible sources. -5. Existing ImageSharp codecs are the authority for ImageSharp API shape, memory ownership, stream behavior, cancellation, resource limits, pixel conversion, tests, and SIMD dispatch. - -The linked ImageSharp discussion establishes the project constraint: the shipped implementation is purely managed and other codec libraries are references, not native runtime dependencies. libaom is the official encoder/decoder implementation reference for AV1, but it does not parse the HEIF container or implement HEVC. libavif dispatches AV1 work to external codec libraries, so it remains an observable AVIF/container oracle rather than the source for every AV1 algorithm. - -The AOM source is distributed under the BSD 2-Clause License and the Alliance for Open Media Patent License 1.0. HM is distributed under its BSD license with an explicit patent-rights disclaimer, while the reviewed Android `libhevc` source is distributed under Apache-2.0. Before porting further code, record the exact upstream file, commit, applicable license/patent notice, and corresponding managed file or method. Audit the existing SVT-AV1-attributed WIP separately rather than relabeling it as libaom-derived. Update `THIRD-PARTY-NOTICES.TXT` before any referenced implementation code is merged. A pure managed HEVC implementation does not remove HEVC patent or licensing obligations, so those must be resolved before the HEIC work is considered releasable. - -### Pinned reference and baseline snapshot - -The initial post-merge snapshot was established on 2026-08-24: - -- the official libaom reference is tag `v3.14.1`, commit `03087864cf4bea6abb0d28f95cf7843511413d8f`, matching the revision selected by the local libavif `ext/aom.cmd` dependency script; -- the local libavif container, color-conversion, and interoperability oracle is commit `092276ce89098ead06db80975173191e5fee1826`, described as `v1.4.2-66-g092276ce`; -- the official HM reference software is commit `9c1f298659ab0cee9dc13d23d0304221575410b9`; its decoder and profile tables cover the HEVC Range Extensions chroma, bit-depth, intra, and still-picture profiles required for the final HEIC feature matrix; -- the official Android `libhevc` reference is v1.6.0 at commit `c83a76b084498d55f252f48b2e3786804cdf24b7`; it remains a secondary common-path comparison because its SPS parser accepts only 8-bit YUV 4:2:0; -- the official ITU-T H.274 (V4) recommendation from January 2026 is the normative semantics reference for mastering-display and content color-volume fields reused by still-image item properties; its video-SEI persistence and cancellation behavior is outside this container scope; -- an independent HEIC container interoperability oracle and the complete external HEIC fixture-generation matrix remain to be pinned before HEIC support can pass its final integration gate; -- `dotnet build ImageSharp.sln -c Release --no-restore -m:1 -v minimal` succeeds with no errors after the upstream compatibility fixes; and -- the existing HEIF-focused test run executes 8,198 cases, with 8,184 passing and 14 failing. Thirteen failures are isolated to the WIP AV1 YUV conversion tests, and one is the existing legacy JPEG HIF reference-image mismatch. Golden artifacts have not been changed. - -This snapshot pins or classifies the available references and failures; it does not complete Phase 0. The full WIP provenance map, disabled-test inventory, HEIC container interoperability selection, and feature-state matrix remain required. - -### Provenance map in progress - -| Managed implementation | Normative behavior | Reviewed implementation reference | Use | -| --- | --- | --- | --- | -| `HeifColorConversionParameters`, `HeifColorConverter`, `IHeifPlanarSampleBuffer`, `HeifPlanarColorConverter`, `Av1YuvConverter`, and `HevcYuvConverter` | ITU-T H.273 (V4) equations 14-16 and 27-84, including limited/full-range scaling, chromaticity-derived equations 39-47, YCgCo equations 51-57, constant-luminance equations 66-75, IPT-C2, YCgCo-Re/YCgCo-Ro, and the PQ/HLG ICtCp matrices; AV1 section 6.4.2 and HEVC Annex E chroma sample positions | Official ITU-T H.273 (V4) (07/2024); libavif `src/reformat.c` and `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826`; libaom `aom/aom_image.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | SIMD-first closed operators provide full/limited-range conversion at 8, 10, and 12 bits, with an equivalent scalar fallback and tail. Decode and encode cover every H.273 V4 matrix code point through 17: identity, YCgCo, coefficient-based NCL, both fixed and chromaticity-derived constant/non-constant-luminance systems, SMPTE ST 2085, ICtCp, IPT-C2, YCgCo-Re, and YCgCo-Ro. One closed generic planar driver owns pooled sequential row traversal, chroma reconstruction/downsampling, fixed-point specialization, SIMD dispatch, scalar tails, and packed-pixel conversion for both AV1 and HEVC adapters. Shared paths cover monochrome, YUV 4:2:0, 4:2:2, and 4:4:4; AV1 centered/co-sited placement; and all six HEVC progressive 4:2:0 sample locations. Identity and reversible lifting enforce their full-chroma constraint. Limited-range YCgCo retains the 219-code scale inherited from its R/G/B inputs instead of applying YCbCr's unrelated 224-code chroma range. YCgCo-Re and YCgCo-Ro apply range adjustment at reduced-precision RGB code values before signed integer lifting. Chromaticity derivation uses every defined H.273 primary and matches libavif's BT.709 fallback for unspecified or reserved primaries. ICtCp and IPT-C2 inverses come from the exact H.273 matrices rather than unrelated display conversions. Closed operators provide `Vector512`, `Vector256`, and `Vector128` implementations with one scalar tail. Eight-bit conversion uses JPEG's RGB plane pack/unpack contracts; high-bit-depth conversion retains native 16-bit packed pixels. | -| `HeifTransferFunctions` | ITU-T H.273 (V4) Table 3 transfer characteristics 1-18 | Official ITU-T H.273 (V4) (07/2024); libavif `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826` | Apply every HEVC/AV1-signallable transfer function required by constant-luminance, ICtCp, and IPT-C2 conversion. Retain the H.273 normalized PQ and HLG definitions; do not import libavif's display-oriented 203-nit scaling or HLG OOTF into codec sample interpretation. Use libavif's midpoint convention only for the non-bijective zero code of the two logarithmic curves. | -| `ObuReader.ReadSequenceHeader`, `ReadUncompressedFrameHeader`, decoder-model parsing, and operating-parameter consumption | AV1 sections 5.5.2 through 5.5.4 sequence timing and decoder-model syntax, section 5.9.2 uncompressed frame-header syntax, and section 5.9.31 temporal-point syntax | libaom `av1/decoder/decodeframe.c` functions `av1_read_decoder_model_info`, `av1_read_op_parameters_info`, `read_temporal_point_info`, and `read_uncompressed_header`, plus `common/av1_config.c`, at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Read the normative 32-bit decoding-tick field; consume operating-point buffer delays and the layer-applicable frame removal times needed to keep a non-reduced still-image sequence aligned; and read presentation time only under the normative decoder-model condition. Retain no scheduling or playback behavior from those values and introduce no ISO BMFF timing, track, sample-table, or sequence surface. | -| `Av1FrameBuffer` high-bit-depth sample layout and `Av1YuvConverter` 10/12-bit packed-pixel conversion | AV1 section 6.4.1 bit depth and H.273 sample-range scaling | libaom `aom_scale/yv12config.h`, `av1/common/idct.c`, and `av1/common/reconintra.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f`; libavif `src/avif.c` and `src/reformat.c` at `092276ce89098ead06db80975173191e5fee1826` | Establish two-byte native sample storage with sample-unit strides for 10/12-bit reconstruction and use ImageSharp's existing `Rgb48` pixel-operation paths in both directions so packed-pixel staging does not reduce high-bit-depth samples to eight bits. | -| `Av1PredictionDecoder`, `Av1IntraPredictor`, `Av1ChromaFromLumaContext`, `Av1ChromaFromLumaPredictor`, `Av1PartitionInfo`, and the DC, directional, Paeth, smooth, filter-intra, and chroma-from-luma predictor operators | AV1 sections 7.11.2 and 7.11.2.3 intra prediction | libaom `aom_dsp/intrapred.c`, `av1/common/reconintra.c`, `av1/common/av1_common_int.h`, `av1/common/blockd.h`, `av1/common/cfl.c`, and `av1/common/cfl.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Design the shared predictor contract, sample layout, reference preparation, and scratch ownership for widest-to-narrowest SIMD traversal, then provide the equivalent scalar fallback through that contract. Preserve luma/chroma mode-neighbor addressing, directional upsampling, Paeth selection, smooth normalization, filter-intra taps, high-bit-depth clipping, chroma-from-luma storage/subsampling, and chroma-from-luma row strides. The WIP rectangular byte-pipeline smooth digest expectations encode width/height-swapped weights and must be replaced only from an independently generated oracle, not regenerated from this implementation. | -| `Av1ReferenceScale`, `Av1InterPredictor` scaled operators, `Av1BlockDecoder` scaled-reference traversal, and `Av1FrameBuffer` prediction border | AV1 sections 7.11.3.3 reference scaling and 7.11.3.4 block inter prediction | libaom `av1/common/scale.c`, `av1/common/scale.h`, `av1/common/reconinter.h`, `av1/decoder/decodeframe.c`, `av1/common/convolve.c`, `av1/common/filter.h`, and `aom_scale/yv12config.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve Q14 reference scale factors, Q10 variable phases and steps, signed coordinate rounding, scaled UMV margins, the 288-sample reference border, reduced filter selection, bit-depth-dependent two-pass rounding, and byte/high-bit-depth clipping. A four-output `Vector128` gather-shaped traversal and exact scalar tail share one behavior model and caller-owned scratch. The production path covers ordinary, compound, and OBMC prediction without per-block allocation. | -| `Av1TileReader` palette mode/color-map parsing, `Av1SymbolDecoder` palette distributions, `Av1BlockModeInfo` palette state, and `Av1PredictionDecoder` palette reconstruction | AV1 sections 5.11.46, 5.11.49, and 7.11.2 palette prediction | libaom `av1/decoder/decodemv.c`, `av1/decoder/detokenize.c`, `av1/decoder/decoder.h`, `av1/common/pred_common.c`, `av1/common/pred_common.h`, and `av1/common/entropymode.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Reuse the existing tile range decoder and frame-owned mode map while porting the normative palette cache merge, high-bit-depth color deltas, tile-adaptive mode/size/index distributions, diagonal color-map traversal, edge padding, and direct palette-sample reconstruction. This is AV1 still-image compression syntax and does not add retained video reference state or any ISO BMFF surface. | -| `Av1DeblockingFilter`, `Av1LoopFilterContext`, and `Av1LoopFilterDecoder` | AV1 section 7.14 deblocking loop filter | libaom `aom_dsp/loopfilter.c` and `av1/common/av1_loopfilter.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve the 8- and high-bit-depth edge layout and 4-, 6-, 8-, and 14-tap kernels for SIMD-first traversal, with sharpness thresholds, per-block intra/inter reference and mode deltas, current and previous skipped-transform decisions, transform and prediction-unit edge selection, and plane traversal shared by the exact scalar fallback. This is normative AV1 image reconstruction and adds neither generic ISO BMFF models nor retained video reference state. | -| `Av1CdefDecoder`, `Av1CdefFilter`, and CDEF-unit strength storage | AV1 sections 7.15.2 through 7.15.4 constrained directional enhancement filtering | libaom `av1/common/cdef.c`, `av1/common/cdef_block.c`, `av1/common/cdef.h`, and `av1/common/cdef_block.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Design direction search and constrained primary/secondary filtering around vector-friendly block storage and SIMD-first kernels, with variance adjustment, subsampling direction conversion, skipped-8x8 selection, and frame-edge sentinel behavior shared by the scalar fallback. Use a frame-owned source snapshot so filtering never consumes already modified samples. This is normative AV1 still-image reconstruction and introduces no ISO BMFF, track, timing, or sequence-playback surface. | -| `Av1SuperResolutionDecoder`, `Av1SuperResolutionFilter`, frame-size derivation, and decoded-image dimensions | AV1 section 7.16 normative super-resolution upscaling | libaom `av1/common/resize.c`, `av1/common/resize.h`, `av1/common/convolve.c`, `av1/common/x86/av1_convolve_horiz_rs_sse4.c`, and `aom_dsp/aom_filter.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Port the fixed 64-phase, 8-tap horizontal filter, phase/step derivation, replicated frame edges, chroma width rounding, signed rounding, and 8/10/12-bit clipping. Follow libaom's four-output multiply/add, two-stage horizontal reduction, packed-store traversal with exact scalar fallback. Generic image resizing is not normative AV1 super-resolution. This adds no track, timing, fragment, animation, or generic ISO BMFF model. | -| `Av1TileReader` loop-restoration unit syntax, `Av1SymbolDecoder` restoration distributions/subexponential codes, and `Av1FrameInfo` unit storage | AV1 section 5.11.57 `read_lr` and `read_lr_unit` syntax | libaom `av1/decoder/decodeframe.c`, `av1/common/restoration.c`, `av1/common/restoration.h`, `av1/common/entropymode.c`, `aom_dsp/binary_codes_reader.c`, and `aom_dsp/recenter.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Decode tile-local switchable/Wiener/self-guided selections, finite reference-subexponential coefficients, chroma Wiener windows, self-guided parameter sets, super-resolution-adjusted unit corners, and the AV1 nearest-unit-count rule into frame-owned per-plane grids. This is compressed still-image syntax and adds no movie, track, timing, fragment, audio, or sequence surface. | -| `Av1WienerFilter` | AV1 sections 7.17.4 and 7.17.5 Wiener restoration filtering and coefficient derivation | libaom `av1/common/restoration.c`, `av1/common/restoration.h`, `av1/common/convolve.c`, and `av1/common/convolve.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve the implicit center-sample contribution, separable horizontal/vertical rounding, bit-depth-dependent 16-bit intermediate range, and final 8/10/12-bit clipping. Reuse `Vector128_.MultiplyAddAdjacent` for the contiguous horizontal eight-tap product with an exact scalar fallback. Keep the restoration stage disabled until stripe boundaries and self-guided filtering are both complete. | -| `Av1SelfGuidedFilter` | AV1 sections 7.17.2 and 7.17.3 self-guided and box-filter processes | libaom `av1/common/restoration.c` and `av1/common/restoration.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Design the window layout, caller-owned scratch, local statistics, and projection traversal from libaom's architecture-specific kernels so the production path is SIMD-first. Implement the equivalent scalar fallback through the same contract. Preserve the sixteen normative radius/variance parameter sets, local mean and variance normalization, alternating-row radius-two optimization, decoded projection-coefficient behavior, signed rounding, and 8/10/12-bit clipping. Keep this image-reconstruction stage disabled until restoration stripe boundaries are complete. | -| `Av1LoopRestorationBoundary`, `Av1LoopRestorationDecoder`, and `Av1FrameDecoder` restoration-stage ordering | AV1 section 7.17 loop restoration, including striped boundary semantics | libaom `av1/common/restoration.c`, `av1/common/restoration.h`, `av1/common/resize.c`, and `av1/decoder/decodeframe.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve two deblocked rows at internal 64-luma stripe boundaries before CDEF, apply the existing normative SIMD-backed super-resolution kernel to saved rows when scaled, use post-CDEF/super-resolution samples at frame edges, extend the final restoration unit up to 150 percent of nominal size, and filter from immutable plane snapshots into separate output planes. This is bounded still-image reconstruction state, not retained reference-frame, track, timing, or playback state. | -| `Av1FilmGrainDecoder`, `Av1FilmGrainGaussianSequence`, `Av1FilmGrainNoise`, `Av1FilmGrainOverlap`, and `Av1FilmGrainSampleOperations` | AV1 section 7.18 film-grain synthesis | libaom `av1/decoder/grain_synthesis.c`, `av1/decoder/grain_synthesis.h`, and `aom_dsp/grain_params.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve the normative 2,048-sample Gaussian sequence as compile-time span data, the serial linear-feedback shift register and luma/chroma autoregressive templates, scaling lookup interpolation, 32x32 block selection, boundary overlap, restricted-range clipping, monochrome and 4:2:0/4:2:2/4:4:4 layouts, and 8/10/12-bit arithmetic. Use allocator-owned scratch. Apply scaling and noise through AVX2 gather arithmetic at every bit depth, retain the measured cross-platform 128-bit high-bit-depth path, and use the exact scalar path for 8-bit machines without AVX2 and for vector tails. Process horizontal overlap through preferred-native 512-bit, then 256-bit, 128-bit, and scalar tiers; retain scalar vertical overlap because each output row exposes only one or two strided samples. Keep SIMD width, ISA, sample storage, and bit depth out of folders, namespaces, files, and type names. Apply grain only to displayed samples after all in-loop filters, inherit reference-selected parameters through the bounded image-layer decoder state, and keep the ungrained reconstruction in every refreshed reference slot. This does not add sequence playback or video scope. | -| `Av1FrameInfo`, `Av1TileReader`, and `Av1BlockDecoder` transform/coefficient storage | AV1 section 5.11.39 coefficient syntax and section 7.11.2 reconstruction | libaom `av1/decoder/decodetxb.c` and `av1/decoder/decoder.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve separate luma and chroma transform coefficients at monotonically advancing per-plane offsets within each superblock so reconstruction consumes the same transform-block order produced by tile parsing. | -| `Av1InverseQuantizer` and `Av1InverseQuantizationLookup` | AV1 section 7.12.3 inverse quantization | libaom `aom_dsp/aom_dsp_common.h`, `av1/common/quant_common.c`, and `av1/decoder/decodetxb.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Select the per-segment matrix level, alias 64-pixel transform dimensions to their adjusted matrices, retain a flat level-15 matrix, and apply the five-bit inverse-matrix weight scale. The large managed lookup remains a single process-wide table. | -| `Av1ForwardTransformer`, `Av1Inverse2dTransformer`, `Av1Transform2dFlipConfiguration`, their nested forward/inverse 1-D operator structs, and `Av1Transform1dMath` | AV1 forward transform definitions and section 7.11.2 inverse transform and reconstruction | libaom `av1/encoder/av1_fwd_txfm2d_hwy.h`, its AVX-512 and AVX2 instantiations, `av1/encoder/av1_fwd_txfm1d.c`, `av1/common/av1_inv_txfm1d_cfg.h`, `av1/common/av1_inv_txfm1d.c`, `av1/common/av1_inv_txfm2d.c`, the x86 AVX2/SSE4 implementations, and the corresponding Neon implementations at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Preserve the normative staged DCT, ADST, and identity arithmetic, direction-specific stage ranges and shifts, transposition, clipping, and high-bit-depth sample addition. Stateless static-generic operators follow ImageSharp's JPEG color-transform pattern and remain nested in the family that owns their shared traversal. Forward operators share one behavior model across scalar, `Vector128`, `Vector256`, and `Vector512`, selecting packed or expanded lane storage at the 2-D boundary. Inverse production traversal retains the verified scalar, `Vector128`, and `Vector256` tiers until a wider upstream shape and complete-block benefit are both established. | -| `HeifDecoderCore` box extension handling and `HeifDecoderCore`/`HeifEncoderCore` item-property associations | ISO/IEC 14496-12 box extensibility and section 8.11.14 item properties and `ipma` syntax | libavif `src/read.c` and `src/write.c` at `092276ce89098ead06db80975173191e5fee1826` | Skip unrecognized top-level and metadata child boxes, preserve the position of every property in `ipco`, reject an unrecognized property only when its item association marks it essential, associate properties by item ID, and read or write the essential bit plus one-based 7-bit or 15-bit property index according to the full-box flags. Independent HEIC, HIF, and AVIF fixtures provide the reader oracle; container-level identification of encoded output guards the writer independently of pixel roundtripping. | -| `HeifCleanAperture`, `HeifItem` presentation state, and `HeifDecoderCore` transformative-property parsing and application | ISO/IEC 14496-12 section 12.1.4 clean aperture; HEIF image rotation and mirror properties; MIAF section 7.3.6.7 presentation order and section 7.3.9 essential transformative properties | libavif `src/avif.c` clean-aperture conversion, `src/read.c` property parsers and alpha-property validation, and `apps/shared/avifutil.c` transform application at `092276ce89098ead06db80975173191e5fee1826` | Resolve fractional clean-aperture dimensions and center offsets to exact bounded integer pixels, validate the registered rotation/mirror reserved bits, require essential associations, crop after auxiliary-alpha composition, map counter-clockwise HEIF quarter turns to ImageSharp's optimized clockwise rotate modes, then mirror around the signaled axis. Reuse ImageSharp's existing crop, rotation, and flip processors for every pixel type. Retain only the three image-item property values; do not add a generic transform-box or ISO BMFF model. | -| `HeifConstants.IsSupportedFileType`, `HeifImageFormatDetector`, and `HeifDecoderCore.CheckFileTypeBox` | ISO/IEC 14496-12 `FileTypeBox` syntax and the MP4 Registration Authority HEIF/AVIF still-image and sequence brand registrations | libavif `src/read.c` functions `avifParseFileTypeBox`, `avifFileTypeHasBrand`, and `avifFileTypeIsCompatible` at `092276ce89098ead06db80975173191e5fee1826` | Apply one rule to the major and compatible brands, accept implemented still-image brands and the bounded `avis`, `hevc`, and `hevx` image-sequence brands, and distinguish item and sequence presentation before parsing their payloads. Layered HEVC and JPEG sequence brands remain unsupported. The decoder validates the complete `ftyp` payload; the fixed-size format detector inspects the available prefix. | -| `HeifSequenceParser`, `HeifTrackMetadataParser`, and the bounded HEIF image-sequence writer | ISO/IEC 23008-12:2022 clauses 7 through 10 and HEVC Annex B; ISO/IEC 14496-12 sample tables, normal edit lists, and track-level metadata; AVIF 1.2 section 3; AV1-ISOBMFF 1.2 section 2 | libavif `src/read.c` track/sample-table and track-metadata paths and `src/write.c` sequence path at `092276ce89098ead06db80975173191e5fee1826`; independently produced HEIC sequence fixtures remain required | Resolve only `pict` master tracks and linked image alpha/metadata state into validated sample descriptors, exact frame durations, sync/dependency state, repetition, presentation transforms, color/HDR properties, and frame metadata. Track metadata is limited to implicitly associated Exif and unencoded XMP items declared by bounded `iinf`/`infe`, `iloc`, and optional `idat` syntax. `DecoderOptions` controls metadata retention and recoverable ancillary/image-data errors through the same rules as other decoders. Consume the required container boxes listed in the bounded syntax matrix without exposing a movie, track, edit, or sample-table API. | -| `HeifDecoderCore.ReadBoxHeader` and `HeifDecoderCore.ParseBoxHeader` | ISO/IEC 14496-12 section 4.2.2 basic box syntax | libavif `src/stream.c` functions `avifROStreamReadBoxHeaderPartial` and `avifROStreamReadBoxHeader` at `092276ce89098ead06db80975173191e5fee1826` | Resolve 32-bit, 64-bit, UUID, and top-level size-zero boxes into content lengths only after validating the complete variable-sized header and the remaining parent boundary. Nested size-zero boxes are invalid; large skips retain 64-bit offsets. | -| `HeifDecoderCore.ParseMetadata` | ISO/IEC 14496-12 `MetaBox` and HEIF item declarations, locations, properties, and associations | libavif `src/read.c` functions `avifParseMetaBox`, `avifMetaFindOrCreateItem`, `avifParseItemLocationBox`, and `avifParseItemPropertiesBox` at `092276ce89098ead06db80975173191e5fee1826` | Index unique recognized metadata children by type and payload location, then parse them in dependency order so physical placement does not control item lookup or property association. Duplicate unique children and truncated full-box headers are invalid. | -| `HeifDecoderCore.ApplyAssociatedMetadata` | HEIF Annex A Exif item data, MIME metadata items, and `cdsc` item references | libavif `src/read.c` function `avifDecoderFindMetadata`, `src/exif.c` function `avifGetExifTiffHeaderOffset`, and the Exif/XMP item writing paths in `src/write.c` at `092276ce89098ead06db80975173191e5fee1826` | Resolve only metadata items whose `cdsc` reference identifies the decoded primary image, validate the Exif TIFF-header offset, and attach Exif or `application/rdf+xml` XMP through ImageSharp's existing profile types. This is a bounded still-image metadata path; it does not introduce a generic ISO BMFF metadata, media, or track model. | -| `HeifPropertyParser`, `HeifDecoderCore` color-property association, `HeifItem` and `HeifSequenceTrack` color profiles, and `Av1Decoder` effective color validation | ISO/IEC 14496-12 section 12.1.5 color information; HEIF section 6.5.5.1 color-information properties; AV1-ISOBMFF section 2.3.4 configuration semantics | libavif `src/read.c` functions `avifParseColourInformationBox`, `avifReadColorNclxProperty`, and `avifReadColorProperties`, plus `src/write.c` function `avifEncoderWriteColorProperties`, at `092276ce89098ead06db80975173191e5fee1826` | Associate at most one ICC and one `nclx` property with each presented color image item or selected image-sequence sample entry, validate ICC payloads and CICP reserved bits, expose them through ImageSharp's existing profile types, and inherit a grid's CICP description only for tiles that do not declare one. For AV1 images, require every specified sequence-header CICP field and the range flag to match `nclx`; use the container property only to supply sequence-header fields coded as unspecified before color conversion, then expose the effective CICP values even when no property is present. Retain only the two image color profiles; do not add a reusable color-box or media model. | -| `HeifPropertyParser`, `HeifPixelAspectRatio`, item/track presentation state, and `HeifDecoderCore.ApplyItemPixelAspectRatioMetadata` | ISO/IEC 14496-12 section 12.1.4.3 pixel aspect ratio | libavif `src/read.c` function `avifParsePixelAspectRatioBox`, `src/write.c` functions `avifEncoderWritePaspProperty` and `avifEncoderWriteTransformativeProperties`, and the presented-image property selection in `src/read.c` at `092276ce89098ead06db80975173191e5fee1826` | Preserve the registered `pasp`, `clap`, `irot`, and `imir` presentation properties on an associated image item or selected image-sequence sample entry, reject invalid lengths, reserved bits, duplicates, and non-integral clean apertures, and map the displayed pixel ratio and transforms through ImageSharp's existing metadata and processing model. No generic transform or display model is introduced. | -| `Av1CodecConfiguration`, `HeifItem.Av1CodecConfiguration`, `Av1HeifItemDecoder`, and AV1 grid configuration checks | AV1-ISOBMFF sections 2.3.3 and 2.3.4 codec-configuration record syntax and semantics; AVIF sections 2.1, 2.2.1, and 2.2.3 AV1 image-item, item-configuration, and HDR metadata constraints; AV1 sections 5.8.3, 5.8.4, 6.7.3, and 6.7.4 HDR metadata syntax and semantics; ISOBMFF mastering-display and content-light image properties; ITU-T H.274 section 8.9 mastering-display field semantics; MIAF section 7.3.11.4.1 grid input constraints | libavif `src/read.c` functions `avifParseCodecConfiguration`, `avifDecoderItemValidateProperties`, `avifReadCodecConfigProperty`, `avifParseContentLightLevelInformation`, and `avifSkipMasteringDisplayColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; libaom `av1/decoder/obu.c` functions `read_metadata`, `read_metadata_hdr_cll`, and `read_metadata_hdr_mdcv` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Associate exactly one `av1C` property with each decoded `av01` image item, validate the fixed record and its bit depth/chroma fields against the item's AV1 sequence header and optional `pixi` channel depths, require matching configurations across grid tiles, and report the encoded image precision and monochrome shape through `HeifMetadata`. Validate low-overhead OBU framing, require exactly one sequence header in the image item, allow at most one first-position sequence header in `configOBUs`, and compare a repeated header's extension and payload exactly while ignoring only its legal size-field representation. Decode `clli` and `mdcv` as still-image item properties, validate matching HDR CLL and HDR MDCV metadata OBUs from the combined configuration/item sequence, and account for the different primary order and fixed-point precision of the ISOBMFF and AV1 MDCV representations. Expose the effective HDR values without adding sample groups, tracks, or media metadata. Related still-image HDR properties remain required. Consume but do not retain presentation-delay syntax, and introduce no sample entry, sample description, track, timing, or generic decoder-configuration model. | -| `HevcCodecConfiguration`, `HevcNalUnitArray`, and `HeifItem.HevcCodecConfiguration` | ISO/IEC 14496-15 `HEVCDecoderConfigurationRecord`, HEVC NAL-unit header syntax, and HEVC image-item configuration requirements | HM `source/Lib/TLibDecoder/NALread.cpp` and parameter-header paths at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` v1.6.0 decoder NAL and parameter-header paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; independently produced HEIC fixtures for `hvcC` record layout | Associate exactly one `hvcC` property with each `hvc1` image item; validate the fixed profile, level, chroma, bit-depth, reserved, length-size, array, and NAL-header fields within the property boundary; and retain only the image description plus bounded configuration NAL units required to decode that item. Consume but do not retain average-frame-rate or temporal-layer presentation fields. Do not add visual sample entries, sample descriptions, tracks, timing, access-unit timelines, or a generic decoder-configuration model. BSD and Apache-2.0 notice preservation and separate HEVC patent/release clearance remain final integration gates. | -| `HevcNalUnit`, `HevcNalUnitHeader`, `HevcRbspDecoder`, and `HevcBitReader` | HEVC sections 7.3.1.1 and 7.3.1.2 NAL-unit and header syntax, section 7.3.2.11 RBSP trailing bits, and section 9.1 Exp-Golomb parsing | HM `source/Lib/TLibDecoder/NALread.cpp` and `SyntaxElementParser.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` NAL extraction and bitstream paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Validate the fixed NAL header, remove only legal emulation-prevention bytes, and provide bounded fixed-width, flag, unsigned/signed Exp-Golomb, and RBSP-trailing-bit reads. Configuration arrays immediately use the shared parser. The primitives consume only NAL units delivered by a supported HEIF image item or its `hvcC` property and introduce no Annex B API, access-unit model, timeline, track, sample table, or generic ISO BMFF surface. | -| `HevcProfileTierLevel`, `HevcVideoParameterSet`, and `HevcCodecConfiguration.VideoParameterSets` | HEVC sections 7.3.2.1 video parameter sets, 7.3.3 profile-tier-level syntax, and E.2.2 hypothetical-reference-decoder syntax; ISO/IEC 14496-15 `HEVCDecoderConfigurationRecord` matching semantics | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` functions `parseVPS`, `parsePTL`, `parseProfileTier`, and `parseHrdParameters` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_headers.c` profile-tier-level and VPS paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Retain the base-layer VPS identifier, temporal-sublayer count, nesting flag, and exact general profile/tier/constraint/level fields. Validate profile, tier, compatibility, level, temporal count, and nesting against `hvcC`, while tolerating the independently observed legacy-muxer practice of zeroing some or all profile-specific constraint bits in the configuration record; SPS validation remains authoritative for chroma and bit depth. Consume but do not retain sublayer ordering, timing, and HRD syntax. Reject multilayer and layer-set state because the supported contract is one independently coded image item; add no layer-selection, access-unit, DPB, scheduling, track, or sample model. | -| `HevcSequenceParameterSet`, `HevcVideoUsabilityInformation`, `HevcScalingList`, `HevcShortTermReferencePictureSet`, and `HevcParameterSetSyntax` | HEVC sections 7.3.2.2 sequence parameter sets, 7.3.4 scaling-list data, 7.3.7 short-term reference-picture sets, E.2.1 VUI syntax, and Range Extensions SPS syntax | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` functions `parseSPS`, `parseScalingList`, `xDecodeScalingList`, `parseShortTermRefPicSet`, `parseVUI`, and `parseHrdParameters`, plus `source/Lib/TLibCommon/TComRom.cpp` default matrices and diagonal scans, at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_headers.c` SPS, VUI, scaling-list, and reference-set paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Retain coded/display dimensions, conformance crop, monochrome/4:2:0/4:2:2/4:4:4 and separate-plane layout, 8-through-16-bit precision, coding/transform/PCM geometry, effective scaling matrices, compression-tool flags, bounded reference-set declarations, Range Extensions tools, and still-image VUI aspect/color/range/chroma-location/display-window fields. Consume timing, HRD, ordering, and bitstream-restriction syntax without scheduling state. Reject interlaced fields and multilayer extensions because they do not represent the one progressive independently coded image-item contract; add no DPB pictures, playback state, tracks, or sample tables. | -| `HevcPictureParameterSet` and `HevcCodecConfiguration.PictureParameterSets` | HEVC sections 7.3.2.3 picture parameter sets, 7.3.4 scaling-list data, and Range Extensions PPS syntax | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` function `parsePPS`, `source/Lib/TLibDecoder/TDecConformance.cpp` PPS range checks, and `source/Lib/TLibCommon/TComPicSym.cpp` tile derivation at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_headers.c` PPS and tile paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Link each PPS to a configuration-local SPS and retain the CABAC, quantization, transform-skip, scaling-list, tile/wavefront, deblocking, SAO, slice-header, and Range Extensions controls required to reconstruct the independently coded picture. Derive the complete CTB tile grid and validate every SPS-dependent depth, size, precision, and offset bound during parsing. Reject multilayer extensions and add no access-unit, reference-picture lifetime, playback, track, timing, or sample-table model. | -| `HevcImageItemBitstream`, `HevcSliceSegmentHeader`, and `HevcSliceType` | HEVC sections 7.3.1.2 NAL-unit syntax, 7.3.6 slice-segment headers, and 7.4.7 slice-segment-header semantics; HEVC image-item length delimiting | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` function `parseSliceHeader`, `source/Lib/TLibDecoder/NALread.cpp`, and slice byte-alignment and entry-point handling at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_nal.c`, `ihevcd_parse_slice_header.c`, and `ihevcd_parse_headers.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Split only the selected `hvc1` image item's one-through-four-byte length-delimited NAL units, reject in-band parameter-set replacement and end-of-sequence state, and require exactly one base-layer IDR picture. Retain the intra slice, separate-plane, SAO, quantization, deblocking, cross-slice filtering, tile/wavefront entry-point, extension, byte-alignment, and entropy-payload fields required by reconstruction. Keep emulation-prevention positions only to translate slice substream byte boundaries; add no Annex B scanner, access-unit collection, non-IDR reference lifetime, playback, timing, track, or sample model. | -| `HevcHeifItemDecoder`, `HeifCompressionFactory`, and `HevcYuvConverter` coded-window output | HEVC sections 6.1 through 6.3 decoded picture and conformance-window semantics; ISO/IEC 14496-12 section 12.1.5 color information and ISO/IEC 23008-12 HEVC image-item binding | HM `source/Lib/TLibDecoder/TDecTop.cpp` decoded-picture output and `source/Lib/TLibCommon/TComPicYuv.cpp` conformance-window handling at `9c1f298659ab0cee9dc13d23d0304221575410b9`; ImageSharp's shared HEIF H.273 SIMD operators, pooled row conversion, and item-decoder lifecycle | Register only `hvc1` still-image items, bind the selected slice PPS/SPS to one picture decoder, convert the conformance-window region directly from native component planes without an intermediate packed image, and preserve the effective container-or-VUI CICP description used by conversion. Reuse the shared descending-width SIMD color operators and pixel packers with one pooled row workspace. Add no Annex B entry point, decoded-picture queue, inter-picture state, track, timing, or general-purpose HEVC API. | -| `HevcCabacContext` and `HevcCabacDecoder` | HEVC sections 9.3.3.2 through 9.3.3.4 context initialization, arithmetic decoding, bypass decoding, and termination | HM `source/Lib/TLibCommon/ContextModel.cpp`, `source/Lib/TLibCommon/TComCABACTables.cpp`, and `source/Lib/TLibDecoder/TDecBinCoderCABAC.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `common/ihevc_cabac_tables.c`, `common/ihevc_cabac_tables.h`, `decoder/ihevcd_cabac.c`, and `decoder/ihevcd_cabac.h` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Implement the bit-exact scalar probability-state transitions, range normalization, bounded byte refills, bypass modes, termination, and alignment validation for one slice tile or wavefront substream. CABAC decisions are serial and adaptive, so this primitive is the scalar oracle rather than a SIMD target; later prediction, transform, filtering, and color-conversion stages must reuse ImageSharp's compatible SIMD kernels. The decoder owns no container boxes, access units, reference pictures, tracks, timing, playback, or general-purpose bitstream surface. | -| `HevcCabacContexts` | HEVC section 9.3.2.2 initialization of context variables for an intra-coded slice and Range Extensions context additions | HM `source/Lib/TLibCommon/ContextTables.h` and `source/Lib/TLibDecoder/TDecSbac.cpp` function `resetEntropy` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `common/ihevc_cabac_tables.c` and `decoder/ihevcd_cabac.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Initialize only the context groups reachable from the independently coded intra-picture syntax, including transform skip, chroma quantization adjustment, and cross-component prediction. Keep one contiguous mutable context set per entropy substream so tile and wavefront synchronization can copy the normative adaptive state without retaining reference-picture or playback state. Omit skip, merge, motion, reference-index, and other inter-picture-only groups. | -| `HevcCabacSyntaxReader` | HEVC sections 9.3.3 through 9.3.4 CABAC binarization and context selection for intra coding units, transform trees, residual coefficients, sample-adaptive offset, and Range Extensions tools | HM `source/Lib/TLibDecoder/TDecSbac.cpp`, `source/Lib/TLibCommon/TComChromaFormat.h`, and `source/Lib/TLibCommon/TComTrQuant.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_cabac.c`, `decoder/ihevcd_parse_slice.c`, and `decoder/ihevcd_parse_residual.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Decode only syntax values reachable while reconstructing the selected independently coded still picture: intra partition and mode selectors, transform splitting and coded-block flags, quantization adjustments, transform skip, coefficient significance and levels, cross-component prediction, SAO, bypass alignment, and termination. Keep neighbor-dependent context selection in the owning coding-tree/reconstruction layer and expose no inter prediction, reference-picture, access-unit, track, timing, or playback syntax. | -| `HevcCoefficientScanOrder`, `HevcCoefficientCodingParameters`, and `HevcCoefficientDecoder` | HEVC sections 9.3.4.2.4 through 9.3.4.2.8 last-position, significance-map, level, sign-data-hiding, and Rice-adaptation processes; Range Extensions transform-skip context and bypass-alignment rules | HM `source/Lib/TLibCommon/TComRom.cpp` grouped scan generation, `source/Lib/TLibCommon/TComChromaFormat.cpp` transform-unit entropy parameters, `source/Lib/TLibCommon/TComTrQuant.cpp` significance-context derivation, and `source/Lib/TLibDecoder/TDecSbac.cpp` function `parseCoeffNxN` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_residual.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7`; ImageSharp `MemoryAllocator` and fixed inline buffers | Generate diagonal, horizontal, and vertical grouped scans into entropy-substream-owned scratch; derive directional scan selection, last-significant positions, right/lower group contexts, significant coefficient flags, greater-than-one/two levels, escape remainders, sign hiding, and the four persistent Rice states. Allocate one pooled scratch block per entropy substream and perform no allocation or dynamic stack growth per transform block. This serial adaptive stage intentionally remains scalar and feeds the existing SIMD-first inverse quantization, inverse transform, transform-skip, and residual-reconstruction kernels. | -| `HevcPlane` and `HevcPictureBuffer` | HEVC sections 6.2 and 6.3 source and decoded picture sample-array dimensions for monochrome, 4:2:0, 4:2:2, 4:4:4, and separate-color-plane coding | HM `source/Lib/TLibCommon/TComPicYuv.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` decoded-picture buffer layout paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; ImageSharp `MemoryAllocator` and `Buffer2D` | Allocate one allocator-owned native `ushort` plane set for the selected still picture so the same reconstruction path preserves every supported 8-through-16-bit sample without byte-backed reinterpretation. Derive chroma dimensions by ceiling division for each sampling layout and treat separate color planes as full-resolution independently coded arrays. The buffer contains one picture only and introduces no decoded-picture buffer, reference lifetime, frame queue, or playback state. | -| `HevcCodingTreeState` | HEVC sections 6.4 coding-tree block and coding-block availability plus section 9.3 split-flag context derivation | HM `source/Lib/TLibCommon/TComDataCU.cpp` function `getCtxSplitFlag` and `source/Lib/TLibDecoder/TDecCu.cpp` coding-tree traversal at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_slice.c` coding-tree paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; ImageSharp `MemoryAllocator` and `Buffer2D` | Store only leaf depth, effective luma QP, transquant-bypass, and PCM state at minimum-coding-block resolution for the selected picture. Derive split contexts from caller-approved left and above availability so slice and tile boundaries remain owned by traversal rather than hidden in a general block graph. Clip edge writes to the coded still-picture state and add no prediction units, motion fields, references, decoded-picture queue, or sequence lifetime. | -| `HevcIntraPredictionState` | HEVC sections 8.4.2 and 9.3 intra luma/chroma prediction-mode derivation and binarization | HM `source/Lib/TLibCommon/TComDataCU.cpp` functions `getIntraDirPredictor` and `getAllowedChromaDir`, plus `source/Lib/TLibDecoder/TDecSbac.cpp` functions `parseIntraDirLumaAng` and `parseIntraDirChroma`, at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` intra-mode parsing and neighbor derivation paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; ImageSharp `MemoryAllocator` and `Buffer2D` | Preserve the normative two-pass luma flag/suffix order, spatial most-probable-mode derivation, omitted-mode reinsertion, four-way minimum-CU partition order, explicit chroma candidate substitution, and derived-chroma mode at 4x4 luma resolution. Accept slice/tile availability from traversal, fill only the selected still-picture map, and add no inter prediction unit, motion, reference, or sequence state. | -| `HevcIntraPredictor` | HEVC section 8.4.4.2 intra sample prediction and section 8.4.4.2.3 reference-sample substitution and filtering | HM `source/Lib/TLibCommon/TComPrediction.cpp` functions `predIntraGetPredValDC`, `xPredIntraAng`, `xPredIntraPlanar`, and `xDCPredFiltering`, plus `source/Lib/TLibCommon/TComPattern.cpp` function `fillReferenceSamples` and its reference-filter selection at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` intra-prediction kernels at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Predict from caller-prepared references with planar, DC, and all 33 angular modes; negative-angle extension; three-tap and strong-bilinear smoothing; and the normative luma boundary filters. Traverse `Vector512`, `Vector256`, and `Vector128` widths before one scalar tail, use caller-owned reusable scratch for horizontal transposition, and retain no generic video, inter-picture, or reference-picture state. | -| `HeifContentColorVolume`, `HeifItem.ContentColorVolume`, and `HeifDecoderCore` content color-volume parsing and presentation | HEIF content color-volume item property; AVIF 1.2 content color-volume requirements; ITU-T H.274 (V4) content colour volume syntax and semantics | libavif `src/read.c` function `avifSkipContentColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode only the bounded per-image `cclv` property: require zero cancellation, persistence, and reserved bits; preserve optional signed G/B/R primary coordinates and normalized minimum, maximum, and average luminance values; and validate their registered ranges and ordering. Expose the effective grid-or-tile still-image value through `HeifMetadata`. Do not add SEI persistence, retained video state, tracks, samples, timing, or a generic ISO BMFF color-volume box model. | -| `HeifAmbientViewingEnvironment`, `HeifReferenceViewingEnvironment`, `HeifNominalDiffuseWhite`, and their per-item presentation metadata | ISOBMFF ambient viewing environment; ITU-T H.274 (V4) section 8.13; HEIF Amendment 1 sections 6.5.44 and 6.5.45; AVIF 1.2 image-item box requirements | libavif `src/read.c` functions `avifSkipAmbientViewingEnvironment`, `avifSkipReferenceViewingEnvironment`, and `avifSkipNominalDiffuseWhite` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode the fixed-size `amve`, version-zero `reve`, and version-zero `ndwt` properties only when associated with the presented still-image item. Preserve physical illuminance and luminance units, distinct surround/periphery chromaticities, and the coded zero that requests the standard nominal diffuse-white default. Validate registered coordinate ranges and duplicate associations. Retain no video-SEI persistence, visual sample entry, display pipeline, track, timing, or generic viewing-environment box model. | -| `GridHeifItemDecoder` and `HeifDecoderCore` grid/thumbnail selection | ISO/IEC 23008-12 section 6.6.2.3 image-grid syntax and MIAF grid-cell constraints | libavif `src/read.c` functions `avifParseImageGridBox`, `avifDecoderDataAllocateImagePlanes`, and `avifDecoderDataCopyTileToImage` at `092276ce89098ead06db80975173191e5fee1826` | Parse version-zero 16-bit and 32-bit grid descriptors, preserve row-major `dimg` order, require the declared tile count and one coding format, validate canvas coverage and edge overlap, and crop only the rightmost column and bottom row while copying through ImageSharp pixel buffers. A primary grid whose tile codec is unavailable may use only a decodable thumbnail that explicitly references that grid. | -| `HeifDecoderCore` alpha auxiliary selection/composition and `GridHeifItemDecoder` auxiliary tile ordering | ISO/IEC 23008-12 alpha auxiliary image semantics, `auxC`, `auxl`, `prem`, and per-grid-tile alpha relationships | libavif `src/read.c` functions `avifParseAuxiliaryTypeProperty`, `avifDecoderItemIsAlphaAux`, `avifMetaFindAlphaItem`, and `avifDecoderCheckAlphaProperties`, plus `src/scale.c` box-filter scaling at `092276ce89098ead06db80975173191e5fee1826` | Recognize both registered alpha URNs, decode a direct alpha image/grid or the complete row-major set of per-color-tile alpha auxiliaries, normalize through `L16`, box-resample differing auxiliary dimensions, compose through `Rgba64` and `PixelOperations`, and unassociate `prem` color samples with transparent-black handling. This remains an image-item relationship only; no track or generic media-reference model is introduced. | - -This table is intentionally incomplete. Add a row before each additional AV1 or HEVC algorithm is ported or materially reshaped. - -## Current implementation assessment - -This assessment was reconciled with the source tree on 2026-08-30, including production checkpoint `9d7348efd6496b54cef7ec27e42352641439c400`. Unless a result is stated explicitly, each item is a source-inspection finding rather than a verified interoperability claim. - -### Public integration - -- `HeifFormat` combines the HEIF, HEIC, HIF, and AVIF identities and extensions, but the implementation does not yet decode all payloads that contract implies. -- `HeifDecoder` now defaults to `Rgba32`, preserving decoded auxiliary alpha for non-generic loads. -- `HeifMetadata` now reports alpha presence and the corresponding 24/32-bit RGB pixel shape, but complete decoded HEVC/AV1 bit depth, monochrome/chroma layout, color signaling, and profiles remain absent. -- `HeifEncoder` defines quality, alpha quality, effort, lossless, chroma-subsampling, and bit-depth contracts directly, without a single-implementation options interface. The legacy JPEG path applies its supported quality, bit-depth, and chroma options and rejects unsupported combinations; AV1 and HEVC must implement the same public contracts before the Phase 1 API-review gate can pass. -- HEIF/HEIC/AVIF is absent from the format source-generation list in `_Formats.ttinclude`, so the standard ImageSharp save extensions are not generated. -- Configuration registration exists, but it currently registers capabilities broader than the implementation provides. - -### HEIF/ISO BMFF container - -- The bounded reader now handles basic, extended-size, UUID, and permitted top-level to-end boxes, skips unknown optional top-level and metadata children, and rejects child boxes that escape their parent. -- File type handling evaluates supported still-image major and compatible brands and recognizes the bounded `avis`, `hevc`, and `hevx` image-sequence brands. The sequence parser retains only the selected picture and auxiliary-alpha tracks, timing, sample locations, dependencies, presentation properties, and image metadata required by animated HEIF; unrelated media tracks and general ISO BMFF playback state are not materialized. -- Item IDs are resolved as keys rather than list indices; metadata children are indexed and parsed in dependency order rather than physical order. -- Item property associations now preserve physical `ipco` indices, apply one-based 7-bit or 15-bit indices and essential flags, associate by item ID, and reject arbitrary unknown essential properties. -- Item locations now support bounded file-relative and `idat`-relative storage, multiple ordered extents, versioned item IDs, 0/4/8-byte registered field sizes, and 64-bit offsets. Referenced-item construction method two and external data references remain explicitly unsupported. -- Grid derived-image decoding now parses both registered descriptor widths, resolves the ordered `dimg` cells, validates tile count, coding format, dimensions, canvas coverage, and edge overlap, then composes the output through ImageSharp row buffers. Unsupported grid tile codecs can select only a decodable thumbnail of the same primary grid. HEVC grids have exact complete-image libheif references and exact HM tile-plane references; independent AV1 and JPEG grid fixtures are still required. -- Alpha auxiliary decoding recognizes `auxC`, `auxl`, and `prem`, supports direct auxiliary images, auxiliary grids, and per-color-grid-tile alpha, and reports alpha presence. The common-size path normalizes the native luma plane and composes each row directly into the destination; the scaled path uses allocator-owned row staging and the bounded ImageSharp resize window instead of materializing a complete `Image`. HEVC auxiliary-alpha presentation matches the independent libheif reference exactly, and independently encoded AVIF alpha fixtures cover composed alpha and ICC interaction. -- Clean aperture, image rotation, and image mirror properties now validate their registered payloads, exact integer crop geometry, and essential associations; affect Identify dimensions; and reuse ImageSharp's optimized crop/rotate/flip processors after auxiliary alpha composition in the MIAF-defined order. Independent transform vectors must still verify every crop/rotation/mirror/alpha combination. -- Decode now resolves `cdsc`-associated Exif and `application/rdf+xml` XMP items for the primary still image, validates the declared Exif TIFF-header offset, and attaches the payloads through ImageSharp's existing profile types before presentation transforms. Independent AVIF, HEIC, and HIF metadata fixtures and Identify-time profile reporting remain required. -- ICC and `nclx` CICP color properties are now associated with the presented color item instead of global parser state, validated, and exposed on Decode and Identify through the existing ImageSharp profiles. AV1 decode requires specified sequence-header fields and the range flag to match `nclx`, uses the property only for fields the bitstream leaves unspecified, and exposes the effective sequence-header CICP description when no property is present. Independent libavif fixtures verify exact ICC byte preservation, observable non-sRGB conversion, canonical-sRGB compaction, non-sRGB compact preservation, and metadata skipping for primary, grid, alpha-composited, and every presented sequence frame; the alpha vector also verifies that ICC conversion does not alter composed alpha. Identify-time bitstream fallback, independent CICP vectors, and HEVC integration remain required. -- 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 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. -- 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 - -- `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, 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. -- 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 - -- The bounded `Av1Decoder` session parses all tile state before allocating and reconstructing each coded image layer or sequence sample. After successful completion it retains the ungrained reference planes, frame header, frame information, and published entropy snapshot in the refreshed slots, while presentation-only ownership remains separate. A new accepted sequence header resets both parser and retained-owner state. Bounded image-sequence decoding accepts `show_existing_frame`, including its key-frame reference reset and separate film-grain presentation ownership, without exposing a general animation/video reference model. -- Committed inter-frame support parses single-reference selection, builds the fixed-capacity spatial and temporal reference-MV stack, decodes NEAREST/NEAR/NEW/GLOBAL and DRL syntax, decodes or infers interpolation filters, checks inter-intra and motion-mode eligibility, and invokes simple translational prediction before residual reconstruction. Commit `8b9aa4b2cfe59ff0f8c348d9bb06678a5ff12f2a` records exact corrected dependent-frame native-plane and final-presentation comparisons plus the complete focused Release matrix and ownership/allocation evidence. -- Commit `e1a764b8f2e8a8db98a003fb3b913213f40084db` owns every compound reference-selection and inter-mode distribution through the retained CDF lifecycle, derives primary and secondary candidate vectors as inseparable pairs, reconstructs both retained references through the existing translational predictor, and combines them through one SIMD-first byte/ushort averaging operator with scalar parity. The pinned 19-frame libavif sequence exercises the complete production block branch and matches pinned libaom native planes and pinned libavif presentation exactly after decoding all preceding reference samples. -- Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` reconstructs eligible above and left OBMC neighbors through the retained primary reference, exact libaom masks, and the existing SIMD-first masked blender without per-block allocation. Direct production tests cover 8/10/12-bit storage plus 4:2:0 and 4:2:2 plane geometry. A pinned 19-frame sequence contains more than one hundred traced OBMC blocks and passes exact final native-plane, final-presentation, constrained-allocation, normal-dispatch, and scalar-dispatch comparison. -- Commit `8f57c29b250b4291f034b31c1ea3de3fa3345467` maps current-frame Q4 positions into retained-frame Q10 coordinates with pinned-libaom Q14 scale factors and signed rounding, then applies allocation-free variable-phase two-pass convolution across byte and high-bit-depth storage. The production block path selects scaling from actual retained/current dimensions, covers both primary and secondary workspaces plus scaled OBMC neighbors, and retains libaom's 288-sample prediction border. The real layered fixture also exposed and now verifies per-block inter loop-filter reference/mode deltas and skipped-transform edge decisions that the earlier intra-only deblocking integration did not model. -- Commit `0b2db86809e26757460e719a6b59c0fe58bb0586` derives AV1's local affine matrix from fixed-capacity spatial samples with pinned integer least squares, reciprocal lookup, coefficient clamping, and shear validation. Production reconstruction applies the pinned 193-phase Q7 warped filter in allocation-free 8x8 tiles through `Vector128` convolution with scalar fallback for 8/10/12-bit planes; an invalid selected model falls back to translational prediction as libaom does. -- Commit `c5637ea0187df35b385bf43e2fe85cd955f01099` selects the full frame-header rotation/zoom or affine matrix independently for each `GLOBAL_GLOBALMV` reference, while identity, translation, invalid shear, scaled references, integer-only frames, OBMC, and sub-8x8 plane predictions retain their normative translational path. A pinned bounded sequence proves seven actual single-reference `GLOBALMV` blocks through exact native and presentation output, and a direct `FeatureTestRunner` production test proves both compound matrices at 8, 10, and 12 bits. -- Source inspection against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f` resolves two disputed audit claims in favor of the current predicates. The interpolation call path uses `is_nontrans_global_motion()`, which returns false only for `TRANSLATION`, so an identity `GLOBALMV` block omits filter symbols. The single-reference spatial extension loops use `MAX_MV_REF_CANDIDATES`, which is two, while the full reference-MV stack capacity is eight. These loops are spatial extension, not temporal extension. -- Every padded AV1 frame plane requests ImageSharp's contiguous allocation contract, and constrained-allocator reconstruction passes without copying. Motion fields use allocator-owned storage and counted leases; allocation tracking passes for initialization, retained aliases, failure unwinding, presentation ownership, decoder-result ownership, and exactly-once disposal. The corrected logical progressive color payload reaches the production inter branch and its final native planes match pinned libaom exactly. -- Transform coefficient entropy derivation and updates now address the above contexts relative to the tile column and the left contexts relative to the current superblock row, preserve luma coordinates independently of chroma subsampling, and test every packed context entry for the libaom any-nonzero rule. Extended vertical partition updates advance the mode-information column rather than the row. The existing multi-superblock 4:4:4 AVIF fixture now completes tile parsing; independent coefficient-context vectors across tile boundaries, chroma layouts, bit depths, and edge-clipped transforms remain required. -- The reconstruction pipeline now records plane-relative transform geometry, preserves tile-local delta-Q and delta-LF predictors, derives segmentation and reference-adjusted filter levels, and runs the exact AV1 4-, 6-, 8-, and 14-tap deblocking kernels in normative vertical-then-horizontal order. Deblocking uses the same closed edge-operator architecture as the HEVC filter, with operators specialized by sample storage and orientation, `Vector128` lanes representing the four rows or columns along an edge, and an allocation-free scalar fallback for disabled intrinsics. Exact native-plane comparison with pinned scalar libaom output now verifies active deblocking and complete reconstruction for real 8-bit 4:2:0, 10-bit 4:4:4, and 12-bit 4:4:4 content; genuine AVIF containers separately verify presentation and public bit-depth metadata. The pipeline then applies CDEF through one semantic filter architecture: paired AVX2 and single-block `Vector128` direction analysis, closed primary/secondary strength operators, packed 4x4/4x8/8x4/8x8 constrained filtering, byte/16-bit output operators, and an exact allocation-free scalar fallback. Decoder orchestration now owns the immutable plane snapshots and clean direction/variance maps through ImageSharp's memory allocator, widens 8-bit source rows with the same AVX2/128-bit/scalar tiers as libaom, lists each unit's non-skipped blocks in fixed inline storage, analyzes listed blocks in pairs, and writes filtered bytes or 16-bit samples directly to the frame planes. Independently encoded active-CDEF 8-bit 4:2:0 and 10/12-bit 4:4:4 streams match every visible native sample produced by pinned scalar libaom with restoration disabled. Independently encoded AVIF containers at the same three bit depths also match pinned scalar-libavif presentation exactly under normal, 256-bit, 128-bit, and scalar color-conversion dispatch. Active super-resolution derives the Appendix A bounded coded width and applies the exact 64-phase, 8-tap horizontal filter with aligned reconstruction-edge input, 8/10/12-bit clipping, and the existing cross-platform `Vector128_.MultiplyAddAdjacent` helper. Independently encoded active-super-resolution AV1 streams at 8, 10, and 12 bits match every visible native sample produced by pinned scalar libaom under normal and forced-scalar dispatch. Independently packaged AVIF containers at the same bit depths retain matching libavif profile, dimensions, chroma, and CICP properties, require active super-resolution in their actual AV1 item, and match pinned scalar-libavif presentation exactly under normal, 256-bit, 128-bit, and scalar color-conversion dispatch. Loop restoration follows super-resolution, preserves the required pre-CDEF deblocked context at internal stripes, and applies decoded Wiener or self-guided units from immutable plane snapshots. Independently encoded active-restoration streams at 8, 10, and 12 bits now match every native sample from pinned scalar libaom across AVX2, 128-bit, and scalar dispatch, with the fixture matrix proving both Wiener and self-guided unit selection. The matching AVIF containers also match pinned scalar-libavif presentation exactly, and an independent direct-window definition verifies all sixteen self-guided parameter sets at each supported bit depth across vector and scalar dispatch. Combined 8-bit 4:2:0, 10-bit 4:2:2, and 12-bit 4:4:4 streams additionally verify restoration-unit boundaries after super-resolution, including clipped chroma transform traversal at a coded-frame edge. -- The visible still-image path applies the complete self-contained film-grain parameter set after all in-loop filters. Independently encoded pinned-libaom vectors match every native sample at 8, 10, and 12 bits across monochrome, 4:2:0, 4:2:2, and 4:4:4 layouts, full and restricted ranges, identity-matrix signaling, overlap, and odd 33x11 frame extension. `FeatureTestRunner` verifies normal, AVX-disabled, and fully scalar dispatch. A full-HD-equivalent 4:2:0 benchmark reports zero allocation: 8-bit AVX2 is 2.335 ms versus 5.806 ms scalar, while 12-bit AVX2 is 3.195 ms, cross-platform 128-bit is 7.382 ms, and scalar is 8.614 ms on the measured Ryzen platform. The slower 8-bit 128-bit lookup construction is deliberately not dispatched. -- Palette mode now reads the normative luma/chroma mode and size CDFs, neighbor color caches, high-bit-depth color syntax, diagonal color-index maps, and clipped-edge padding. Reconstruction retains libaom's exact indexed-palette contract while using repeated eight-entry tables and native lane-local shuffles across runtime-selected `Vector512`, `Vector256`, and `Vector128` paths before an exact scalar fallback. `FeatureTestRunner` verifies palette sizes 2-8, transform widths 4-64, padded rows, and 8/10/12-bit samples. An independently encoded 33x11 8-bit YUV 4:4:4 AVIF fixture selects both luma and chroma palettes, matches every native sample from pinned scalar libaom, and matches scalar-libavif presentation exactly across the available vector widths and scalar fallback. -- Non-reduced still-image sequence parsing now consumes decoder-model operating parameters, temporal presentation fields, and OBU-layer-applicable buffer-removal fields only to preserve AV1 bit alignment. The scheduling values are not retained, and no movie, track, timing, playback, or generic ISO BMFF surface has been introduced. Existing focused sequence-header coverage exercises only reduced-still syntax, so an independent non-reduced still AVIF vector remains required. -- Loop-restoration unit parsing records tile-local switchable/Wiener/self-guided filter selections and coefficients in frame-owned plane grids, including super-resolution-adjusted unit corners and the corrected conditional 64x64-superblock unit-size bit. The active restoration stage implements the normative unit geometry, striped deblocked boundaries, Wiener filtering, self-guided projection, and 8/10/12-bit clipping. Self-guided filtering now follows libaom's summed-area-table, coefficient-grid, alternating-row radius-two, full radius-one, and projection stages through AVX2 and cross-platform 128-bit traversals with one exact scalar fallback and caller-owned scratch. Independently encoded fixtures cover active Wiener and self-guided reconstruction and exact AVIF presentation at every supported bit depth. An independent direct-window definition covers all sixteen self-guided parameter sets, narrow and odd processing units, both vector-tail widths, padded strides, and the scalar fallback. Combined active-restoration and super-resolution fixtures cover 4:2:0, 4:2:2, and 4:4:4 at 8, 10, and 12 bits, including restoration-unit boundaries and clipped chroma transform traversal. Other normative independently decodable still-image syntax paths still contain `NotImplementedException` or equivalent unsupported branches. Tile-local palette CDF adaptation is present; the remaining still-image frame-context behavior requires a separate source audit without introducing sequence playback state. -- The frame buffer now establishes two-byte native sample storage, logical plane rows, and sample-unit block strides for 10/12-bit frames. The active intra-prediction, inverse-transform, and block-reconstruction path selects native 16-bit samples for 10/12-bit frames and has focused pipeline wiring coverage. Chroma-from-luma storage, 4:4:4/4:2:2/4:2:0 subsampling, rounded mean subtraction, U/V sharing, and 8/10/12-bit prediction now traverse AVX2 and cross-platform `Vector128` paths before an exact scalar fallback. `FeatureTestRunner` verifies every tier against independent fixed-point definitions across every supported block width; independently encoded high-bit-depth and chroma-from-luma AVIF conformance files are still required. -- `Av1YuvConverter` now consumes the signaled full or limited range, every non-reserved AV1 H.273 matrix coefficient, transfer characteristics where the matrix definition requires them, subsampling, and chroma sample position for 8, 10, and 12-bit output. Its high-bit-depth decode and encode paths use allocator-backed `Rgb48` rows and the existing `PixelOperations` conversions, avoiding the former eight-bit intermediate. Encoder conversion covers monochrome, YUV 4:2:0, 4:2:2, and 4:4:4 with libavif-compatible box averaging. Identity, full/limited-range YCgCo, the fixed non-constant-luminance matrices, both fixed and chromaticity-derived constant/non-constant-luminance systems, SMPTE ST 2085, and PQ/HLG ICtCp are active in both directions. Independent vectors for every matrix, transfer, range, bit depth, sampling layout, and chroma position remain required before the complete color matrix is externally verified. -- Forward and inverse transforms use operation-owned workspace, explicit sequential fixed storage, and stateless static-generic operator structs for every valid DCT, ADST, and identity size. Named configuration factories keep the encoder's three shifts and variable cosine precision separate from the decoder's two shifts, fixed 12-bit cosine precision, and 8/10/12-bit clamp ranges. Forward traversal shares one libaom-shaped stage network across scalar, `Vector128`, `Vector256`, and `Vector512`; inverse production traversal uses the verified scalar, `Vector128`, and `Vector256` tiers. Lossless segments now bypass the DCT pipeline and apply the reversible four-by-four inverse Walsh-Hadamard transform through the same byte/high-bit-depth output operators, with a `Vector128` production path and caller-workspace scalar fallback. The focused Release matrix passes across the `FeatureTestRunner` hardware configurations, and the production transform benchmarks report zero allocation. Commit `ef93d584511055f8e91e8d801662c44a5b0984d9` records the separately audited 12-bit inverse ADST4, Identity4, and Identity16 widening correction and closes the Phase 3 transform correctness gate. -- Core intra prediction, chroma-from-luma, palette reconstruction, and nonlinear self-guided restoration now use SIMD-first operator or packed-kernel architectures with exact scalar fallbacks. Self-guided restoration dispatches portably through `Vector256`, `Vector128`, and scalar tiers; AVX2-only gather and scan operations remain local fast paths within the 256-bit implementation. `FeatureTestRunner` verifies AVX2, portable AVX-only 256-bit, 128-bit, and scalar execution. No namespace, file, or type name exposes SIMD width, ISA, storage, or bit depth. Transform traversal is already SIMD-first, while normative super-resolution and Wiener horizontal products reuse ImageSharp's cross-platform adjacent multiply/add SIMD helper with exact scalar fallbacks. - -### AV1 encoder - -- `HeifEncoderCore.Encode()` is synchronous, matching the ImageSharp encoder contract. -- The production switch writes only an explicitly selected legacy JPEG image item. It throws for `HeifCompressionMethod.Av1` and `HeifCompressionMethod.Hevc`, so it produces neither AVIF nor HEIC. -- `Av1FrameEncoder.Encode()` is an outline of an SVT-style pipeline rather than an implementation. -- Required mode-decision, block-geometry, forward-transform, token-writing, neighbor-context, palette, intra-block-copy, transform-size, quantization/rate-control, and OBU-writing paths are absent or throw. -- Encoder tests currently cover the public option contract and legacy JPEG boundaries. There is no AV1 or HEVC payload output to cross-decode. - -### Legacy JPEG image items - -- A JPEG item decoder exists, and the Fujifilm `.hif` fixture is identified and decoded as a legacy JPEG image item. -- The encoder uses the existing ImageSharp JPEG codec only when `HeifCompressionMethod.LegacyJpeg` is explicitly selected and emits a `jpeg` primary item. -- The legacy JPEG path still needs independent container interoperability coverage, but its quality, bit-depth, lossless, and chroma restrictions are explicit at the public option and codec boundaries. - -### Tests - -- 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. -- 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 --scaling-mode:u 1/1 `. 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. -- The selected-layer derivative adds one essential `lsel` association selecting spatial layer zero without changing the logical AV1 payload. Its absolute `iloc` extent offsets move by eleven bytes with the enlarged property containers, and pinned libavif linked to the same libaom commit presents the selected 40x40 base at the item's 80x80 `ispe` extent. The 2,505-byte AVIF has SHA-256 `E6E1F53F8DD558C5309E0C80B0635FA4EFCBB636541E4E38206985A91E0FBA66`; its exact pinned-libavif PNG has SHA-256 `678C5E5D4650EA6F0C590302E7DB9E3C6608851BC577453DA4A6837BDB4D3AF3`. -- The AV1 transform matrix verifies scalar, `Vector128`, `Vector256`, and `Vector512` forward representations plus the production inverse tiers across every valid size/type combination and supported bit depth. All 511 focused forward and inverse cases pass in Release; `FeatureTestRunner` isolates hardware tiers, every two-dimensional configuration exercises production dispatch, and the complete-block benchmark records zero managed allocation. -- Independent libavif fixtures cover primary, grid, auxiliary-alpha, ICC, metadata-skipping, and all-sync image-sequence presentation. Reference-dependent AV1 and HEVC sequence reconstruction and independent HEVC ICC sequence coverage remain open. -- Focused decoder-option tests cover strict, ancillary-only, image-data, and metadata-skipping behavior for still items and sequence samples. Complete adversarial dimension, allocation, malformed-container, and resource-limit coverage remains open. -- No cross-codec encode test exists for AV1 or HEVC because both production encoding branches still throw before producing a payload. - -## Architectural direction - -### Public format identities, shared internal HEIF container - -Expose HEIC and AVIF as distinct public format identities backed by one internal HEIF/ISO BMFF container implementation. Provide format-appropriate decoders, encoders, metadata, options, MIME types, extensions, and generated save methods. Generic `.heif` and `.hif` input must be dispatched by brands and primary item type to HEVC, AV1, or legacy JPEG rather than by extension. Avoid a broad internal namespace rename until the container model is stable. - -Name folders, namespaces, and types for the image-format behavior they own. SIMD width, ISA, sample storage, and bit depth remain internal operator or dispatch details; do not create `.Simd`, `.LowBitDepth`, width-specific, or equivalent implementation-mechanic namespaces, files, or type suffixes. - -Generate the standard sync and async `SaveAsHeic` and `SaveAsAvif` overloads through the existing format templates. If a generic `SaveAsHeif` API is retained, its encoder options must require an explicit supported payload codec rather than infer one from an extension. Public XML documentation must describe observable behavior and option effects only. - -### Parsed container model - -Parse the meta box into an immutable logical model before decoding payloads: - -- dictionaries keyed by item ID, not collection position; -- a one-based property table with explicit association records and essential flags; -- resolved item references (`dimg`, `auxl`, `thmb`, and metadata relationships); -- item locations represented as validated extents and construction methods; -- typed properties for `ispe`, `pixi`, `hvcC`, `av1C`, `colr`, `auxC`, `pasp`, `clap`, `irot`, and `imir`; -- bounded views over item payloads, independent of source box order; and -- explicit primary, alpha, grid-tile, thumbnail, Exif, and XMP roles. - -Use existing ImageSharp buffered stream and allocation abstractions. Do not copy the whole file into an unbounded byte array. Every length, offset, multiplication, allocation, tile count, and image dimension coming from the file is an external boundary and must be checked against the enclosing box and ImageSharp limits. Internal decode stages should rely on the validated model rather than repeat defensive checks. - -### Codec state and sample storage - -Keep HEVC and AV1 bitstream state in separate codec implementations. Within each codec, separate parameter/sequence state, frame or picture headers, tile/slice entropy state, and reconstructed images. Still-image operations retain no reference pictures; bounded image-sequence operations retain only the reference frames required by their declared dependencies. Give each allocation one owner and a deterministic disposal point. - -Represent 8-bit samples with bytes and high-bit-depth samples with unsigned 16-bit storage. Plane dimensions and strides must reflect monochrome and chroma subsampling instead of pretending every plane is full-resolution 4:4:4. Design storage and reconstruction operators for SIMD-first traversal, with the scalar fallback sharing the same layout and contract for behavioral parity. - -### Decode directly into ImageSharp pixels - -Perform chroma upsampling, range expansion, matrix conversion, alpha composition, and pixel packing into allocator-backed row buffers or the destination frame. Use `PixelOperations` and the existing packed pixel conversion paths instead of creating an intermediate `Image`. - -Do not reuse JPEG or WebP color constants merely because those codecs already contain vectorized YUV conversion. Reuse their vector dispatch, lane handling, row processing, and pixel packing patterns only when the signaled HEVC/AV1 range and matrix semantics are preserved. - -## Existing ImageSharp code to reuse - -| Need | Reuse target | Constraint | -| --- | --- | --- | -| Stream parsing | `BufferedReadStream`, existing endian readers, bounded decoder-core patterns | HEIF box extents remain the source of truth. | -| Memory ownership | `MemoryAllocator`, `IMemoryOwner`, `Buffer2D`, allocator-backed row buffers | No unbounded file-sized arrays or per-block allocations. | -| Pixel output | `PixelOperations`, `Rgba32`, `Rgba64`, existing packed conversion methods | Preserve alpha and high-bit-depth precision. | -| SIMD utilities | `SimdUtils`, `Vector128_`, `Vector256_`, `Vector512_`, `Numerics` helpers | Add codec-specific math only where semantics differ. | -| SIMD structure | JPEG color-converter factories and WebP YUV row converters | Reuse dispatch/tail patterns, not incompatible coefficients. | -| Decode lifecycle | JPEG/WebP decoder cores, ImageSharp cancellation and dimension-limit handling | Identify must not reconstruct pixels. | -| Public format API | PNG, JPEG, and WebP format/decoder/encoder/options/metadata patterns | Keep HEIC, AVIF, and generic HEIF dispatch behavior explicit. | -| Generated save APIs | `_Formats.ttinclude` and existing format templates | Generate `SaveAsHeic` and `SaveAsAvif`; require an explicit codec for generic HEIF output. | -| Metadata | Existing ICC, Exif, XMP, and CICP-related metadata structures where available | Preserve payloads and apply only specified transforms. | -| Test infrastructure | `ReferenceCodec`, image comparers, feature-test helpers, codec test base classes | Include independent artifacts and cross-codec tests. | -| Performance tests | Existing ImageSharp benchmark project and hardware-intrinsics test controls | Benchmark real decode stages and representative images. | - -## Implementation phases - -Every phase exit gate is an internal prerequisite for the next phase. Only the Phase 9 exit gate together with a fully passing verification matrix marks the PR complete. - -### Phase 0: establish a reproducible baseline - -Tasks: - -- [ ] Build the merged solution in Release and record compile errors and warnings attributable to the WIP. -- [ ] Run only the existing HEIF/HEVC/AV1 tests first, then record disabled tests and unexecuted asset coverage. -- [ ] Record the pinned official libaom, local libavif, HM, and Android `libhevc` revisions, then pin the independent HEIC container interoperability oracle and external fixture toolchain. -- [ ] Create a provenance map from each WIP codec file to its specification section and exact upstream source. Preserve the current SVT-AV1 origins where applicable and identify which missing paths will use libaom. -- [ ] Convert the completion boundary above into a feature matrix with `unsupported`, `parses`, `decodes`, `encodes`, and `verified independently` states. -- [ ] Audit every source file under `src/ImageSharp/Formats/Heif`. Document every type and shared contract, and add technical comments wherever the code depends on non-obvious specification syntax, fixed-point arithmetic, transform staging, entropy state, buffer layout, or SIMD behavior. Keep public XML documentation limited to observable API behavior. - -Exit gate: - -- [ ] The post-merge branch has a recorded Release baseline, every existing failure is classified, all upstream code origins are known, and the complete HEIF source tree passes the documentation audit before additional porting begins. - -### Phase 1: correct the format contract - -Tasks: - -- [ ] Define distinct HEIC and AVIF public format types over the shared internal HEIF container and register the correct brands, MIME types, and extensions. -- [ ] Add generated `SaveAsHeic` and `SaveAsAvif` APIs and format metadata integration through the same mechanisms as established codecs. Define generic `SaveAsHeif` only if its options require an explicit supported payload codec. -- [ ] Define decoder options using existing `DecoderOptions` behavior, including target pixel type, metadata handling, cancellation, and image-size limits. - - [x] Generic decode selects the caller's pixel type and the non-generic entry point defaults to `Rgba32`. `TargetSize` and `Sampler` are applied once after HEIF presentation composition. `MaxFrames` bounds retained sequence samples. `SkipMetadata`, `SegmentIntegrityHandling`, `ColorProfileHandling`, `Configuration`, and cancellation flow through the container, item, sequence, and nested-codec boundaries. - - [ ] Complete adversarial dimension and allocation-limit coverage for still items, grids, auxiliary images, and sequence tracks before closing this contract item. Large payload and image buffers already use the configured allocator, but the complete cross-product has not been verified. -- [ ] Define codec-specific encoder options with observable semantics for quality, speed/effort, lossless mode, chroma subsampling, bit depth, alpha quality, and metadata handling. Avoid exposing internal HEVC or AV1 tuning knobs without a stable user-facing meaning. -- [x] Make `Rgba32` the default 8-bit decode output so alpha is not silently lost. -- [x] Remove unsupported JPEG 2000, JPEG-XR, JPEG-XS, and AVC capability claims unless those payload codecs are added to the completion matrix. Retain legacy JPEG as an explicit supported HEIF image-item codec. - -Exit gate: - -- [ ] API review confirms that names and documented behavior match existing ImageSharp patterns and promise only the completed HEVC, AV1, and legacy JPEG HEIF payload paths. Capabilities must not be registered before their implementation reaches the final PR gate. - -### Phase 2: rebuild the bounded HEIF still-image reader around validated items - -Tasks: - -- [ ] Implement a bounded box reader supporting 32-bit, 64-bit, and to-end box sizes where allowed, with overflow-safe arithmetic and correct parent bounds. -- [ ] Accept the applicable HEIF, HEIC, and AVIF still-image brands through the major or compatible brand rules. Recognize and reject sequence/movie brands without implementing their track surface. -- [ ] Skip unknown non-essential boxes and properties. Reject unknown essential properties attached to a decoded item. -- [ ] Parse meta children independently of physical order and build the item/property/reference model described above. -- [ ] Correct item ID lookup, one-based property indices, association flag masks, full-box versions, and large IDs/offsets. -- [ ] Resolve `idat` and `mdat` item data, multiple extents, construction methods, and 64-bit offsets through bounded item streams. -- [ ] Parse and associate HEVC and AV1 configuration, dimensions, plane information, ICC/CICP color, alpha auxiliary type, grids, transforms, Exif, XMP, and thumbnails. -- [ ] Make Identify return dimensions, bit depth, color type, alpha presence, profiles, and metadata from the parsed model without decoding AV1 tiles. -- [ ] Add malformed-container tests for every external length, offset, count, ID, association, extent, and relationship boundary. - -Exit gate: - -- [ ] The parser resolves each current HEIC, HIF, and AVIF asset into a stable logical model, malformed inputs fail without escaping bounds or allocating attacker-controlled sizes, and Identify has reference-verified metadata and payload classification. - -### Phase 3: complete SIMD-first AV1 still-image reconstruction - -Implement and verify in dependency order: - -- [x] OBU framing, sequence headers, frame headers, tile groups, byte alignment, and trailing bits. - - [x] Isolate every declared OBU payload with a zero-copy bounded span reader, advance ignored metadata and reserved units without parsing their bytes as headers, validate padding and trailing bytes, enforce primary/redundant/combined frame-header order, accept the final unsized low-overhead OBU by consuming the bounded image-item remainder, and reject lengths that cross the containing boundary. Focused malformed-input tests and independent 8/10/12-bit AVIF reconstruction pass in Release. - - [x] Re-audit the complete bounded parser against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`, including ULEB128 and Annex B lengths, operating-point filtering, sequence/frame ordering, redundant-header identity, temporal delimiters, padding, byte alignment, tile-group bounds, tile sizes, and complete ordered tile coverage. AV1-ISOBMFF's final unsized low-overhead OBU remains the deliberate item-specific extension and is covered by a real fixture. Correct the stale direct tile tests to address item-relative tile bytes and make header round-trip diagnostics skip span-valued properties that reflection cannot box. Both Release source builds remain zero-warning and zero-error, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and all 119 focused framing, lifecycle, bit-reader, tiling, and official-sequence cases pass without failures or skips. Commit `e6f0ef84fc8273a7d50fdfc1489f54f058a2a424` records the checkpoint. -- [x] One coherent bounded decoder lifecycle retains parsed tile state until successful frame completion, commits frame-owned reference samples and metadata only after reconstruction, resets retained state at a new sequence boundary, and disposes tile, reference, entropy, and presentation ownership deterministically. The real two-layer lifecycle fixture proves the header/reference ownership boundary through a fake tile reader; dependent inter reconstruction remains covered by the unchecked items below. -- [x] Tile partitioning, mode information, segmentation, delta quantization, transform-size selection, coefficient token decode, inverse quantization, and inverse transforms. - - [x] Match libaom's depth-first traversal and frame-edge behavior for all ten AV1 partition types. Independent 8/10/12-bit streams collectively select every terminal partition shape and contain nested block geometry that requires recursive `Split` traversal; their complete native planes remain byte-exact under normal hardware dispatch and the scalar fallback. - - [x] Verify the complete 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 base profile matrix against pinned libaom native planes and pinned libavif presentation output. All 12 fixtures match exactly with normal dispatch, AVX-512 disabled, AVX disabled, and all hardware intrinsics disabled through `FeatureTestRunner`. - - [x] Correct monochrome plane classification, maximum-superblock-row loop-filter traversal, and identity/one-dimensional inverse-quantization-matrix selection exposed by the profile matrix. The quantization rule matches libaom's `tx_type < IDTX` boundary and is verified by the exact native-plane oracle rather than a tolerance. - - [x] Re-audit partition and transform traversal, mode and segment ordering, superblock delta-Q and delta-LF updates, coefficient EOB, reverse magnitude, sign, and Golomb parsing, inverse quantization, and inverse-transform dispatch against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. Correct the stale coefficient round-trip oracle to compare retained entropy-scan order and dispose its encoded owner. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and all 947 focused cases pass without failures or skips. Commit `fc9320262cc08dd5daf41f231998403b804b98da` records the checkpoint. -- [x] Intra prediction, including every directional, smooth, Paeth, CFL, filter-intra, and palette case permitted by AV1. - - [x] Implement SIMD-first chroma-from-luma storage, 4:4:4/4:2:2/4:2:0 subsampling, rounded mean subtraction, and 8/10/12-bit prediction with exact scalar fallback and `FeatureTestRunner` parity. - - [x] Implement allocation-free SIMD-first palette reconstruction for palette sizes 2-8, transform widths 4-64, and 8/10/12-bit samples with exact scalar fallback and `FeatureTestRunner` parity. - - [x] Verify palette mode syntax and presented reconstruction with an independently encoded palette AVIF fixture. - - [x] Re-audit conventional intra syntax, palette and filter-intra eligibility and ordering, chroma-from-luma eligibility and alpha mapping, top-right and bottom-left availability, edge extension, filtering, upsampling, and predictor dispatch against pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. Every directional angle, DC, Paeth, smooth, filter-intra, chroma-from-luma, and palette operator remains family-owned under the JPEG-style static operator contract. All 93 focused cases pass without failures or skips, including the exact 39-frame official all-intra native sequence, exact palette native and presentation fixtures, and the 12-profile exact matrix. The existing production implementation required no correction. Commit `42384454eb45b6107608ed33379c7a69b101d533` records the checkpoint. -- [x] Intra-block copy for still-image intra frames. - - [x] Decode tile-adaptive integer displacement vectors, derive and validate spatial references, apply the inter transform sets, and reconstruct luma and subsampled chroma through allocation-free `Vector512`/`Vector256`/`Vector128` operators with exact-width stores and scalar fallback. `FeatureTestRunner` verifies every transform width, bit-depth storage path, chroma phase, intrinsic tier, scalar continuation, and destination-padding boundary. - - [x] Verify displacement-vector entropy, spatial candidate ordering, wavefront legality, native 8/10/12-bit reconstruction, and presented output against independently encoded opaque intra-block-copy AVIF fixtures from the pinned generic libaom/libavif reference. The fixtures require actual intra-block-copy block selection, compare every native plane sample from retained scalar-decoder Y4M output, and compare every presented RGBA byte exactly under normal hardware dispatch and each narrower fallback configuration without a tolerance. - - [x] Correct libaom's 64-sample spatial-scan step threshold and ranked-reference clamping, consume intra-block-copy transform-size contexts, and compare both complete official extreme-displacement frames exactly with the pinned-libaom native YUV420 reference under normal and scalar dispatch. -- [x] Inter-frame prediction for layered still-image items. - - [x] Retain completed ungrained reference planes, frame headers, frame information, and published CDF snapshots in one eight-slot owner; apply refresh flags only after successful completion; resolve full and short reference signaling against occupancy and frame-ID validity; and implement primary-reference selection and `frame_size_with_refs`. - - [x] Inherit primary-reference CDFs, segmentation features and unchanged maps, loop-filter deltas, and same-role global-motion parameters. Initialize the frame-owned per-8x8 temporal motion field and project eligible retained motion vectors in normative reference order. - - [x] Implement allocation-free SIMD-first translational single-reference interpolation for regular, smooth, sharp, and bilinear filters; reduced four-sample kernels; horizontal, vertical, and separable two-dimensional convolution; exact AV1 rounding; 8/10/12-bit clipping; padded reference origins; and guarded destination strides. The operator contract and concrete operator files mirror JPEG color conversion, and `FeatureTestRunner` verifies normal, AVX-512-disabled, AVX-disabled, and scalar execution against an independent fixed-point oracle. - - [x] Decode temporal segmentation prediction and consume the retained segmentation map when `segmentation_update_map == 1` and temporal update is enabled. - - [x] Finish the simple single-reference path. - - [x] Own and decode the adaptive single-reference, inter-mode, DRL, normal-motion-vector, interpolation-filter, inter-intra, OBMC, and motion-mode distributions through the frame CDF lifecycle. - - [x] Derive the spatial and temporal reference-MV stack, select NEAREST/NEAR/NEW/GLOBAL and DRL candidates, decode NEW motion-vector differences, validate the final vector, and preserve libaom's exact duplicate and weighting behavior. - - [x] Verify the disputed source predicates against pinned libaom: identity `GLOBALMV` blocks of sufficient size omit interpolation symbols through `is_nontrans_global_motion()`, and the single-reference spatial extension loops stop at `MAX_MV_REF_CANDIDATES` (two), not the full eight-entry stack capacity. - - [x] Consume the exact inter-intra and binary or ternary motion-mode syntax that precedes interpolation filters, then reconstruct simple translational blocks through the retained padded reference and existing SIMD-first predictor before residual traversal. - - [x] Store frame-sized retained and temporal motion fields in ImageSharp allocator-owned memory with deterministic counted disposal. Allocation tracking verifies aliases, success ownership, failure unwinding, presentation and decoder-result ownership, and exactly-once final disposal. - - [x] Request the established contiguous allocation contract for every padded frame plane and verify complete-plane reconstruction with a constrained ImageSharp allocator without copying. - - [x] Complete source review of the new motion-mode and single-reference tests. - - [x] Pass the exact single-reference checkpoint's Release verification. The `net10.0` and `net11.0` source builds complete with zero warnings and errors, while the `net10.0` test-project analyzer build completes with zero errors and pre-existing repository warnings. All 293 focused `net10.0` syntax, CDF lifecycle, candidate, vector, interpolation, lifecycle, reconstruction, ownership, and `FeatureTestRunner` cases pass with zero failures or skips, including exact dependent-frame native-plane and presentation comparisons, constrained allocation, and motion-field lifetime coverage. `git diff --check` is clean. - - [x] Correct the logical progressive color payload and compare the final dependent frame's native planes and libavif presentation exactly. Pinned libaom decodes both layers from the primary item's two `iloc` extents, and pinned libavif produces the retained two-frame YUV444-alpha reference and final PNG. The exact production-path comparisons pass through `FeatureTestRunner`. - - [x] Decode all compound reference trees and inter modes, derive and retain paired reference-MV candidates, reconstruct both unscaled translational references, and combine them with equal-weight averaging through `Vector512`, `Vector256`, `Vector128`, and scalar paths without per-block allocation. - - [x] Verify equal-average compound reconstruction with the pinned 19-frame libavif sequence, exact pinned-libaom final native planes, exact pinned-libavif final presentation, direct 8/10/12-bit production-block tests, constrained allocation, and exactly-once motion-field disposal. Both Release source targets pass with zero warnings and errors; the test-project analyzer build passes with zero errors and pre-existing repository warnings; 119 direct focused cases and three `FeatureTestRunner` cases pass without failures or skips; and `git diff --check` is clean. - - [x] Decode and reconstruct selected inter-intra prediction and selectable distance-weighted, wedge, and difference-weighted compound blending through reconstructed reference planes. Direct syntax and 8/10/12-bit production tests, SIMD/scalar mask and blend tests, exact four-sequence native/presentation comparisons, constrained contiguous allocation, and exactly-once tracked returns pass. Both source targets build with zero warnings and errors; the test project builds with zero errors and pre-existing repository warnings; 25 direct focused cases and six `FeatureTestRunner` cases pass without failures or skips; and `git diff --check` is clean. Commit `864b11ad9791d3254a38691246270c569001b5a1` records the checkpoint. - - [x] Decode and reconstruct OBMC through above and left neighboring prediction, exact libaom masks, allocator-owned workspaces, and the existing SIMD-first masked blender. Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` records exact pinned native/presentation evidence, direct 8/10/12-bit and subsampled reconstruction, constrained allocation, both Release source builds, and normal/scalar dispatch. - - [x] Decode scaled references through exact Q14 scale factors, signed Q4-to-Q10 coordinate rounding, variable per-output phases and steps, regular/smooth/sharp/bilinear reduced and full filters, bit-depth-dependent two-pass rounding, scaled OBMC neighbors, and the normative 288-sample retained border without per-block allocation. The independently generated 40x40-to-80x80 layered fixture proves the base reference before retention and in the production retained session, then matches the final pinned-libaom Y/U/V planes and pinned-libavif RGBA presentation exactly. Active inter deblocking additionally verifies per-block reference/mode deltas and skipped inter/intra boundary decisions. Normal and scalar `FeatureTestRunner` runs, constrained contiguous planes, unique allocation identities, 8/10/12-bit production-block coverage, zero-warning `net10.0` and `net11.0` source builds, the zero-error test-project build with 1,013 pre-existing warnings, 87 focused cases, documentation review, and `git diff --check` pass. - - [x] Decode local warped motion through pinned integer projection and allocation-free 8x8-tiled affine filtering across byte and high-bit-depth planes. Commit `0b2db86809e26757460e719a6b59c0fe58bb0586` records exact traced native/presentation evidence, constrained allocation, and normal/scalar dispatch. - - [x] Decode non-translational global motion through reconstructed reference planes. Commit `c5637ea0187df35b385bf43e2fe85cd955f01099` records pinned single-reference rotation/zoom syntax and reconstruction, both compound reference workspaces, 8/10/12-bit storage, exact native and presentation output, constrained allocation, balanced ownership, and normal/scalar dispatch. - - [x] Decode all ordinary inter modes, motion modes, and regular/smooth/sharp dual-filter pairs from the official four-frame 352x288 YUV420 motion-vector sequence. The production path includes sub-8x8 chroma assembly and allocation-free no-round compound intermediates. Exact pinned-libaom native planes, normal/scalar dispatch, constrained tracked allocation, direct compound predictor parity, zero-warning source builds, the zero-error analyzer build, Roslyn compiler diagnostics, 3,985 focused cases, documentation review, and `git diff --check` pass. Commit `1c58d855f70b024170ced9eb0a7005f0f9c955ad` records the checkpoint. - - [x] Verify every connected inter mode and filter with exact independent content. Complete official coverage masks prove all ordinary inter modes, all motion modes, and every switchable regular/smooth/sharp filter pair, while the committed bounded AVIF fixtures independently prove every compound, inter-intra, OBMC, scaled, local-warp, and global-warp reconstruction branch. - - [x] Return an explicitly selected lower spatial layer at the item's presentation extent without rescaling retained references. The essential-`lsel` derivative selects the exact pinned-libaom 40x40 YUV444 base, scales native planes to 80x80 before color conversion, and matches pinned-libavif output. The allocator-owned presentation buffer uses group-safe rows, portable `Vector128` two-times filtering with scalar fallback, exact 8/12-bit libyuv-shaped vectors, constrained-allocation lifetime tracking, and leaves committed final-layer and display-only film-grain results unchanged. - - [x] Decode the official two-spatial-layer L2T1 sequence at its default operating point. The 159,037-byte IVF has manifest-matching SHA-1 `E14825F50FF845B8A6932C64CB254007A0B5E3A1` and SHA-256 `D65824E8E7C19BC958DB810D728B2132B513B5A93CB5F99CA3D7995E19A226E1`; its pinned-libaom Y4M has SHA-256 `7A427631ECBF144F435AA4612F1201415FB1A9BCF9A67BA010AEF830B0C3AB81`. All eight 1280x720 YUV420 frames match exactly under normal and scalar dispatch, and an 8 KiB constrained tracked allocator verifies balanced exactly-once returns. The existing decoder required no production correction. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and all five focused compact-sequence cases pass without failures or skips. - - [x] Decode the official one-spatial-layer, two-temporal-layer L1T2 sequence at its default operating point. The 48,883-byte IVF has manifest-matching SHA-1 `E94687EB0E90179B3800B6D5E11EB7E9BFB34EEC` and SHA-256 `EADE696AB60415D476E1D2A489EC2CCCD24D5EAAADFA4559C6488B7932274C5E`; its pinned-libaom Y4M has SHA-256 `4012DE2D4AFD095E7BB68EAE18B50B0674781BB4971CECABC0E5471E63373ED3`. All eight 640x360 YUV420 frames match exactly under normal/scalar dispatch, and an 8 KiB constrained tracked allocator verifies balanced exactly-once returns. The existing decoder required no production correction. The scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and both focused cases pass without failures or skips. - - [x] Decode the official two-spatial-layer, two-temporal-layer L2T2 sequence at its default operating point. The 119,907-byte IVF has manifest-matching SHA-1 `32EF2F14EE9CB11A24A22934F4C065E926E5D236` and SHA-256 `14257D5FD3901581DBEB88133378CD79D50EA32506ADB729909AB56B4A52AF3C`; its pinned-libaom Y4M has SHA-256 `1ABB981CFF76BA9557DA437B258D8A95FCA755DED8E3949D857E8388AB1D6AE3`. All eight 1280x720 YUV420 frames match exactly under normal/scalar dispatch, and an 8 KiB constrained tracked allocator verifies balanced exactly-once returns. The existing decoder required no production correction. The scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and both focused cases pass without failures or skips. - - [x] Decode the official ten-frame active-film-grain sequence. The 28,634-byte IVF has manifest-matching SHA-1 `11BB40026103182C23A88133EDAFCA369E5575E2` and SHA-256 `BA3EDD82A58414F009E1C821B947EC8DE4E0420F33803CA7BFEA39AF6AEEA155`; its pinned-libaom grained Y4M has SHA-256 `C71958DE496A949D12E819190F240BD56010E5AD57D8A0D55903BF380A9E0C26`, while pinned libaom's skip-grain output differs with SHA-256 `9E0762A82E582E854AFF2DC61EBA1E48C0F1D41BBC21E5C896573DA3BB2F1200`. All ten displayed 352x288 YUV420 frames match exactly under normal/scalar dispatch; active synthesis, dependent retained-reference reconstruction, and 2 KiB constrained tracked allocation with exactly-once returns pass. The fixture corrected the AV1 default loop-filter reference deltas from a swapped Golden/Backward pair to the normative `[1, 0, 0, 0, -1, 0, -1, -1]` order. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, 28 focused cases pass without failures or skips, documentation review is complete, and `git diff --check` is clean. Commit `15613b01c0bc098fd9926b886216558e0d26eff9` records the checkpoint. - - [x] Decode the official eight-bit monochrome sequence. The 10,000-byte IVF has manifest-matching SHA-1 `A17584012187CD886B64F8CB0F35BFD8D762F9DC` and SHA-256 `3DCC2A3C181F74788BD9575D94F0862D9E914415AC358A4DA4ED7D904F0535B7`; its ten-frame 576,000-byte pinned-libaom luma reference has SHA-256 `6A4F8A68366E801D60A14C00BE6EA88A861B1B631D86DEF8A8DB65EFB6B06FA8`. All ten displayed 320x180 native YUV400 frames match exactly under normal/scalar dispatch, the shared compact-sequence oracle keeps every prior YUV420 fixture exact, and a 2 KiB constrained tracked allocator verifies balanced exactly-once returns. The existing decoder required no production correction. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, all 17 official-sequence cases pass without failures or skips, documentation review is complete, and `git diff --check` is clean. Commit `da9a46b3d72fd682ea9293af3f88fde26defb222` records the checkpoint. - - [x] Reconcile every completed inter-frame child with the current production path and pinned libaom `03087864cf4bea6abb0d28f95cf7843511413d8f`. The connected syntax, reference-state, motion-derivation, translational, compound, inter-intra, OBMC, scaled, local-warp, global-warp, layered-presentation, film-grain, and monochrome paths contain no remaining explicit unsupported branch. Both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and all 342 focused inter-frame cases pass without failures or skips. The existing production implementation required no correction. Commit `6b3a5a5e1849de7881b929f46a5744fa3efe3fd6` records the checkpoint. -- [x] Lossless and high-bit-depth reconstruction with correct clipping and intermediate precision. - - [x] Route lossless 4x4 blocks through allocation-free reversible inverse Walsh-Hadamard reconstruction for 8/10/12-bit samples, including the DC-only specialization, `Vector128` production traversal, scalar fallback, exact clipping, and `FeatureTestRunner` parity. - - [x] Verify lossless syntax, inverse quantization, prediction, and presented reconstruction with independently encoded 8/10/12-bit AVIF fixtures. The tests require coded residuals with palette and intra-block copy disabled, compare every native YUV sample with the pinned generic libaom-backed decoder, and compare every presented RGBA byte with pinned generic libavif exactly under normal hardware dispatch and the scalar fallback. -- [x] Deblocking loop filter. - - [x] Implement allocation-free SIMD-first 4-, 6-, 8-, and 14-tap filtering for vertical and horizontal edges in 8/10/12-bit storage through closed edge operators, with exact scalar fallback and `FeatureTestRunner` parity against an independent definition. - - [x] Verify deblocking syntax, per-block intra/inter reference and mode delta derivation, skipped-transform boundary decisions on both sides of each edge, and boundary traversal with independently encoded 8/10/12-bit AV1 samples and exact scalar-libaom planes; verify presented reconstruction and public precision with genuine AVIF containers at every supported bit depth. -- [x] CDEF. - - [x] Replace the scalar helper bucket with an allocation-free SIMD-first `Av1CdefFilter`: vector direction analysis, 4x4/4x8/8x4/8x8 constrained filtering, byte/16-bit output operators, and one exact scalar fallback verified through `FeatureTestRunner`. - - [x] Move the immutable deblocked-plane snapshot and frame-wide direction/variance maps to allocator-owned decoder memory, preserving luma-first paired direction derivation, fixed-storage skipped-block selection, asymmetric chroma direction conversion, frame-edge sentinels, SIMD source widening, and direct frame-plane output. - - [x] Verify CDEF syntax, selected nonzero strengths, unit/block traversal, subsampling, frame edges, and exact native reconstruction with independently encoded active-CDEF AV1 streams across 8/10/12-bit samples and pinned scalar-libaom output. - - [x] Verify exact presented reconstruction with independently encoded active-CDEF AVIF containers across 8/10/12-bit samples and pinned scalar-libavif output. `FeatureTestRunner` covers normal dispatch, AVX-512 disabled, AVX disabled, and all hardware intrinsics disabled without a comparer tolerance. -- [x] Super-resolution scaling. - - [x] Replace the per-output dot product with the libaom-shaped four-output SIMD filter, exact scalar remainder, compile-time coefficient span, direct decoder-padding source rows, and one allocator-owned overlap row shared by every plane. - - [x] Verify exact 8/10/12-bit native reconstruction against independently encoded active-super-resolution AV1 streams under normal SIMD dispatch and with hardware intrinsics disabled. - - [x] Verify exact presented reconstruction with independently packaged AVIF containers across 8/10/12-bit samples. -- [x] Loop restoration. - - [x] Replace scalar self-guided restoration with the libaom-shaped summed-area, coefficient, alternating-row radius-two, radius-one, and projection pipeline using AVX2, cross-platform 128-bit vectors, and an exact scalar fallback through one semantic `Av1SelfGuidedFilter` contract. - - [x] Verify active Wiener and self-guided unit selection plus exact 8/10/12-bit native reconstruction against independently encoded pinned-libaom streams under AVX2, 128-bit, and scalar dispatch through `FeatureTestRunner`. - - [x] Verify exact presented reconstruction with independently packaged active-restoration AVIF containers across 8/10/12-bit samples. - - [x] Verify all sixteen self-guided parameter sets at 8, 10, and 12 bits against an independent direct-window definition, including narrow, odd, AVX2-tail, 128-bit-tail, and padded-stride processing units through `FeatureTestRunner`. - - [x] Extend independent integration coverage across restoration-unit boundary shapes, chroma subsampling layouts, and super-resolution-adjusted unit geometry. -- [x] Frame padding and film-grain synthesis when signaled. - - [x] Preserve the normative Gaussian sequence, LFSR, autoregressive templates, scaling interpolation, block selection, overlap, clipping, and odd-dimension extension with allocator-owned scratch. - - [x] Apply noise and horizontal overlap through measured SIMD-first semantic operators, with exact scalar fallbacks and scalar serial dependencies where the algorithm prevents useful vectorization. - - [x] Verify exact independent pinned-libaom fixtures across 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 layouts, full and restricted ranges, identity signaling, overlap, and odd dimensions through `FeatureTestRunner`. - - [x] Benchmark full-HD-equivalent 8/12-bit 4:2:0 application across the supported dispatch tiers and verify zero allocation. - -For each SIMD-suitable item, design the data layout, operator contract, scratch ownership, traversal, and descending-width dispatch around the production SIMD path first. Implement the behaviorally equivalent scalar fallback through the same contract, then verify every supported vector width and the scalar path against normative or independent results before enabling the stage. Serial adaptive work such as entropy decoding remains scalar where data dependencies prevent meaningful vectorization. Remove constant feature-disable flags and unsupported branches only when their replacement is verified. Unsupported syntax must produce a codec-specific invalid-image error; it must never silently skip a normative reconstruction stage. - -Exit gate: - -- [x] Independently encoded opaque AVIF still images, including reference-dependent layered items, reconstruct correctly across all exposed AV1 profiles, bit depths, subsampling modes, and normative image compression tools. Native planes are compared exactly with pinned libaom output, and presented pixels are compared after applying the same signaled color conversion in the reference path. The exact 12-profile native and presentation matrix passes all four normal/fallback cases; five real progressive layered native, presentation, constrained-allocation, and ownership cases pass; and all 25 official-sequence cases pass without failures or skips. Both Release source targets remain zero-warning and zero-error, the scoped Release test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and `git diff --check` is clean. Commit `2219edbc91edb6a2d905f1221a45207eb2719abc` closes Phase 3. - -### Phase 4: complete HEVC still-image reconstruction - -Implement and verify in dependency order: - -- [x] HEIF item-local length-delimited NAL units, `hvcC`, VPS, SPS, PPS, and the parameter-set and slice-header syntax needed to decode the one independently coded picture carried by a supported still-image item. The production path remains item-local and exposes no Annex B byte-stream API or access-unit/timeline abstraction. Ten official Sony GENERAL Range Extensions first pictures, five real HEIC item payloads, and five public HEIC presentation cases pass exactly in Release: 20 cases with no failures or skips. -- [x] One bounded decoder lifecycle owns only the parameter sets, current-picture state, slice/tile entropy state, and reconstructed planes required by that image item. Every allocator-backed child constructor and the owning picture decoder now unwind unpublished rents in reverse order. Focused tests exercise every constructor allocation-failure position and a complete real HEIC reconstruction through 2 KiB allocator groups with balanced exactly-once returns; all 22 official native-plane, real-item, ownership, constrained-allocation, and public presentation cases pass without failures or skips. Both Release source targets build with zero warnings and errors, the scoped Release test-project analyzer build completes with zero errors and 1,011 repository warnings, Roslynk reports zero compiler errors, and `git diff --check` is clean. Commit `b6ba2dbf2f90c7b0a5d9da4e9f45d045bbce1a65` records the syntax reconciliation and lifecycle correction. -- [x] CABAC arithmetic decoding and every required context transition. Commit `35e65e9546dd78847d4de7cae082e26229b3a744` records exact pinned-HM table and algorithm comparison, 9 focused arithmetic/context cases, 17 complete production-slice and allocator-lifecycle cases, zero Roslynk compiler errors, clean scoped StyleCop and whitespace verification, and clean `git diff --check`. -- [x] Coding-tree, coding-unit, prediction-unit, and transform-unit traversal across all permitted sizes and partition modes. Commit `66d1f3c41b0baeaa98726b57d5feaeba50874f16` records exact first-picture native-plane comparison for official ITU RQT_A through RQT_E, STRUCT_A and STRUCT_B, and TUSIZE_A; exact pinned-HM complete-sequence MD5 and RQT decoded-picture-hash verification; analyser-proven partition, coding, prediction, and transform-size coverage; 150 focused Release cases; a zero-warning, zero-error `net11.0` source build; zero Roslynk compiler errors; clean scoped formatting and StyleCop verification; and clean `git diff --check`. -- [x] Intra prediction for every luma and chroma mode, including strong intra smoothing and constrained prediction rules. Commit `9bc5b9842` records exact published native output for the official 245-combination `IPRED_B_Nokia_3` picture, exact pinned-HM plane hashes for the independently coded first `CIP_A_Panasonic_3` picture, all four required `FeatureTestRunner` configurations, 149 focused Release cases, both zero-warning source builds, zero Roslynk compiler errors, clean scoped StyleCop and whitespace verification, and clean `git diff --check`. - - [x] Implement and verify the allocation-free SIMD-first predictor primitive for all 35 modes, 4/8/16/32 blocks, and 8/10/12-bit samples. - - [x] Implement reconstructed-plane reference collection, substitution, asymmetric chroma-unit handling, and reference-filter selection from caller-owned availability and scratch spans. - - [x] Build each availability flag from reconstructed-plane and coding state, and connect the predictor to transform-unit traversal for the independently verified 8-bit 4:2:0 and 4:4:4 item paths. -- [x] Scaling lists, inverse quantization, transform skip, every required inverse transform for independently coded pictures, range-extension precision, and lossless reconstruction. Commit `97538abd2` records exact pinned-HM native-plane verification for the five official fixtures described above. - - [x] Implement allocation-free SIMD-first inverse DCT for every 4/8/16/32 width and height combination, the four-by-four intra-luma inverse DST, normative intermediate and residual clipping, and saturated prediction addition for 8/10/12-bit samples. Verify normal and forced-scalar execution against fixed results and a dense scalar oracle. - - [x] Implement the SIMD-first inverse-quantization primitive for flat and scaling-list paths across every transform size, component matrix, prediction mode, transform-skip scaling rule, signed normalization direction, and range-extension precision path. - - [x] Derive effective luma and chroma quantization parameters, including bit-depth offsets and chroma-format mapping, then connect scaling-list selection and inverse quantization to transform-unit traversal. - - [x] Implement the immutable effective-QP value used by reconstruction, including independent luma/chroma bit-depth offsets, the normative 4:2:0 mapping plateaus, the 4:2:2/4:4:4 saturation rule, and combined picture/slice/coding-unit chroma offsets. - - [x] Select each transform unit's coding-unit luma QP and chroma-adjustment-list entry, then pass the derived component QP into inverse quantization. - - [x] Implement transform skip, coefficient rotation, implicit residual DPCM, transquant bypass, and lossless reconstruction for the independently coded Phase 4 scope. - - [x] Implement allocation-free SIMD-first transform-skip normalization, complete-block coefficient rotation, transquant-bypass copying, implicit intra-direction selection, and horizontal/vertical inverse residual DPCM with a scalar fallback and signed residual clipping. - - [ ] Decode explicit inter residual-DPCM modes when Phase 5 supplies the owning dependent-picture HEVC inter-prediction path; this syntax cannot occur in the independently coded Phase 4 pictures. - - [x] Connect coefficient decoding, inverse quantization, transform selection, reusable scratch, and add/clip to transform-unit traversal. - - [x] Implement grouped diagonal/horizontal/vertical scans, last-significant and significance context derivation, coefficient levels and signs, sign-data hiding, Range Extensions bypass alignment, four persistent Rice states, and one pooled scratch owner with no per-transform allocation or `stackalloc`. - - [x] Invoke coefficient decoding and the existing reconstruction kernels from transform-unit traversal, then verify complete transform blocks from official independently decoded HEVC fixtures. -- [x] Deblocking and sample-adaptive offset for every signaled luma/chroma and bit-depth path. - - [x] Verify the production still-picture path against the official `DBLK_A_SONY_3`, `DBLK_A_MAIN10_VIXS_4`, `SAO_A_MediaTek_4`, and `SAO_A_RExt_MediaTek_1` streams. Pinned HIGH_BITDEPTH HM at commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces every archive's published complete-output MD5, and ImageSharp matches the retained first picture's native planes exactly across 8-bit 4:2:0, 10-bit 4:2:0, and 12-bit 4:4:4. - - [x] Explicitly verify active deblocking and sample-adaptive-offset signaling in the existing ten-picture official GENERAL matrix while retaining its exact published native-plane comparisons across 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4. - - [x] Verify the same four pictures through `FeatureTestRunner` with normal dispatch, AVX-512 disabled, AVX disabled, and all hardware intrinsics disabled. The official SAO picture also matches all three native planes through 2 KiB allocator groups with balanced exactly-once disposal. - - [x] Keep the SAO traversal on the JPEG color-converter architecture: one nested classifier contract in `HevcSampleAdaptiveOffsetFilter.Operator.cs`, separate nested semantic `readonly struct` band and edge operators, descending `Vector512`/`Vector256`/`Vector128` dispatch, and one scalar tail. Commits `702739916b2a440ad2dc9bae8b680ca7fcc636c4` and `1c7292759` record the implementation, official fixtures, exact provenance, dedicated filter tests, and explicit full-profile signaling assertions. Both Release source targets build with zero warnings and errors, Roslynk reports zero compiler errors, all 175 focused HEVC cases pass without failures or skips, documentation review is complete, and `git diff --check` is clean. -- [x] Implement and verify tiles, wavefront entry points, dependent slices, entry-point offset conversion across emulation-prevention bytes, and the other parallelization syntax permitted by the exposed still-image profiles. The retained first pictures from the official `DSLICE_A`, `DSLICE_B`, `DSLICE_C`, `TILES_A`, `TILES_B`, `WPP_A`, `WPP_B`, `WPP_C`, `WPP_D`, `WPP_E`, `WPP_F`, `ENTP_A`, and `ENTP_C` bitstreams match exact native Y/Cb/Cr planes. Pinned HIGH_BITDEPTH HM at commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces every archive's published complete-output MD5. The production correction follows HM's exact tile-origin and wavefront-row dependent-slice context rules and retains wavefront context across dependent segment boundaries. Direct boundary arithmetic verifies entry-point conversion across emulation-prevention bytes, while combined dependent-slice wavefront and tile cases pass through 4 KiB constrained allocator groups with balanced exactly-once disposal. All 191 focused HEVC cases pass without failures or skips, both Release source targets build with zero warnings and errors, the scoped test-project analyzer build completes with zero errors and 1,010 pre-existing repository warnings, Roslynk reports zero compiler errors, the retained fixtures use the existing LFS attributes without changing `.gitattributes`, and `git diff --check` is clean. Commit `b337bce1a6931885d61ef2cda792df0a29869b8b` records the checkpoint. -- [x] Supplemental enhancement information that changes ImageSharp still-image presentation or metadata exposed by ImageSharp. Commit `fcd16c862c437e3249c7e693012c717512985163` records pinned-HM syntax for display orientation, no-display, mastering-display color volume, content light level, alternative transfer characteristics, ambient viewing environment, and content color volume; complete production ordering across scaling, auxiliary alpha, codec orientation, and container transforms; exact independent RGBA comparison; malformed and conflicting metadata rejection; constrained exactly-once allocation; normal/scalar `FeatureTestRunner` coverage; all 195 focused HEVC cases; both zero-warning Release source builds; zero Roslynk compiler errors; clean scoped StyleCop and whitespace verification; an unchanged `.gitattributes`; and clean `git diff --check`. - - [x] Establish exact HM 18 native-plane conformance for four real 8-bit item payloads covering 4:2:0, 4:4:4, cropping, multiple grid positions, and wavefront entry points. - - [x] Establish exact independent presentation verification for single images, grids, auxiliary alpha, ICC, 4:2:0, and 4:4:4 output without weakening the exact native-plane codec oracle. - - [x] Establish exact native-plane conformance for 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 across every exposed HEVC profile and required Range Extensions tool. Commit `9d7348efd6496b54cef7ec27e42352641439c400` records 25 additional official H.265.1 independently coded pictures, exact profile/tool assertions and native-plane hashes, the PCM precision and chroma-deblocking corrections, all 220 focused HEVC cases, both zero-warning Release source builds, zero Roslynk compiler errors, verified fixture hashes and existing LFS pointers without an attributes change, and clean `git diff --check`. - -Each subsystem keeps a specification-shaped scalar oracle derived from the HEVC specification and the pinned implementation reference, while production hot paths are designed SIMD-first with scalar fallback. No valid syntax in the exposed HEIC profiles may terminate in an unsupported branch or silently omit a normative reconstruction stage. - -Exit gate: - -- [ ] Independently encoded HEIC files reconstruct correctly across every exposed HEVC profile, chroma format, bit depth, range-extension tool, and normative still-image compression path. - -### Phase 5: shared color conversion, alpha, grids, and presentation transforms - -Tasks: - -- [ ] Implement monochrome, 4:2:0, 4:2:2, and 4:4:4 plane access with every valid signaled chroma sample position. - - [x] Implement SIMD AV1 4:2:0 and 4:2:2 chroma reconstruction and downsampling, including centered/colocated reconstruction and odd image edges. - - [x] Implement pooled HEVC plane conversion for monochrome, 4:2:0, 4:2:2, and 4:4:4, including exact encode-coordinate and decode-interpolation coverage for all six progressive 4:2:0 sample locations. - - [ ] Connect reconstructed HEVC pictures and complete the independently verified AV1/HEVC chroma-position matrix before closing this item. -- [ ] Implement full- and limited-range expansion for 8, 10, and 12-bit samples across every supported plane layout. - - [x] Implement AV1 range expansion and quantization with SIMD-first byte/ushort loaders and storers and pooled planar rows. - - [x] Implement the equivalent HEVC plane paths with independent luma/chroma precision, native 16-bit high-depth staging, and exact limited-range endpoint coverage. - - [ ] Independently verify the complete HEVC range/precision/plane-layout cross-product after reconstruction is connected. -- [ ] Implement every non-reserved HEVC/AV1 color-primary, transfer-characteristic, and matrix-coefficient signaling path, including identity conversion, with correct fixed-point rounding and clipping. - - [x] Implement one shared H.273 matrix and transfer-function operator set in both directions for AV1 and HEVC, including identity, derived coefficients, constant luminance, SMPTE ST 2085, PQ/HLG ICtCp, IPT-C2, YCgCo-Re, and YCgCo-Ro. - - [ ] Add independent conformance vectors for signaling combinations that libavif deliberately rejects, then verify the operator set through completed HEVC reconstruction. -- [ ] Make SIMD the default decode path for YUV range expansion, chroma reconstruction, H.273 matrix and transfer conversion, clamping, and 8/10/12-bit RGB packing. Use the established static operator pattern with `Vector512`, `Vector256`, and `Vector128` dispatch followed by one scalar tail that remains the behavioral oracle. - - [x] AV1 uses the JPEG-style static operator contract and descending SIMD-width traversal for decode and encode, with scalar execution limited to the remaining lanes or hardware without vector support. - - [x] HEVC plane conversion uses the same shared static operators, descending SIMD-width traversal, JPEG byte-plane pack/unpack, pooled high-depth staging, and scalar fallback; full-HD SIMD/scalar timings are recorded above. - - [ ] Connect the HEVC reconstruction path and record complete HEIC item and sequence measurements before closing this item. -- [ ] Apply embedded ICC profiles after codec YUV/CICP conversion and image composition through ImageSharp's existing color-profile converter. Cover preserve, convert, compact, and ignore behavior for primary items, grids, alpha-composited images, and every presented sequence frame; never substitute CICP or an assumed sRGB profile for an embedded ICC profile. - - Primary AVIF verification passes four independently scoped cases: exact profile preservation, non-sRGB conversion with changed pixels and paired-source comparison, canonical-sRGB compaction without pixel changes, and metadata skipping. Grid, auxiliary-alpha, sequence, and HEVC cases remain open. -- [ ] Decode alpha auxiliary items as monochrome planes, validate dimensions and bit depth, and compose them without losing precision. Define premultiplication behavior from AVIF signaling and ImageSharp's pixel contract. -- [ ] Validate grid tile type, dimensions, properties, order, and canvas coverage. Compose directly into the destination frame, cropping only the permitted right and bottom tile overlap. -- [ ] Apply clean aperture, rotation, mirroring, and pixel aspect ratio according to the container property order and ImageSharp metadata/processing conventions. -- [ ] Preserve ICC, CICP, Exif, and XMP metadata using existing ImageSharp profile types. ICC preservation and ICC color conversion are separate contracts and must both be independently verified. - -Exit gate: - -- [ ] The complete cross-product of valid HEVC/AV1 bit depths, subsampling modes, ranges, color signaling, alpha, grids, and transforms is covered by focused vectors and representative HEIC/AVIF integration files and matches independently decoded references within a documented conversion tolerance. Tests must prove ICC conversion changes pixel values according to an independent profile transform instead of checking profile retention alone. Integer identity/lossless cases must match exactly. - -### Phase 6: implement a real AV1 still-image encoder - -Delete the JPEG payload path from the production encoder. Keep the synchronous ImageSharp encoder contract synchronous unless the established base contract provides an async implementation; never use `async void`. - -Implement in vertical slices that always produce a decodable AV1 bitstream: - -- [ ] Convert RGB/RGBA to AV1 planes for every 8, 10, and 12-bit output, range, matrix, and monochrome/4:2:0/4:2:2/4:4:4 combination permitted by the selected AV1 profile. - - [x] Implement the pooled sequential RGB-to-planar pipeline, 8/10/12-bit quantization, every implemented H.273 operator, and SIMD 4:2:0/4:2:2 downsampling with odd-edge handling. - - [ ] Complete the exhaustive format/range/matrix/transfer/primary cross-product and validate encoded planes against an independent implementation before closing this item. -- [ ] Write sequence, frame, tile-group, and metadata OBUs for a reduced still picture. -- [ ] Implement a temporary smallest-valid intra-only vertical slice using existing partition, prediction, transform, quantization, coefficient, and entropy structures. -- [ ] Complete block geometry, neighbor/context updates, transform selection and forward transforms, quantization, coefficient tokenization, and range coding. -- [ ] Add mode decision and rate/distortion selection in increasing effort levels. Reuse computed prediction and transform results instead of duplicating analysis across stages. -- [ ] Add lossless mode and validate the exact lossless constraints rather than treating quality 100 as lossless. -- [ ] Add in-loop filter decisions and signaling. A legal choice to disable an encoder feature is distinct from a decoder skipping a signaled feature. -- [ ] Add alpha as an auxiliary AV1 item with independently controllable quality where the public option warrants it. -- [ ] Write the AVIF item graph, extents, `av1C`, pixel information, color properties, metadata, and alpha relationships with correct brands. -- [ ] Write embedded ICC and CICP color properties independently, preserve the source profile selected by the encoder options, and verify both metadata round-trip and independently converted output pixels. -- [ ] Make output deterministic for identical pixels, metadata, options, and configuration. -- [ ] Complete the partition, prediction, transform, quantization, entropy, filter, and rate/distortion choices needed for quality and effort settings to provide a genuine compression tradeoff rather than selecting from a fixed coding subset. -- [ ] Exercise every encoder bit-depth, plane-layout, range, color, alpha, lossless/lossy, quality, and effort combination through independent decode. - -Every vertical slice must be decoded by libavif before additional compression features are added. Self-round-trip tests are supplementary because matching encoder and decoder bugs can otherwise hide invalid bitstreams. - -Exit gate: - -- [ ] libavif and another independent AV1 decoder accept ImageSharp output across the complete encoder matrix, and ImageSharp reconstructs the same files. Lossless output is pixel-exact; lossy output demonstrates effective quality/effort tradeoffs and meets recorded quality and size expectations without malformed or non-AV1 payloads. - -### Phase 7: implement a real HEVC still-image encoder - -Implement in vertical slices that always produce a HEVC bitstream accepted by an independent HEIC decoder: - -- [ ] Convert ImageSharp pixels into every exposed HEVC bit depth, range, and monochrome/4:2:0/4:2:2/4:4:4 combination. -- [ ] Write VPS, SPS, PPS, slice headers, parameter arrays in `hvcC`, and a smallest-valid intra picture as a temporary vertical slice. -- [ ] Complete coding-tree partitioning, intra prediction selection, forward transforms, scaling/quantization, coefficient scanning, and CABAC encoding. -- [ ] Add real rate/distortion mode decision, quality and effort controls, lossless mode, deblocking decisions, and sample-adaptive-offset decisions. -- [ ] Add alpha auxiliary images, grids, metadata, color properties, image relationships, and correct HEIC brands. -- [ ] Write embedded ICC and CICP color properties independently, preserve the source profile selected by the encoder options, and verify both metadata round-trip and independently converted output pixels. -- [ ] Exercise every encoder profile, bit depth, chroma format, range, color, alpha, lossless/lossy, quality, and effort combination through independent decode. - -Exit gate: - -- [ ] Independent HEIC decoders accept ImageSharp output across the complete encoder matrix. Lossless output is pixel-exact; lossy output demonstrates effective quality/effort tradeoffs and meets recorded quality and size expectations. - -### Phase 8: SIMD and allocation optimization - -Production hot paths are designed and implemented SIMD-first. The same architecture must provide a behaviorally equivalent scalar fallback for unsupported hardware, remainder work, and parity verification; scalar code is not a temporary production architecture that SIMD is added to later. Every supported hardware path and the scalar fallback are verified against independent specification-shaped results before the subsystem is complete. - -Tasks: - -- [ ] Remove known avoidable allocations first: per-transform arrays, the intermediate RGB image, repeated block scratch arrays, and file-sized buffering. - - [x] Complete the active AV1 forward and inverse transform-family checklist above. -- [ ] Benchmark codec-specific costs for CABAC/range decode, inverse transforms, still-image prediction, deblocking, SAO, CDEF, restoration, chroma upsampling, color conversion, alpha packing, and grid copies. - - [x] Add a permanent frame-wide HEVC intra-prediction benchmark. On .NET 10, SIMD planar, vertical-angular, and horizontal-angular prediction measured 155.2, 150.7, and 255.0 microseconds per padded 1920x1088 frame, compared with forced-scalar timings of 1.890, 1.081, and 0.979 milliseconds: 12.2, 7.2, and 3.8 times faster with zero managed allocations. - - [x] Add a permanent frame-wide HEVC reference-preparation benchmark. On .NET 10, complete and partially substituted borders measured 465.3 and 566.7 microseconds per 2,040-block padded 1920x1088 frame, compared with forced-scalar timings of 477.5 and 608.0 microseconds, with zero managed allocations. - - [x] Add a permanent frame-wide HEVC inverse-transform benchmark. On .NET 10, dense-coefficient 32x32 twelve-bit inverse DCT, transposition, and add/clip measured 3.643 milliseconds per padded 1920x1088 frame, compared with 45.23 milliseconds with hardware intrinsics disabled: 12.4 times faster with zero managed allocations. - - [x] Add a permanent frame-wide HEVC inverse-quantization benchmark. On .NET 10, dense 32x32 twelve-bit flat and scaling-list paths measured 69.98 and 238.4 microseconds per padded 1920x1088 frame, compared with forced-scalar timings of 1.469 and 1.609 milliseconds: 21.0 and 6.7 times faster with zero managed allocations. Pre-expanding the scaling matrices once reduced the SIMD scaling-list path from 4.974 milliseconds to 238.4 microseconds. - - [x] Add a permanent frame-wide HEVC residual-reconstruction benchmark. On .NET 10, dense 32x32 twelve-bit transform skip, horizontal RDPCM, and vertical RDPCM measured 79.64, 275.61, and 110.81 microseconds per padded 1920x1088 frame, compared with forced-scalar timings of 571.5 microseconds, 1.512 milliseconds, and 1.257 milliseconds: 7.2, 5.5, and 11.3 times faster with zero managed allocations. - - [x] Add a permanent frame-wide HEVC grouped coefficient-scan benchmark. On .NET 10, generating the complete diagonal 32x32 scan for all 2,040 transform blocks in a padded 1920x1088 frame measured 1.432 milliseconds with zero managed allocations. CABAC decisions remain serial and require an independently encoded complete-slice workload after transform-unit traversal is connected. - - [x] Add a permanent frame-wide AV1 chroma-from-luma prediction benchmark. The lane-selection run rejected the explicit `Vector512` experiment because AVX2 was 2.6% faster at 8-bit and 4.8% faster at 12-bit on the test system. With that path removed, the selected hardware and forced-AVX2 paths are equivalent; the final .NET 10 run measured 88.76 and 74.89 microseconds per padded 1920x1088 frame at 8 and 12 bits, compared with forced-scalar timings of 1.594 and 1.604 milliseconds: 18.0 and 21.4 times faster with zero managed allocations. - - [x] Add a permanent frame-wide AV1 palette-reconstruction benchmark. On .NET 10, runtime-selected hardware measured 20.40 and 62.99 microseconds per padded 1920x1088 frame at 8 and 12 bits, compared with forced-scalar timings of 488.96 and 509.58 microseconds: 24.0 and 8.1 times faster with zero managed allocations. Forced `Vector512` measured 20.81 and 62.70 microseconds, so production continues to honor the runtime's preferred vector width instead of overriding this machine's `Vector256` selection. - - [x] Add a permanent frame-wide AV1 lossless inverse-transform benchmark. Dense four-by-four reconstruction across a padded 1920x1088 frame measured 595.6 microseconds at 8 bits and 512.4 microseconds at 12 bits, compared with forced-scalar timings of 2.074 and 2.270 milliseconds: 3.5 and 4.4 times faster with zero managed allocations. - - [x] Add a permanent frame-wide AV1 deblocking benchmark. Complete vertical and horizontal fourteen-tap passes across a padded 1920x1088 frame measured 4.040 milliseconds at 8 bits and 4.184 milliseconds at 12 bits, compared with forced-scalar timings of 9.416 and 9.410 milliseconds: 2.33 and 2.25 times faster with zero managed allocations. - - [x] Add a permanent frame-wide AV1 super-resolution benchmark. Upscaling a 1280x1080 coded luma plane to 1920x1080 measured 2.052 milliseconds at 8 bits and 2.045 milliseconds at 12 bits, compared with forced-scalar timings of 10.627 and 10.634 milliseconds: 5.18 and 5.20 times faster with zero managed allocations. BenchmarkDotNet could not change the Windows power plan, so retain these figures as an uncontrolled local baseline rather than a release comparison. - - [x] Add a permanent full-HD-equivalent AV1 self-guided restoration benchmark. AVX2 measured 6.674 milliseconds at 8 bits and 7.076 milliseconds at 12 bits, compared with 11.425 and 11.119 milliseconds for the 128-bit path and 40.386 and 40.522 milliseconds for the scalar fallback: AVX2 is 6.05 and 5.73 times faster than scalar with zero managed allocations. BenchmarkDotNet could not change the Windows power plan or query the processor name, so retain these figures as an uncontrolled local baseline rather than a release comparison. -- [ ] Implement vector paths only for confirmed hot loops, using existing `Vector128`, `Vector256`, and `Vector512` helper and dispatch patterns where supported. -- [ ] Prioritize shared color conversion and pixel packing, chroma upsampling, inverse-transform add-and-clip, intra predictors, HEVC deblock/SAO, AV1 loop filter/CDEF/restoration, and contiguous grid copies. -- [ ] Benchmark the complete decode color pipeline on representative 8/10/12-bit AVIF and HEIC images with and without embedded ICC profiles. Report absolute end-to-end timings and allocations in addition to the isolated YUV/CICP and ICC stage costs. -- [ ] Design each hot-path operator and data layout from the applicable upstream SIMD algorithm, mapping lane width, signedness, intermediate precision, rounding, saturation, edge extension, and high-bit-depth behavior before implementation. -- [ ] Implement the scalar fallback through the same operator contract, tables, constants, scratch ownership, and traversal as the SIMD paths so it remains a specification-shaped oracle without becoming a separate architecture. -- [ ] Test scalar and each available hardware path with intrinsics explicitly enabled and disabled, including widths shorter than a vector, exact-vector widths, non-multiples, edges, maximum sample values, and high-bit-depth overflow cases. -- [ ] Remove dead or commented SIMD experiments once a verified production path replaces them. - -Exit gate: - -- [ ] Benchmarks show a material improvement on representative AVIF files, allocation measurements meet an agreed budget, and every vector path is behaviorally identical to the scalar path for integer reconstruction or within the documented color-conversion tolerance. - -### Phase 9: hardening, documentation, and release readiness - -Tasks: - -- [ ] Fuzz the box parser, AV1 OBU parser, HEVC NAL/parser, entropy decoders, and dimension/allocation boundaries using the same safety expectations as established ImageSharp codecs. -- [ ] Test seekable and non-seekable streams, short reads, cancellation, truncated data, unknown optional boxes, unknown essential properties, oversized dimensions, malicious counts, and offset arithmetic overflow. -- [ ] Run the focused HEIF/HEIC/AVIF suite after every final codec edit, then the full ImageSharp suite in Release. -- [ ] Build all supported target frameworks and run packaging/API compatibility checks used by the repository. -- [ ] Update public documentation, format tables, MIME/extension lists, samples, and `THIRD-PARTY-NOTICES.TXT`. -- [ ] Remove placeholder images, legacy JPEG-in-HEIF production paths, stale TODO-only code, disabled tests that now have coverage, and unsupported capability claims. - -Exit gate: - -- [ ] The full Release build and test matrix passes, every valid HEVC/AV1 color/compression/bit-depth entry is implemented, independent HEIC and AVIF interoperability is recorded for the complete feature matrix, the public API has been reviewed, provenance and patent/license obligations are complete, and no advertised feature depends on a placeholder, narrow temporary subset, silent fallback, disabled normative stage, or unsupported valid syntax branch. - -## Verification matrix - -Every valid combination in the HEVC and AV1 profiles exposed by the final public contract needs focused coverage and representative integration coverage. Each axis needs independently produced HEIC/AVIF inputs and ImageSharp-produced outputs; pairwise and targeted cross-product cases must cover interactions where exhaustive media fixtures would be redundant. - -| Status | Area | Required coverage | -| --- | --- | --- | -| - [ ] | Container/payload | HEIC with HEVC, AVIF with AV1, and generic HEIF/HIF with supported HEVC, AV1, or legacy JPEG items; detection by brands/items. | -| - [ ] | Bit depth | Every bit depth permitted by the exposed HEVC and AV1 profiles, including 8, 10, and 12-bit decode and encode. | -| - [ ] | Planes | monochrome, 4:2:0, 4:2:2, and 4:4:4 decode and encode in every valid HEVC/AV1 profile and depth combination. | -| - [ ] | Range | full and limited decode and encode. | -| - [ ] | Color | every valid non-reserved color-primary, transfer-characteristic, matrix-coefficient, and chroma-position signaling path; identity RGB; ICC; CICP defaults and overrides. | -| - [ ] | Alpha | opaque, binary, gradient, different alpha quality, high bit depth, malformed relationship. | -| - [ ] | Structure | single item, multiple extents, `idat`, `mdat`, grids with cropped edge tiles, metadata items. | -| - [ ] | Transform | `pasp`, `clap`, `irot`, `imir`, and valid combinations. | -| - [ ] | AV1 decode tools | every normative transform type/size, still-image predictor, partition, palette, segmentation, quantization, entropy/context, lossless, deblock, CDEF, super-resolution, restoration, and film-grain path valid in independently decodable AVIF still-image items. | -| - [ ] | AV1 encode compression | real mode decision and rate/distortion selection across partitions, predictions, transforms, quantization, entropy coding, filters, lossless/lossy quality, and effort settings; no permanent fixed coding subset. | -| - [ ] | HEVC decode tools | every normative NAL/parameter/slice, CABAC, coding-tree, intra prediction, transform, quantization, range-extension, lossless, tile/wavefront, deblock, and SAO path valid in independently decodable still-image items for the exposed HEIC profiles. | -| - [ ] | HEVC encode compression | real coding-tree, prediction, transform, quantization, CABAC, filter, and rate/distortion decisions across lossless/lossy quality and effort settings; no permanent fixed coding subset. | -| - [ ] | Streams | file, memory, non-seekable, short-read wrapper, cancellation. | -| - [ ] | Failure | truncation at every box/OBU layer, invalid sizes/offsets/counts, unknown essential properties, unsupported profile. | -| - [ ] | Interop | libavif and libaom for AVIF/AV1; a pinned independent HEIC container and HEVC codec implementation for HEIC/HEVC. | -| - [ ] | SIMD | scalar, 128, 256, and 512-bit paths where supported; tails and edge blocks. | - -Reference outputs must be versioned artifacts or generated by a pinned reference command whose exact tool version and arguments are recorded. Do not use ImageSharp's own decoder to establish the expected pixels for its encoder, and do not replace final-image assertions with internal buffer or non-zero checks. - -No valid HEVC or AV1 color, compression, or bit-depth row may remain `unsupported`, partially implemented, disabled, or deferred when the PR is marked complete. - -## Working rules for implementation - -- Keep changes vertical and reviewable. A slice should add one behavior, its focused tests, independent evidence, and any required notice update. -- Keep every AV1 prediction family on the established JPEG color-converter operator architecture. Each distinct traversal contract owns a family-named predictor type; its `.Operator.cs` defines the static interface, and its family-named files own the closed generic widest-to-narrowest SIMD traversal. Semantic `readonly struct` operators implement scalar, `Vector128`, `Vector256`, and `Vector512` arithmetic through that contract. Only modes which share the same traversal and contract may share a predictor family; do not nest a separate predictor beneath a broad intra/inter family or create hardware-width-specific class hierarchies. -- Keep AV1 one-dimensional transforms on the JPEG color-converter owner pattern. `Av1ForwardTransformer.Operator.cs` defines the nested forward contract, and each `Av1ForwardTransformer.Operator.cs` file defines one nested semantic `readonly struct` with concrete scalar and SIMD bodies. `Av1Inverse2dTransformer` owns the inverse contract and semantic operators in the same layout, while `Av1InverseTransformer` owns its byte and high-bit-depth reconstruction-output operators. Shared lane arithmetic belongs in explicitly named `Operations` types, never in forwarding semantic operators. Do not create top-level transform operators, `Forward1d` or `Inverse1d` wrapper types, generic semantic forwarding cores, CRTP contracts, or hardware-width-named operator types. -- Design SIMD-suitable codec work SIMD-first. Establish vector-friendly storage, operator boundaries, scratch ownership, traversal, every applicable lane width, and benchmark-gated dispatch before implementing the equivalent scalar fallback; never build a scalar production architecture and bolt SIMD onto it later. -- Inspect every owning method and upstream invariant before adding guards. Validate external file data at the parser/model boundary and rely on those established invariants internally. -- Do not extract one-use helpers merely to label code. Extract shared primitives only when they have genuine reuse or remove substantial complexity. -- Put comments at the points where HEVC/AV1 rounding, edge extension, context propagation, or SIMD lane behavior is not evident from the code. Comments should explain why the algorithm has that shape and identify the normative rule and pinned implementation reference. -- Use observable behavior only in public API documentation. -- Do not use reflection, built-assembly probing, native runtime fallbacks, fabricated images, or self-round-trip-only evidence. -- Build and test in Release configuration. - -## Recommended implementation order - -The dashboard and immediate execution queue define the remaining critical path. In phase terms, work proceeds as follows: - -- [x] Finish the Phase 8 checkpoint for the implemented AV1 `Vector512` transforms, including Release, feature-isolation, and benchmark evidence. -- [x] Finish the complete focused Release matrix, final source/documentation review, and checkpoint implementation for the simple single-reference inter slice. Commit `8b9aa4b2cfe59ff0f8c348d9bb06678a5ff12f2a` records the corrected real dependent-frame AVIF, exact pinned-libaom native-plane and pinned-libavif presentation comparisons, and 293 focused cases passing with zero failures or skips. -- [x] Implement compound reference selection and equal averaging with paired reference-MV derivation, allocation-free SIMD/scalar parity, and exact independent native/presentation evidence through a reference-dependent bounded AVIF sequence. Commit `e1a764b8f2e8a8db98a003fb3b913213f40084db` records the checkpoint. -- [x] Implement and independently verify selected inter-intra prediction and selectable distance-weighted, wedge, and difference-weighted compound blending through exact pinned native-plane and presentation comparisons, direct 8/10/12-bit production coverage, constrained allocation, and SIMD/scalar parity. -- [x] Implement and independently verify OBMC through exact traced native-plane and presentation evidence, direct 8/10/12-bit and subsampled production coverage, constrained allocation, and normal/scalar dispatch. Commit `59a4fbad5ed3bc7c34331c4bdc65acc7acde3f04` records the checkpoint. -- [x] Implement and independently verify scaled references through pinned fixed-point oracles, direct 8/10/12-bit production reconstruction, a genuine 40x40-to-80x80 bounded AVIF, exact retained-base/final-native/final-presentation comparisons, active inter deblocking, constrained allocation, and normal/scalar dispatch. Commit `8f57c29b250b4291f034b31c1ea3de3fa3345467` records the checkpoint. -- [x] Implement and independently verify local warped motion through pinned projection, direct 8/10/12-bit predictor coverage, a genuine traced two-frame bounded AVIF, exact final native and presentation comparisons, constrained allocation, and normal/scalar dispatch. Commit `0b2db86809e26757460e719a6b59c0fe58bb0586` records the checkpoint. -- [x] Implement and independently verify non-translational global prediction with a genuine traced bounded sequence, exact native and presentation comparisons, constrained allocation, normal/scalar dispatch, and direct 8/10/12-bit compound production coverage. Commit `c5637ea0187df35b385bf43e2fe85cd955f01099` records the checkpoint. -- [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] 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] Close Phase 4 with genuine HEIC presentation evidence across every exposed profile and required Range Extensions tool while retaining the exact native-plane matrix. -- [ ] **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. -- [ ] 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 animated AVIF and HEIC writing only after both still payload encoders pass their independent exit gates. -- [ ] Close Phase 8 end-to-end performance and allocation budgets for every completed subsystem, then close Phase 9 and the full verification matrix. - -The phase numbers describe technical workstreams; they are not permission to work out of order. SIMD, allocation control, documentation, and independent evidence are completion requirements inside each active workstream. The PR remains incomplete until every dashboard stage and verification-matrix row is complete. +Work must proceed in this order. Do not skip to a later item while an earlier checkpoint is open. + +### 1. Finish and verify the AV1-only cleanup + +- [x] Remove production types, registrations, constants, parser branches, properties, tests, benchmarks, fixtures, reference outputs, notices, and documentation for removed codec work. +- [x] Remove downloaded non-libaom reference source, tools, generated outputs, and local installations. +- [x] Retain the pinned libaom source and build artifacts required for AV1 verification. +- [x] Retain user-supplied AV1 fixtures and their recorded expected outputs. +- [x] Audit production source, tests, benchmarks, assets, project files, notices, and documentation for stale removed-code references. +- [x] Build the current source targets in Release with restore disabled, build servers disabled, and one MSBuild node. +- [x] Run the focused AV1/container tests needed to prove the cleanup did not damage AVIF behavior. +- [x] Run scoped semantic and StyleCop inspection, whitespace inspection, and git diff --check. +- [x] Record the exact verified evidence in this plan. + +Verified cleanup evidence on 2026-08-30: + +- Release source builds passed for net10.0 and net11.0 with zero warnings and zero errors. Both builds used `--no-restore`, `--disable-build-servers`, and one MSBuild node. +- The focused net10.0 HEIF decoder, encoder, metadata, sequence-parser, and AV1 reconstruction set passed 221 of 221 tests with zero failures and zero skips. +- The Roslyn compiler and configured StyleCop analyzers accepted the changed production source. Roslynk's `open_solution` entry point was attempted separately but failed before returning a solution handle, so no Roslynk result is claimed. +- The final text and filename audit found no removed-code references outside the unchanged repository and shared-infrastructure `.gitattributes` patterns. +- `git diff --check` passed and neither `.gitattributes` file changed. + +### 2. Correct the single-reference inter-frame checkpoint + +The following findings are confirmed by direct source inspection and keep the checkpoint open. + +- [ ] Correct interpolation-filter syntax in Av1TileReader. + - Current source treats every global-motion type other than Translation as non-translational. + - Pinned libaom omits interpolation-filter syntax only when the selected model type is greater than Translation. + - Identity GLOBALMV blocks of sufficient size must consume switchable-filter symbols. + - Add production-path syntax coverage using the default Identity model. A test that forces Translation does not prove this rule. +- [ ] Correct both spatial reference-MV extension loops in Av1ReferenceMotionVectors. + - Current source stops spatial extension when the stack reaches two entries. + - Pinned libaom extends the stack through MAX_REF_MV_STACK_SIZE, which is eight. + - Preserve DRL candidates and consume every required DRL symbol. + - Describe this as spatial single-reference extension, never temporal extension. +- [ ] Replace the contiguous-span dependency in Av1FrameBuffer and all affected inter reconstruction callers. + - Current GetPaddedPlaneSpan calls DangerousGetSingleSpan. + - Buffer2D may use multiple memory groups under a constrained allocator. + - Implement an efficient group-safe row-oriented contract using established ImageSharp Buffer2D access patterns, or prove and enforce a real contiguous-allocation invariant at the allocator boundary. + - Do not copy planes and do not allocate per block, row, or scanline. + - Audit direct DangerousGetSingleSpan use in reconstruction, reference-border extension, film grain, copying, and encoder work rather than fixing only one wrapper. +- [ ] Prove the real Av1BlockDecoder.DecodeBlock inter-reconstruction branch. + - Decode the progressive dependent-frame fixture through the complete public production path. + - Compare the final frame's native Y, Cb, and Cr planes exactly with pinned libaom output. + - Compare the final presented image through the established ImageSharp reference-image comparison API. + - Do not substitute an internal helper test, fake tile reader, non-zero assertion, custom pixel loop, or tolerant comparison. +- [ ] Prove motion-field ownership and lifetime. + - Track initialization, retained-slot aliases, failure unwinding, presentation ownership, decoder-result ownership, and final disposal. + - Every allocator-owned object must be returned exactly once. +- [ ] Correct stale documentation. + - Av1InterFrameModeInfoTests must describe the behavior it actually proves. + - Do not claim production reconstruction, constrained allocation, ownership, or reference-stack coverage unless the test executes that contract. + +Checkpoint gate: + +- [ ] Default Identity and Translation GLOBALMV syntax cases pass. +- [ ] Eight-entry spatial extension and DRL syntax cases pass. +- [ ] The exact dependent-frame native-plane comparison passes. +- [ ] The established exact presentation comparison passes. +- [ ] Normal, AVX-512-disabled, AVX-disabled, and scalar FeatureTestRunner configurations pass where supported. +- [ ] Constrained multi-group allocation passes without copying or per-block allocation. +- [ ] Motion-field allocation tracking is balanced across success and failure. +- [ ] Release builds for net10.0 and net11.0 pass with zero errors. +- [ ] Focused Release tests pass with zero failures or skips. +- [ ] Scoped semantic, StyleCop, whitespace, and git diff checks pass. +- [ ] Only after all evidence is recorded may this checkpoint be committed. + +### 3. Reverify downstream inter prediction in recorded order + +These implementations exist locally but inherit the open single-reference syntax, buffer, and ownership foundation. + +- [~] Compound reference selection, paired reference-MV derivation, and equal averaging. +- [~] Inter-intra prediction. +- [~] Distance-weighted compound prediction. +- [~] Wedge compound prediction. +- [~] Difference-weighted compound prediction. +- [~] OBMC. +- [~] Scaled-reference prediction. +- [~] Local warped prediction. +- [~] Non-translational global prediction. +- [~] Inter deblocking decisions and reference/mode deltas. + +For every item: + +- [ ] Trace syntax and arithmetic to the pinned libaom commit. +- [ ] Execute the real production decoder path. +- [ ] Compare native planes exactly. +- [ ] Compare presentation through the established reference-image API. +- [ ] Run constrained allocator and exactly-once ownership coverage. +- [ ] Run FeatureTestRunner for SIMD and scalar dispatch when the implementation has SIMD. +- [ ] Record focused Release evidence before marking the item verified. + +### 4. Close AV1 decoder coverage + +Previously verified algorithm checkpoints remain valuable evidence, but the final decoder gate requires a fresh current-tree run after the inter and cleanup corrections. + +- [x] Bounded OBU framing, sequence headers, frame headers, tile groups, alignment, and trailing-bit parsing have pinned-reference checkpoint evidence. +- [x] Partition traversal, mode information, segmentation, delta quantization, transform-size selection, coefficient decoding, inverse quantization, and inverse transforms have pinned-reference checkpoint evidence. +- [x] Intra prediction covers directional, DC, smooth, Paeth, chroma-from-luma, filter-intra, and palette families with the established operator architecture. +- [x] Intra-block copy has exact native reconstruction and feature-isolated SIMD evidence. +- [x] Lossless inverse transform, loop filtering, CDEF, super-resolution, restoration, and film grain have focused checkpoint evidence. +- [~] Retained references, CDF snapshots, segmentation maps, global motion, temporal motion fields, and dependent-frame lifecycle exist locally and require current-tree re-verification. +- [~] All-intra and dependent-frame profile fixtures exist for 8, 10, and 12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 paths. +- [ ] Re-run the exact current-tree native-plane matrix through the production decoder. +- [ ] Re-run the exact current-tree presentation matrix through ImageSharp's established comparison API. +- [ ] Verify malformed/truncated data, frame IDs, reference slots, tile bounds, allocation limits, cancellation, and failure unwinding. +- [ ] Verify still items and bounded sequences from file, memory, non-seekable, and short-read streams. +- [ ] Verify ICC, CICP, alpha, grids, pixel aspect ratio, clean aperture, rotation, mirroring, metadata, and every presented sequence frame. +- [ ] Complete the public AVIF format/API review so registered capabilities match implemented behavior. +- [ ] Remove or reject every valid in-scope AV1 syntax branch that remains silently ignored or unsupported. + +Decoder exit gate: + +- [ ] Every supported native format and AV1 tool has exact pinned-libaom production-path evidence. +- [ ] Every supported presentation behavior has established reference-image evidence at the correct output precision. +- [ ] No decoder path relies on a native codec, copied plane, per-block allocation, or contiguous memory-group accident. +- [ ] All allocator ownership is deterministic and exactly once. +- [ ] Full focused Release verification is recorded with no false coverage claims. + +## AV1 encoder implementation + +Writer primitives are not an encoder. The public encoder remains incomplete until it produces independently decodable AV1 payloads and AVIF containers for every exposed option. + +### 5. Define and enforce the encoder contract + +- [ ] Finalize observable options for quality, effort, lossless mode, bit depth, chroma subsampling, alpha quality, metadata, and bounded sequences. +- [ ] Preserve high-bit-depth source precision through 16-bit RGB and native 10/12-bit component planes. +- [ ] Reject unsupported combinations at the public boundary before writing output. +- [ ] Register only capabilities that the completed encoder proves. + +### 6. Build the complete AV1 frame encoder + +- [~] SIMD-first RGB-to-native-plane conversion exists locally. +- [~] Forward transform families and transform workspace exist locally. +- [~] Symbol writer, coefficient writer, and tile writer fragments exist locally. +- [ ] Connect a frame-owned encoder lifecycle using ImageSharp allocators and pools. +- [ ] Write compliant temporal delimiter, sequence header, frame header, tile group, metadata, and padding OBUs as required. +- [ ] Implement superblock and partition analysis for every permitted block size and partition. +- [ ] Implement intra mode search, chroma mode search, palette, filter intra, chroma-from-luma, and intra-block copy decisions. +- [ ] Implement inter mode search for bounded sequences, including reference selection and the decoder-supported inter tools. +- [ ] Implement transform-size/type search, forward transform, quantization, coefficient optimization, and lossless behavior. +- [ ] Implement real rate-distortion selection and make quality and effort change work, size, and output quality. +- [ ] Implement tile-local entropy coding and CDF update behavior. +- [ ] Implement legal deblocking, CDEF, restoration, super-resolution, and film-grain signaling decisions. +- [ ] Remove per-transform and per-block managed allocations from active encoder paths. +- [ ] Use descending SIMD dispatch: Vector512, Vector256, Vector128, then scalar. +- [ ] Verify every SIMD operator with FeatureTestRunner and an independent scalar oracle shaped from the same pinned libaom behavior. + +### 7. Write complete AVIF output + +- [ ] Write the correct AVIF file type, item information, locations, references, properties, AV1 configuration, dimensions, color, alpha, metadata, and media data. +- [ ] Support single images, alpha auxiliary images, grids, multiple extents, and bounded image sequences in the final public scope. +- [ ] Preserve ICC, Exif, and XMP according to encoder options. +- [ ] Write CICP, range, chroma position, bit depth, and subsampling values that match the encoded planes. +- [ ] Apply orientation and clean-aperture behavior consistently with ImageSharp encoder conventions. +- [ ] Stream output through allocator-backed chunked storage without file-sized copies or ToArray materialization. + +Encoder exit gate: + +- [ ] Pinned libaom accepts every produced AV1 payload. +- [ ] Lossless output is exact at native-plane and final-pixel precision. +- [ ] Lossy output demonstrates recorded quality and effort tradeoffs with absolute size, quality, timing, and allocation evidence. +- [ ] 8, 10, and 12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 outputs pass. +- [ ] Alpha, grids, metadata, color profiles, transforms, and bounded sequences pass. +- [ ] ImageSharp decode of its own output is supplemental coverage only, never the sole oracle. +- [ ] Public encoding no longer throws for a supported AV1 request. +- [ ] Focused Release and FeatureTestRunner verification passes with exact recorded evidence. + +## Architecture rules + +- Follow the JPEG color-converter operator architecture exactly. +- Each distinct prediction traversal owns a family-named predictor type. +- The family .Operator.cs file defines the nested static operator contract. +- Each semantic readonly struct belongs to that owner and implements concrete scalar, Vector128, Vector256, and Vector512 arithmetic for the shared traversal. +- Do not place a distinct predictor beneath a broad Av1IntraPredictor or Av1InterPredictor. +- Do not create semantic forwarding wrappers, top-level operator types, hardware-width-named operator types, CRTP contracts, or one file containing unrelated semantic operators. +- Forward transforms belong to Av1ForwardTransformer and its semantic operator files. +- Inverse axis transforms belong to Av1Inverse2dTransformer and its semantic operator files. +- Reconstruction output operators belong to Av1InverseTransformer. +- Shared lane primitives belong only in explicitly named Operations types. +- Dispatch from widest to narrowest supported SIMD width, then execute one scalar tail. +- Keep codec execution sequential. Do not add parallel execution inside the codec. +- Do not allocate per row, block, transform, scanline, or SIMD tail. +- Use ImageSharp allocators and pools. Do not use ToArray to cross an ownership boundary. +- On internal types, use public members when other types consume them; reserve private members for type-local behavior. +- Use established ImageSharp test data, allocator tracking, FeatureTestRunner, and reference-image comparison APIs. Do not build custom substitutes. +- Public XML documentation describes observable behavior only. +- Inline comments explain the pinned numerical rule, ownership boundary, edge extension, entropy ordering, or SIMD shape at technically complex points. +- Every multiline statement or declaration is followed by vertical whitespace. +- Do not edit .gitattributes directly. +- Do not install or download tools without explicit permission. + +## Final verification matrix + +- [ ] Release source build: net10.0, zero errors. +- [ ] Release source build: net11.0, zero errors. +- [ ] Scoped semantic inspection: zero compiler errors attributable to this work. +- [ ] Focused decoder syntax, reconstruction, ownership, presentation, and malformed-input tests. +- [ ] Focused encoder syntax, payload, container, precision, ownership, and option tests. +- [ ] FeatureTestRunner coverage for normal, narrower SIMD tiers, and scalar fallback. +- [ ] Constrained multi-group allocator coverage with balanced exactly-once returns. +- [ ] Exact native-plane comparisons against pinned libaom. +- [ ] Established final-presentation comparisons at the target pixel precision. +- [ ] Scoped StyleCop and vertical-whitespace inspection. +- [ ] No stale unsupported capability claims or removed-code references. +- [ ] No restore-source failures, background test hosts, detached processes, or crash-report popups. +- [ ] .gitattributes unchanged. +- [ ] git diff --check clean. +- [ ] Documentation records exact commands, counts, fixture hashes, and results. +- [ ] Commit only after the relevant checkpoint is genuinely complete. +- [ ] Do not push. diff --git a/ImageSharp.sln b/ImageSharp.sln index 28e5562b7..07b9b96f4 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -666,12 +666,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Heif", "Heif", "{BA5D603A-C84C-43E5-B300-8BB886B02936}" ProjectSection(SolutionItems) = preProject - tests\Images\Input\Heif\dwsample-heic-640.heic = tests\Images\Input\Heif\dwsample-heic-640.heic - tests\Images\Input\Heif\image1.heic = tests\Images\Input\Heif\image1.heic - tests\Images\Input\Heif\image2.heic = tests\Images\Input\Heif\image2.heic - tests\Images\Input\Heif\image3.heic = tests\Images\Input\Heif\image3.heic - tests\Images\Input\Heif\image4.heic = tests\Images\Input\Heif\image4.heic - tests\Images\Input\Heif\IMG-20230508-0053.hif = tests\Images\Input\Heif\IMG-20230508-0053.hif tests\Images\Input\Heif\Irvine_CA.avif = tests\Images\Input\Heif\Irvine_CA.avif tests\Images\Input\Heif\jpeg444_xnconvert.avif = tests\Images\Input\Heif\jpeg444_xnconvert.avif tests\Images\Input\Heif\Orange4x4.avif = tests\Images\Input\Heif\Orange4x4.avif diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index 2d7aeee80..55dc93702 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -172,255 +172,3 @@ Alliance for Open Media Patent License 1.0 2.12. Specification. "Specification" means the specification designated by the Alliance for Open Media as a Final Deliverable for which this License was issued. - - -License notice for the HM HEVC Reference Software ------ - -Parts of the HEVC implementation are adapted from the HM HEVC Reference -Software. -https://vcgit.hhi.fraunhofer.de/jvet/HM - -The copyright in this software is being made available under the BSD -License, included below. This software may be subject to other third party -and contributor rights, including patent rights, and no such rights are -granted under this license. - -Copyright (c) 2010-2026, ITU/ISO/IEC -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the ITU/ISO/IEC nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - -License notice for the Ittiam libhevc Codec Library ------ - -Parts of the HEVC implementation are adapted from the Android fork of the -Ittiam libhevc Codec Library. -https://android.googlesource.com/platform/external/libhevc/ - -Copyright (C) 2012 Ittiam Systems Pvt Ltd, Bangalore - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConversionParameters.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConversionParameters.cs index d722fa275..9432facd5 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConversionParameters.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConversionParameters.cs @@ -182,7 +182,7 @@ internal readonly struct HeifColorConversionParameters public float RgbSampleMaximum { get; } /// - /// Resolves the H.273 matrix coefficients and sample ranges shared by HEVC and AV1 image items. + /// Resolves the H.273 matrix coefficients and sample ranges used by AV1 image items. /// /// The H.273 color-primary code point. /// The H.273 transfer-characteristic code point. diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs deleted file mode 100644 index 942aff347..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifCoefficientOperator.cs +++ /dev/null @@ -1,151 +0,0 @@ -// 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; - -/// -/// Provides pinned-libheif high-bit-depth coefficient conversion at the source RGB precision. -/// -internal static partial class HeifYuvToRgb16Converter -{ - /// - /// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes. - /// - private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb16Operator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector512 y, - Vector512 cb, - Vector512 cr, - in ConversionParameters parameters, - out Vector512 r, - out Vector512 g, - out Vector512 b) - { - Vector512Parameters values = parameters.SixteenLane; - Vector512 luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector512 blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector512 redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector512 half = Vector512.Create(0.5F); - - // libheif evaluates these as distinct float32 multiplies and adds; FMA changes some 12-bit results by one. - Vector512 redValue = Vector512.Multiply(values.RedCr, redDifference); - redValue = Vector512.Add(luma, redValue); - Vector512 greenValue = Vector512.Multiply(values.GreenCb, blueDifference); - greenValue = Vector512.Add(luma, greenValue); - Vector512 greenRedValue = Vector512.Multiply(values.GreenCr, redDifference); - greenValue = Vector512.Add(greenValue, greenRedValue); - Vector512 blueValue = Vector512.Multiply(values.BlueCb, blueDifference); - blueValue = Vector512.Add(luma, blueValue); - Vector512 red = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(redValue, half))); - Vector512 green = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(greenValue, half))); - Vector512 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); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector256 y, - Vector256 cb, - Vector256 cr, - in ConversionParameters parameters, - out Vector256 r, - out Vector256 g, - out Vector256 b) - { - Vector256Parameters values = parameters.EightLane; - Vector256 luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector256 blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector256 redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector256 half = Vector256.Create(0.5F); - Vector256 redValue = Vector256.Multiply(values.RedCr, redDifference); - redValue = Vector256.Add(luma, redValue); - Vector256 greenValue = Vector256.Multiply(values.GreenCb, blueDifference); - greenValue = Vector256.Add(luma, greenValue); - Vector256 greenRedValue = Vector256.Multiply(values.GreenCr, redDifference); - greenValue = Vector256.Add(greenValue, greenRedValue); - Vector256 blueValue = Vector256.Multiply(values.BlueCb, blueDifference); - blueValue = Vector256.Add(luma, blueValue); - Vector256 red = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(redValue, half))); - Vector256 green = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(greenValue, half))); - Vector256 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); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector128 y, - Vector128 cb, - Vector128 cr, - in ConversionParameters parameters, - out Vector128 r, - out Vector128 g, - out Vector128 b) - { - Vector128Parameters values = parameters.FourLane; - Vector128 luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector128 blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector128 redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector128 half = Vector128.Create(0.5F); - Vector128 redValue = Vector128.Multiply(values.RedCr, redDifference); - redValue = Vector128.Add(luma, redValue); - Vector128 greenValue = Vector128.Multiply(values.GreenCb, blueDifference); - greenValue = Vector128.Add(luma, greenValue); - Vector128 greenRedValue = Vector128.Multiply(values.GreenCr, redDifference); - greenValue = Vector128.Add(greenValue, greenRedValue); - Vector128 blueValue = Vector128.Multiply(values.BlueCb, blueDifference); - blueValue = Vector128.Add(luma, blueValue); - Vector128 red = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(redValue, half))); - Vector128 green = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(greenValue, half))); - Vector128 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); - } - - /// - [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); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs deleted file mode 100644 index 06cf40bd1..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.LibheifMonochromeOperator.cs +++ /dev/null @@ -1,83 +0,0 @@ -// 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; - -/// -/// Provides pinned-libheif high-bit-depth monochrome presentation without luma-range expansion. -/// -internal static partial class HeifYuvToRgb16Converter -{ - /// - /// Copies source-precision luma into each source-precision RGB component. - /// - private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb16Operator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector512 y, - Vector512 cb, - Vector512 cr, - in ConversionParameters parameters, - out Vector512 r, - out Vector512 g, - out Vector512 b) - { - r = y; - g = y; - b = y; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector256 y, - Vector256 cb, - Vector256 cr, - in ConversionParameters parameters, - out Vector256 r, - out Vector256 g, - out Vector256 b) - { - r = y; - g = y; - b = y; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector128 y, - Vector128 cb, - Vector128 cr, - in ConversionParameters parameters, - out Vector128 r, - out Vector128 g, - out Vector128 b) - { - r = y; - g = y; - b = y; - } - - /// - [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; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs deleted file mode 100644 index 8715f5fff..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Operator.cs +++ /dev/null @@ -1,293 +0,0 @@ -// 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; - -/// -/// Defines closed high-bit-depth color operators and nearest-sample row traversal. -/// -internal static partial class HeifYuvToRgb16Converter -{ - /// - /// Defines source-precision RGB arithmetic for scalar and SIMD lanes. - /// - private interface IHeifYuvToRgb16Operator - { - /// - /// Converts sixteen YCbCr samples to source-precision RGB lanes. - /// - /// The luma lanes. - /// The blue-difference lanes. - /// The red-difference lanes. - /// The image conversion parameters. - /// The converted red lanes. - /// The converted green lanes. - /// The converted blue lanes. - public static abstract void Convert( - Vector512 y, - Vector512 cb, - Vector512 cr, - in ConversionParameters parameters, - out Vector512 r, - out Vector512 g, - out Vector512 b); - - /// - /// Converts eight YCbCr samples to source-precision RGB lanes. - /// - /// The luma lanes. - /// The blue-difference lanes. - /// The red-difference lanes. - /// The image conversion parameters. - /// The converted red lanes. - /// The converted green lanes. - /// The converted blue lanes. - public static abstract void Convert( - Vector256 y, - Vector256 cb, - Vector256 cr, - in ConversionParameters parameters, - out Vector256 r, - out Vector256 g, - out Vector256 b); - - /// - /// Converts four YCbCr samples to source-precision RGB lanes. - /// - /// The luma lanes. - /// The blue-difference lanes. - /// The red-difference lanes. - /// The image conversion parameters. - /// The converted red lanes. - /// The converted green lanes. - /// The converted blue lanes. - public static abstract void Convert( - Vector128 y, - Vector128 cb, - Vector128 cr, - in ConversionParameters parameters, - out Vector128 r, - out Vector128 g, - out Vector128 b); - - /// - /// Converts one YCbCr sample to source-precision RGB. - /// - /// The luma sample. - /// The blue-difference sample. - /// The red-difference sample. - /// The image conversion parameters. - /// The converted red sample. - /// The converted green sample. - /// The converted blue sample. - public static abstract void Convert( - ushort y, - ushort cb, - ushort cr, - in ConversionParameters parameters, - out int r, - out int g, - out int b); - } - - /// - /// Converts one luma row and its nearest native chroma row to planar 16-bit RGB storage. - /// - /// The source-precision color arithmetic selected for the row. - /// The full-resolution luma samples. - /// The native blue-difference samples. - /// The native red-difference samples. - /// The destination red samples. - /// The destination green samples. - /// The destination blue samples. - /// The horizontal chroma subsampling shift. - /// The image conversion parameters. - private static void ConvertRow( - ReadOnlySpan luma, - ReadOnlySpan chromaBlue, - ReadOnlySpan chromaRed, - Span red, - Span green, - Span 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.Count; - - for (; x <= oneVectorFromEnd; x += Vector512.Count) - { - Vector512 y = LoadVector512(ref Unsafe.Add(ref lumaBase, x)); - Vector512 cb = subsamplingX == 0 - ? LoadVector512(ref Unsafe.Add(ref chromaBlueBase, x)) - : LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); - - Vector512 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 r, out Vector512 g, out Vector512 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.Count; - - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector256 y = LoadVector256(ref Unsafe.Add(ref lumaBase, x)); - Vector256 cb = subsamplingX == 0 - ? LoadVector256(ref Unsafe.Add(ref chromaBlueBase, x)) - : LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); - - Vector256 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 r, out Vector256 g, out Vector256 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.Count; - - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - Vector128 y = LoadVector128(ref Unsafe.Add(ref lumaBase, x)); - Vector128 cb = subsamplingX == 0 - ? LoadVector128(ref Unsafe.Add(ref chromaBlueBase, x)) - : LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); - - Vector128 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 r, out Vector128 g, out Vector128 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); - } - } - - /// - /// Loads sixteen native samples as signed 32-bit SIMD lanes. - /// - /// The first native sample. - /// The widened sample lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 LoadVector512(ref ushort source) - { - (Vector256 lower, Vector256 upper) = Vector256.Widen(Vector256.LoadUnsafe(ref source)); - return Vector512.Create(lower, upper).AsInt32(); - } - - /// - /// Loads eight native samples as signed 32-bit SIMD lanes. - /// - /// The first native sample. - /// The widened sample lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 LoadVector256(ref ushort source) - { - Vector128 samples = Vector128.LoadUnsafe(ref source); - return Vector256.Create(Vector128.WidenLower(samples), Vector128.WidenUpper(samples)).AsInt32(); - } - - /// - /// Loads four native samples as signed 32-bit SIMD lanes. - /// - /// The first native sample. - /// The widened sample lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 LoadVector128(ref ushort source) - { - ulong packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref source)); - return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32(); - } - - /// - /// Loads eight chroma samples and repeats each sample into two of sixteen 32-bit SIMD lanes. - /// - /// The first native chroma sample. - /// The horizontally replicated chroma lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 LoadRepeatedVector512(ref ushort source) - { - Vector128 samples = Vector128.LoadUnsafe(ref source); - Vector128 lower = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); - Vector128 upper = Vector128_.UnpackHigh(samples.AsInt16(), samples.AsInt16()).AsUInt16(); - (Vector256 widenedLower, Vector256 widenedUpper) = Vector256.Widen(Vector256.Create(lower, upper)); - return Vector512.Create(widenedLower, widenedUpper).AsInt32(); - } - - /// - /// Loads four chroma samples and repeats each sample into two of eight 32-bit SIMD lanes. - /// - /// The first native chroma sample. - /// The horizontally replicated chroma lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 LoadRepeatedVector256(ref ushort source) - { - ulong packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref source)); - Vector128 samples = Vector128.CreateScalarUnsafe(packed).AsUInt16(); - Vector128 repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); - return Vector256.Create(Vector128.WidenLower(repeated), Vector128.WidenUpper(repeated)).AsInt32(); - } - - /// - /// Loads two chroma samples and repeats each sample into two of four 32-bit SIMD lanes. - /// - /// The first native chroma sample. - /// The horizontally replicated chroma lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 LoadRepeatedVector128(ref ushort source) - { - uint packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref source)); - Vector128 samples = Vector128.CreateScalarUnsafe(packed).AsUInt16(); - Vector128 repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); - return Vector128.WidenLower(repeated).AsInt32(); - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs deleted file mode 100644 index b7cbbd770..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.Parameters.cs +++ /dev/null @@ -1,347 +0,0 @@ -// 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; - -/// -/// Provides scalar and SIMD parameter storage for high-bit-depth pinned-libheif conversion. -/// -internal static partial class HeifYuvToRgb16Converter -{ - /// - /// Stores every scalar and SIMD coefficient representation resolved once for an image. - /// - private readonly struct ConversionParameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The resolved H.273 matrix and range values. - /// The common source component precision. - 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); - } - - /// - /// Gets the scalar conversion parameters. - /// - public ScalarParameters Scalar { get; } - - /// - /// Gets the sixteen-lane conversion parameters. - /// - public Vector512Parameters SixteenLane { get; } - - /// - /// Gets the eight-lane conversion parameters. - /// - public Vector256Parameters EightLane { get; } - - /// - /// Gets the four-lane conversion parameters. - /// - public Vector128Parameters FourLane { get; } - } - - /// - /// Stores the scalar arithmetic and output scaling used by pinned libheif. - /// - private readonly struct ScalarParameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The resolved H.273 matrix and range values. - /// The common source component precision. - 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; - } - - /// - /// Gets the luma code-value offset removed before limited-range expansion. - /// - public float LumaOffset { get; } - - /// - /// Gets the luma range-expansion factor. - /// - public float LumaScale { get; } - - /// - /// Gets the neutral chroma code value. - /// - public float ChromaMidpoint { get; } - - /// - /// Gets the chroma range-expansion factor. - /// - public float ChromaScale { get; } - - /// - /// Gets the red contribution from Cr. - /// - public float RedCr { get; } - - /// - /// Gets the green contribution from Cb. - /// - public float GreenCb { get; } - - /// - /// Gets the green contribution from Cr. - /// - public float GreenCr { get; } - - /// - /// Gets the blue contribution from Cb. - /// - public float BlueCb { get; } - - /// - /// Gets the largest source-precision RGB code value. - /// - public int Maximum { get; } - - /// - /// Gets the left shift mapping source-precision RGB into 16-bit pixel storage. - /// - public int OutputLeftShift { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for sixteen-lane conversion. - /// - private readonly struct Vector512Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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); - } - - /// - /// Gets the luma offset lanes. - /// - public Vector512 LumaOffset { get; } - - /// - /// Gets the luma scale lanes. - /// - public Vector512 LumaScale { get; } - - /// - /// Gets the chroma-midpoint lanes. - /// - public Vector512 ChromaMidpoint { get; } - - /// - /// Gets the chroma-scale lanes. - /// - public Vector512 ChromaScale { get; } - - /// - /// Gets the red Cr coefficient lanes. - /// - public Vector512 RedCr { get; } - - /// - /// Gets the green Cb coefficient lanes. - /// - public Vector512 GreenCb { get; } - - /// - /// Gets the green Cr coefficient lanes. - /// - public Vector512 GreenCr { get; } - - /// - /// Gets the blue Cb coefficient lanes. - /// - public Vector512 BlueCb { get; } - - /// - /// Gets the maximum source-precision RGB lanes. - /// - public Vector512 Maximum { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for eight-lane conversion. - /// - private readonly struct Vector256Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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); - } - - /// - /// Gets the luma offset lanes. - /// - public Vector256 LumaOffset { get; } - - /// - /// Gets the luma scale lanes. - /// - public Vector256 LumaScale { get; } - - /// - /// Gets the chroma-midpoint lanes. - /// - public Vector256 ChromaMidpoint { get; } - - /// - /// Gets the chroma-scale lanes. - /// - public Vector256 ChromaScale { get; } - - /// - /// Gets the red Cr coefficient lanes. - /// - public Vector256 RedCr { get; } - - /// - /// Gets the green Cb coefficient lanes. - /// - public Vector256 GreenCb { get; } - - /// - /// Gets the green Cr coefficient lanes. - /// - public Vector256 GreenCr { get; } - - /// - /// Gets the blue Cb coefficient lanes. - /// - public Vector256 BlueCb { get; } - - /// - /// Gets the maximum source-precision RGB lanes. - /// - public Vector256 Maximum { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for four-lane conversion. - /// - private readonly struct Vector128Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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); - } - - /// - /// Gets the luma offset lanes. - /// - public Vector128 LumaOffset { get; } - - /// - /// Gets the luma scale lanes. - /// - public Vector128 LumaScale { get; } - - /// - /// Gets the chroma-midpoint lanes. - /// - public Vector128 ChromaMidpoint { get; } - - /// - /// Gets the chroma-scale lanes. - /// - public Vector128 ChromaScale { get; } - - /// - /// Gets the red Cr coefficient lanes. - /// - public Vector128 RedCr { get; } - - /// - /// Gets the green Cb coefficient lanes. - /// - public Vector128 GreenCb { get; } - - /// - /// Gets the green Cr coefficient lanes. - /// - public Vector128 GreenCr { get; } - - /// - /// Gets the blue Cb coefficient lanes. - /// - public Vector128 BlueCb { get; } - - /// - /// Gets the maximum source-precision RGB lanes. - /// - public Vector128 Maximum { get; } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs deleted file mode 100644 index 127a8ada0..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb16Converter.cs +++ /dev/null @@ -1,114 +0,0 @@ -// 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; - -/// -/// Converts high-bit-depth HEIF YUV planes to packed pixels through opaque 16-bit RGB. -/// -internal static partial class HeifYuvToRgb16Converter -{ - /// - /// Determines whether the pinned libheif-compatible high-bit-depth conversion supports the supplied planes. - /// - /// The horizontal chroma subsampling shift. - /// The vertical chroma subsampling shift. - /// The luma sample precision in bits. - /// The chroma sample precision in bits. - /// Whether the image contains only luma samples. - /// The resolved H.273 conversion operation. - /// when the planes can use this converter; otherwise, . - 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; - - /// - /// Converts supported high-bit-depth HEVC planes using pinned libheif arithmetic and nearest chroma sampling. - /// - /// The destination pixel type. - /// The codec adapter that exposes reconstructed component rows. - /// The configuration used for allocation and pixel conversion. - /// The reconstructed component-plane buffer. - /// The destination image frame. - /// The resolved H.273 conversion parameters. - /// The horizontal luma-sample offset of the output window. - /// The vertical luma-sample offset of the output window. - public static void Convert( - Configuration configuration, - TBuffer buffer, - ImageFrame image, - in HeifColorConversionParameters parameters, - int sourceX, - int sourceY) - where TPixel : unmanaged, IPixel - where TBuffer : struct, IHeifPlanarSampleBuffer - { - 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 rowOwner = configuration.MemoryAllocator.Allocate(image.Width * 7); - Span storage = rowOwner.GetSpan(); - Span red = storage[..image.Width]; - Span green = storage.Slice(image.Width, image.Width); - Span blue = storage.Slice(image.Width * 2, image.Width); - Span packed = MemoryMarshal.Cast(storage[(image.Width * 3)..]); - - for (int y = 0; y < image.Height; y++) - { - int lumaY = sourceY + y; - ReadOnlySpan 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( - luma, - luma, - luma, - red, - green, - blue, - 0, - in conversionParameters); - } - else - { - int subsamplingX = buffer.ChromaSubsamplingX; - int chromaY = lumaY >> buffer.ChromaSubsamplingY; - ReadOnlySpan chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX); - ReadOnlySpan 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( - luma, - chromaBlue, - chromaRed, - red, - green, - blue, - subsamplingX, - in conversionParameters); - } - - HeifSampleConversion.PackRgba64(red, green, blue, packed); - Span destination = image.PixelBuffer.DangerousGetRowSpan(y); - PixelOperations.Instance.FromRgba64(configuration, packed, destination); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs deleted file mode 100644 index f7946a5c3..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifCoefficientOperator.cs +++ /dev/null @@ -1,125 +0,0 @@ -// 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; - -/// -/// 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. -/// -internal static partial class HeifYuvToRgb8Converter -{ - /// - /// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes. - /// - private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb8Operator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector512 y, - Vector512 cb, - Vector512 cr, - in ConversionParameters parameters, - out Vector512 r, - out Vector512 g, - out Vector512 b) - { - LibheifVector512Parameters values = parameters.LibheifSixteenLane; - Vector512 luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector512 blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector512 redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector512 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 red = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.RedCr * redDifference) + half)); - Vector512 green = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); - Vector512 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; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector256 y, - Vector256 cb, - Vector256 cr, - in ConversionParameters parameters, - out Vector256 r, - out Vector256 g, - out Vector256 b) - { - LibheifVector256Parameters values = parameters.LibheifEightLane; - Vector256 luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector256 blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector256 redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector256 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 red = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.RedCr * redDifference) + half)); - Vector256 green = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); - Vector256 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; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector128 y, - Vector128 cb, - Vector128 cr, - in ConversionParameters parameters, - out Vector128 r, - out Vector128 g, - out Vector128 b) - { - LibheifVector128Parameters values = parameters.LibheifFourLane; - Vector128 luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; - Vector128 blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; - Vector128 redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; - Vector128 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 red = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.RedCr * redDifference) + half)); - Vector128 green = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); - Vector128 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; - } - - /// - [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); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs deleted file mode 100644 index a62795004..000000000 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.LibheifMonochromeOperator.cs +++ /dev/null @@ -1,82 +0,0 @@ -// 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; - -/// -/// 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. -/// -internal static partial class HeifYuvToRgb8Converter -{ - /// - /// Implements pinned-libheif monochrome conversion for scalar and SIMD lanes. - /// - private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb8Operator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector512 y, - Vector512 cb, - Vector512 cr, - in ConversionParameters parameters, - out Vector512 r, - out Vector512 g, - out Vector512 b) - { - Vector512 value = y >> parameters.LibheifSixteenLane.OutputShift; - r = value; - g = value; - b = value; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector256 y, - Vector256 cb, - Vector256 cr, - in ConversionParameters parameters, - out Vector256 r, - out Vector256 g, - out Vector256 b) - { - Vector256 value = y >> parameters.LibheifEightLane.OutputShift; - r = value; - g = value; - b = value; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Convert( - Vector128 y, - Vector128 cb, - Vector128 cr, - in ConversionParameters parameters, - out Vector128 r, - out Vector128 g, - out Vector128 b) - { - Vector128 value = y >> parameters.LibheifFourLane.OutputShift; - r = value; - g = value; - b = value; - } - - /// - [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; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs index 340b7db39..e5e95fb28 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.Parameters.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; namespace SixLabors.ImageSharp.Formats.Heif.Components; @@ -36,44 +35,17 @@ internal static partial class HeifYuvToRgb8Converter /// public readonly Vector512Parameters FixedPointSixteenLane; - /// - /// The scalar pinned-libheif conversion parameters. - /// - public readonly LibheifParameters LibheifScalar; - - /// - /// The four-lane pinned-libheif conversion parameters. - /// - public readonly LibheifVector128Parameters LibheifFourLane; - - /// - /// The eight-lane pinned-libheif conversion parameters. - /// - public readonly LibheifVector256Parameters LibheifEightLane; - - /// - /// The sixteen-lane pinned-libheif conversion parameters. - /// - public readonly LibheifVector512Parameters LibheifSixteenLane; - /// /// Initializes a new instance of the struct. /// /// The shared floating-point conversion parameters. - /// The common source component precision. - public ConversionParameters(in HeifColorConversionParameters parameters, int bitDepth) + public ConversionParameters(in HeifColorConversionParameters parameters) { 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); } } @@ -286,255 +258,4 @@ internal static partial class HeifYuvToRgb8Converter /// public Vector512 BlueCb { get; } } - - /// - /// Stores the scalar coefficients and range values used by pinned libheif 1.23.1. - /// - private readonly struct LibheifParameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The resolved H.273 matrix and range values. - /// The common source component precision. - 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; - } - - /// - /// Gets the luma code-value offset removed before limited-range expansion. - /// - public float LumaOffset { get; } - - /// - /// Gets the luma range-expansion factor. - /// - public float LumaScale { get; } - - /// - /// Gets the neutral chroma code value. - /// - public float ChromaMidpoint { get; } - - /// - /// Gets the chroma range-expansion factor. - /// - public float ChromaScale { get; } - - /// - /// Gets the red contribution from Cr. - /// - public float RedCr { get; } - - /// - /// Gets the green contribution from Cb. - /// - public float GreenCb { get; } - - /// - /// Gets the green contribution from Cr. - /// - public float GreenCr { get; } - - /// - /// Gets the blue contribution from Cb. - /// - public float BlueCb { get; } - - /// - /// Gets the largest RGB code value at the source precision. - /// - public int Maximum { get; } - - /// - /// Gets the right shift reducing source-precision RGB to eight bits. - /// - public int OutputShift { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for four-lane conversion. - /// - private readonly struct LibheifVector128Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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; - } - - /// Gets the luma offset lanes. - public Vector128 LumaOffset { get; } - - /// Gets the luma scale lanes. - public Vector128 LumaScale { get; } - - /// Gets the chroma-midpoint lanes. - public Vector128 ChromaMidpoint { get; } - - /// Gets the chroma-scale lanes. - public Vector128 ChromaScale { get; } - - /// Gets the red Cr coefficient lanes. - public Vector128 RedCr { get; } - - /// Gets the green Cb coefficient lanes. - public Vector128 GreenCb { get; } - - /// Gets the green Cr coefficient lanes. - public Vector128 GreenCr { get; } - - /// Gets the blue Cb coefficient lanes. - public Vector128 BlueCb { get; } - - /// Gets the maximum RGB code-value lanes. - public Vector128 Maximum { get; } - - /// Gets the output reduction shift. - public int OutputShift { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for eight-lane conversion. - /// - private readonly struct LibheifVector256Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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; - } - - /// Gets the luma offset lanes. - public Vector256 LumaOffset { get; } - - /// Gets the luma scale lanes. - public Vector256 LumaScale { get; } - - /// Gets the chroma-midpoint lanes. - public Vector256 ChromaMidpoint { get; } - - /// Gets the chroma-scale lanes. - public Vector256 ChromaScale { get; } - - /// Gets the red Cr coefficient lanes. - public Vector256 RedCr { get; } - - /// Gets the green Cb coefficient lanes. - public Vector256 GreenCb { get; } - - /// Gets the green Cr coefficient lanes. - public Vector256 GreenCr { get; } - - /// Gets the blue Cb coefficient lanes. - public Vector256 BlueCb { get; } - - /// Gets the maximum RGB code-value lanes. - public Vector256 Maximum { get; } - - /// Gets the output reduction shift. - public int OutputShift { get; } - } - - /// - /// Broadcasts pinned-libheif coefficients for sixteen-lane conversion. - /// - private readonly struct LibheifVector512Parameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The scalar pinned-libheif coefficients. - 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; - } - - /// Gets the luma offset lanes. - public Vector512 LumaOffset { get; } - - /// Gets the luma scale lanes. - public Vector512 LumaScale { get; } - - /// Gets the chroma-midpoint lanes. - public Vector512 ChromaMidpoint { get; } - - /// Gets the chroma-scale lanes. - public Vector512 ChromaScale { get; } - - /// Gets the red Cr coefficient lanes. - public Vector512 RedCr { get; } - - /// Gets the green Cb coefficient lanes. - public Vector512 GreenCb { get; } - - /// Gets the green Cr coefficient lanes. - public Vector512 GreenCr { get; } - - /// Gets the blue Cb coefficient lanes. - public Vector512 BlueCb { get; } - - /// Gets the maximum RGB code-value lanes. - public Vector512 Maximum { get; } - - /// Gets the output reduction shift. - public int OutputShift { get; } - } } diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs index 6e2f74942..b900f49f5 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuvToRgb8Converter.cs @@ -56,28 +56,6 @@ internal static partial class HeifYuvToRgb8Converter && matrixCoefficients == CicpMatrixCoefficients.Unspecified && mode == HeifColorConversionMode.Coefficients; - /// - /// Determines whether the pinned libheif-compatible conversion supports the supplied plane and color description. - /// - /// The horizontal chroma subsampling shift. - /// The vertical chroma subsampling shift. - /// The luma sample precision in bits. - /// The chroma sample precision in bits. - /// Whether the image contains only luma samples. - /// The resolved H.273 conversion operation. - /// when the planes can use this converter; otherwise, . - 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; - /// /// Converts supported HEIF component planes to packed pixels using integer SIMD with a scalar tail. /// @@ -99,7 +77,7 @@ internal static partial class HeifYuvToRgb8Converter where TPixel : unmanaged, IPixel where TBuffer : struct, IHeifPlanarSampleBuffer { - ConversionParameters conversionParameters = new(in parameters, 8); + ConversionParameters conversionParameters = new(in parameters); using IMemoryOwner componentOwner = configuration.MemoryAllocator.Allocate(image.Width * 3); Span components = componentOwner.GetSpan(); Span red = components[..image.Width]; @@ -132,76 +110,4 @@ internal static partial class HeifYuvToRgb8Converter PixelOperations.Instance.PackFromRgbPlanes(red, green, blue, destination); } } - - /// - /// Converts supported HEVC planes with the arithmetic and nearest-sample traversal used by pinned libheif. - /// - /// The destination pixel type. - /// The codec adapter that exposes reconstructed component rows. - /// The configuration used for allocation and pixel conversion. - /// The reconstructed component-plane buffer. - /// The destination image frame. - /// The resolved H.273 conversion parameters. - /// The horizontal luma-sample offset of the output window. - /// The vertical luma-sample offset of the output window. - public static void ConvertLibheif( - Configuration configuration, - TBuffer buffer, - ImageFrame image, - in HeifColorConversionParameters parameters, - int sourceX, - int sourceY) - where TPixel : unmanaged, IPixel - where TBuffer : struct, IHeifPlanarSampleBuffer - { - ConversionParameters conversionParameters = new(in parameters, buffer.LumaBitDepth); - using IMemoryOwner componentOwner = configuration.MemoryAllocator.Allocate(image.Width * 3); - Span components = componentOwner.GetSpan(); - Span red = components[..image.Width]; - Span green = components.Slice(image.Width, image.Width); - Span blue = components.Slice(image.Width * 2, image.Width); - - for (int y = 0; y < image.Height; y++) - { - int lumaY = sourceY + y; - ReadOnlySpan 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( - 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 chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX); - ReadOnlySpan chromaRed = buffer.GetChromaRedRowSpan(chromaY).Slice(sourceX >> subsamplingX); - - ConvertRow( - luma, - chromaBlue, - chromaRed, - red, - green, - blue, - subsamplingX, - in conversionParameters); - } - - Span destination = image.PixelBuffer.DangerousGetRowSpan(y); - PixelOperations.Instance.PackFromRgbPlanes(red, green, blue, destination); - } - } } diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs index 3afe3c9b7..1565dbcdc 100644 --- a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs +++ b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs @@ -238,11 +238,6 @@ public enum Heif4CharCode : uint /// Ndwt = 0x6E647774U, - /// - /// HEVC codec-configuration item property. - /// - HvcC = 0x68766343U, - /// /// AV1 configuration. /// @@ -323,36 +318,6 @@ public enum Heif4CharCode : uint /// Ipma = 0x69706D61U, - /// - /// High Efficient Image Coding brand. - /// - Heic = 0x68656963U, - - /// - /// High Efficient Image Coding brand (legacy name). - /// - Heix = 0x68656978U, - - /// - /// HEVC image sequence brand. - /// - Hevc = 0x68657663U, - - /// - /// HEVC Main 10 image sequence brand. - /// - Hevx = 0x68657678U, - - /// - /// Layered HEVC image sequence brand. - /// - Hevm = 0x6865766DU, - - /// - /// Layered HEVC image sequence brand. - /// - Hevs = 0x68657673U, - /// /// High Efficient File brand. /// @@ -383,21 +348,6 @@ public enum Heif4CharCode : uint /// Iso8 = 0x69736F38U, - /// - /// HEVC-coded image item. - /// - Hvc1 = 0x68766331U, - - /// - /// Layered High Efficiency Coding sample. - /// - Hvc2 = 0x68766332U, - - /// - /// Layered High Efficiency Coding sample. - /// - Lhv1 = 0x6C687631U, - /// /// Legacy JPEG coded tile. /// diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.tt b/src/ImageSharp/Formats/Heif/Heif4CharCode.tt index 810a14ed9..a983a8ad5 100644 --- a/src/ImageSharp/Formats/Heif/Heif4CharCode.tt +++ b/src/ImageSharp/Formats/Heif/Heif4CharCode.tt @@ -52,7 +52,6 @@ "amve", "Ambient viewing environment", "reve", "Reference viewing environment", "ndwt", "Nominal diffuse white", - "hvcC", "HVC configuration", "av1C", "AV1 configuration", "a1op", "AV1 operating-point selector", "lsel", "AV1 layer selector", @@ -69,21 +68,12 @@ "ipmc", "IPMP Control Box", "ipco", "Item Property Container", "ipma", "Item Property Association", - "heic", "High Efficient Image Coding brand", - "heix", "High Efficient Image Coding brand (legacy name)", - "hevc", "HEVC image sequence brand", - "hevx", "HEVC Main 10 image sequence brand", - "hevm", "Layered HEVC image sequence brand", - "hevs", "Layered HEVC image sequence brand", "mif1", "High Efficient File brand", "avif", "AVIF brand", "avis", "AVIF image sequence brand", "avio", "AVIF intra-only image sequence brand", "msf1", "HEIF image sequence structural brand", "iso8", "ISO base media version 8 structural brand", - "hvc1", "High Efficiency Coding tile", - "hvc2", "Layered High Efficiency Coding sample", - "lhv1", "Layered High Efficiency Coding sample", "jpeg", "Legacy JPEG coded tile", "jpgs", "JPEG image sequence brand", "av01", "AOMedia Video Coding tile", diff --git a/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs b/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs index 3e73b0148..df0095703 100644 --- a/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs +++ b/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Heif; @@ -22,7 +21,6 @@ internal static class HeifCompressionFactory { Heif4CharCode.Jpeg => new JpegHeifItemDecoder(), Heif4CharCode.Av01 => new Av1HeifItemDecoder(), - Heif4CharCode.Hvc1 => new HevcHeifItemDecoder(), _ => null }; } diff --git a/src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs b/src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs index f317301b4..2accbeec3 100644 --- a/src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs +++ b/src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs @@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// public enum HeifCompressionMethod { - /// - /// High Efficiency Video Coding (HEVC). - /// - Hevc, - /// /// Legacy JPEG coding. /// diff --git a/src/ImageSharp/Formats/Heif/HeifConstants.cs b/src/ImageSharp/Formats/Heif/HeifConstants.cs index 5f5ed075d..ed184edcc 100644 --- a/src/ImageSharp/Formats/Heif/HeifConstants.cs +++ b/src/ImageSharp/Formats/Heif/HeifConstants.cs @@ -10,30 +10,20 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// internal static class HeifConstants { - /// - /// The HEIC still-image brand written by the encoder. - /// - public const Heif4CharCode HeicBrand = Heif4CharCode.Heic; - /// /// The auxiliary-type URN used by current HEIF alpha image items. /// public const string AlphaAuxiliaryType = "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; /// - /// The auxiliary-type URN used by legacy HEVC alpha image items. - /// - public const string LegacyAlphaAuxiliaryType = "urn:mpeg:hevc:2015:auxid:1"; - - /// - /// The list of mimetypes that equate to a HEIC. + /// The MIME types recognized by this HEIF implementation. /// - public static readonly IEnumerable MimeTypes = new[] { "image/heif", "image/heic", "image/avif" }; + public static readonly IEnumerable MimeTypes = new[] { "image/heif", "image/avif" }; /// - /// The list of file extensions that equate to a HEIC. + /// The file extensions recognized by this HEIF implementation. /// - public static readonly IEnumerable FileExtensions = new[] { "heic", "heif", "hif", "avif" }; + public static readonly IEnumerable FileExtensions = new[] { "heif", "hif", "avif" }; /// /// Determines the supported image presentation declared by a file-type box. @@ -98,9 +88,9 @@ internal static class HeifConstants /// Determines whether an auxiliary-type property identifies an alpha image plane. /// /// The null-terminated auxiliary type decoded from an auxC property. - /// when the type is either registered HEIF alpha URN. + /// when the type is the registered HEIF alpha URN. public static bool IsAlphaAuxiliaryType(string? auxiliaryType) - => auxiliaryType is AlphaAuxiliaryType or LegacyAlphaAuxiliaryType; + => auxiliaryType == AlphaAuxiliaryType; /// /// Determines whether identifies a still-image container supported by this codec. @@ -108,9 +98,7 @@ internal static class HeifConstants /// The registered file-type brand. /// when the brand identifies a supported still-image container. private static bool IsSupportedStillImageBrand(Heif4CharCode brand) - => brand is Heif4CharCode.Heic - or Heif4CharCode.Heix - or Heif4CharCode.Mif1 + => brand is Heif4CharCode.Mif1 or Heif4CharCode.Avif or Heif4CharCode.Jpeg; @@ -120,17 +108,13 @@ internal static class HeifConstants /// The registered file-type brand. /// when the brand identifies a supported timed image sequence. private static bool IsSupportedSequenceBrand(Heif4CharCode brand) - => brand is Heif4CharCode.Hevc - or Heif4CharCode.Hevx - or Heif4CharCode.Avis; + => brand is Heif4CharCode.Avis; /// /// Determines whether requires an image-sequence profile outside the implemented scope. /// /// The registered file-type brand. - /// when the major brand requires layered HEVC or JPEG sequence support. + /// when the major brand requires unsupported JPEG sequence support. private static bool IsUnsupportedSequenceBrand(Heif4CharCode brand) - => brand is Heif4CharCode.Hevm - or Heif4CharCode.Hevs - or Heif4CharCode.Jpgs; + => brand is Heif4CharCode.Jpgs; } diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 3e01a22c2..7c5d70bff 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -7,7 +7,6 @@ using System.Text; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Components.Alpha; -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -254,7 +253,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore } /// - /// Locates and parses the single movie box of a supported HEIC or AVIF image sequence. + /// Locates and parses the single movie box of a supported AVIF image sequence. /// /// The complete container stream positioned after its file-type box. /// The bounded selected image-sequence model. @@ -284,7 +283,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore } /// - /// Creates image and frame metadata from a parsed HEIC or AVIF image sequence without decoding its samples. + /// Creates image and frame metadata from a parsed AVIF image sequence without decoding its samples. /// /// The parsed selected image sequence. /// The identified dimensions and bounded visible-frame metadata. @@ -298,7 +297,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore } /// - /// Decodes the retained visible samples of a HEIC or AVIF image sequence into one multi-frame image. + /// Decodes the retained visible samples of an AVIF image sequence into one multi-frame image. /// /// The destination pixel format. /// The complete seekable HEIF stream. @@ -653,14 +652,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore heifMetadata.BitDepth = av1Configuration.BitDepth; heifMetadata.IsMonochrome = av1Configuration.IsMonochrome; break; - case Heif4CharCode.Hvc1: - HevcCodecConfiguration hevcConfiguration = colorTrack.HevcCodecConfiguration - ?? throw new InvalidImageContentException("The HEVC image-sequence track has no codec configuration."); - - heifMetadata.CompressionMethod = HeifCompressionMethod.Hevc; - heifMetadata.BitDepth = hevcConfiguration.BitDepth; - heifMetadata.IsMonochrome = hevcConfiguration.IsMonochrome; - break; default: throw new InvalidImageContentException($"The image-sequence sample entry '{colorTrack.CodecType}' is not supported."); } @@ -785,7 +776,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore } HeifMetadata meta = metadata.GetHeifMetadata(); - HeifCompressionMethod compressionMethod = HeifCompressionMethod.Hevc; + HeifCompressionMethod compressionMethod; if (metadataItem.Type == Heif4CharCode.Av01) { Av1CodecConfiguration codecConfiguration = metadataItem.Av1CodecConfiguration @@ -795,23 +786,14 @@ internal sealed class HeifDecoderCore : ImageDecoderCore meta.BitDepth = codecConfiguration.BitDepth; meta.IsMonochrome = codecConfiguration.IsMonochrome; } - else if (metadataItem.Type == Heif4CharCode.Hvc1) - { - HevcCodecConfiguration codecConfiguration = metadataItem.HevcCodecConfiguration - ?? throw new InvalidImageContentException($"HEVC image item {metadataItem.Id} has no codec configuration property."); - - if (metadataItem.ChannelBitDepths is not null) - { - codecConfiguration.ValidateChannelBitDepths(metadataItem.ChannelBitDepths); - } - - meta.BitDepth = codecConfiguration.BitDepth; - meta.IsMonochrome = codecConfiguration.IsMonochrome; - } else if (metadataItem.Type == Heif4CharCode.Jpeg) { compressionMethod = HeifCompressionMethod.LegacyJpeg; } + else + { + throw new InvalidImageContentException($"Image item {metadataItem.Id} uses unsupported item type '{metadataItem.Type}'."); + } meta.CompressionMethod = compressionMethod; meta.HasAlpha = this.FindAlphaItem(presentationItem) is not null @@ -1511,13 +1493,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore Heif4CharCode.A1lx, HeifPropertyParser.ParseAv1LayeredImageIndex(boxBuffer))); - break; - case Heif4CharCode.HvcC: - properties.Add( - new KeyValuePair( - Heif4CharCode.HvcC, - new HevcCodecConfiguration(boxBuffer))); - break; case Heif4CharCode.Clap: properties.Add( @@ -1799,29 +1774,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore item.Av1LayeredImageIndex = layeredImageIndex; } - break; - case Heif4CharCode.HvcC: - if (prop.Value is HevcCodecConfiguration hevcCodecConfiguration) - { - if (item.Type != Heif4CharCode.Hvc1) - { - this.ThrowOrIgnoreImageDataSegmentError( - $"Item {itemId} associates an HEVC codec configuration with non-HEVC item type '{item.Type}'."); - - break; - } - - if (item.HevcCodecConfiguration is not null) - { - this.ThrowOrIgnoreImageDataSegmentError( - $"Item {itemId} associates more than one HEVC codec configuration property."); - - break; - } - - item.HevcCodecConfiguration = hevcCodecConfiguration; - } - break; case Heif4CharCode.AuxC: if (prop.Value is string auxiliaryType) @@ -2352,15 +2304,8 @@ internal sealed class HeifDecoderCore : ImageDecoderCore this.ApplyAssociatedMetadata(image.Metadata, rootItem, buffers); } - if (itemDecoder is HevcHeifItemDecoder hevcItemDecoder) - { - // The codec orientation describes the complete cropped picture. Item scaling and alpha composition - // must finish first so rotation neither resizes back to ispe nor leaves the auxiliary plane unrotated. - hevcItemDecoder.ApplySupplementalPresentation(image); - } - // MIAF defines crop, rotation, and mirror as presentation operations in that order. Applying the - // container transforms after codec presentation keeps every composed plane in the same coordinate space. + // container transforms after item composition keeps every composed plane in the same coordinate space. ApplyPresentationTransforms(image, itemToDecode); if (!this.Options.SkipMetadata) diff --git a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs index 299b1fff2..8221fcca7 100644 --- a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs @@ -52,7 +52,6 @@ internal sealed class HeifEncoderCore { HeifCompressionMethod.LegacyJpeg => this.CompressPixels(image, cancellationToken), HeifCompressionMethod.Av1 => throw new NotSupportedException("AV1 encoding is not implemented."), - HeifCompressionMethod.Hevc => throw new NotSupportedException("HEVC encoding is not implemented."), _ => throw new NotSupportedException($"HEIF compression method '{this.encoder.CompressionMethod}' is not supported.") }; @@ -139,15 +138,11 @@ internal sealed class HeifEncoderCore /// The destination stream. private void WriteFileTypeBox(Stream stream) { - Span buffer = stackalloc byte[24]; + Span buffer = stackalloc byte[16]; int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Ftyp); - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Heic); - bytesWritten += 4; - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0); - bytesWritten += 4; BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Mif1); bytesWritten += 4; - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Heic); + BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0); bytesWritten += 4; BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten); @@ -455,7 +450,7 @@ internal sealed class HeifEncoderCore if (this.encoder.Quality == 0) { - // Zero is meaningful to the AV1 and HEVC quality scales, but ImageSharp's JPEG encoder deliberately + // Zero is meaningful to the AV1 quality scale, but ImageSharp's JPEG encoder deliberately // exposes the JPEG quality scale as 1 through 100. Reject the codec-specific mismatch at this boundary. throw new NotSupportedException("Legacy JPEG image items support quality values in the range [1..100]."); } diff --git a/src/ImageSharp/Formats/Heif/HeifFileType.cs b/src/ImageSharp/Formats/Heif/HeifFileType.cs index 86c1cb9b1..3031e30fa 100644 --- a/src/ImageSharp/Formats/Heif/HeifFileType.cs +++ b/src/ImageSharp/Formats/Heif/HeifFileType.cs @@ -19,7 +19,7 @@ internal enum HeifFileType StillImage, /// - /// The container presents a timed HEIC or AVIF image sequence. + /// The container presents a timed AVIF image sequence. /// ImageSequence } diff --git a/src/ImageSharp/Formats/Heif/HeifItem.cs b/src/ImageSharp/Formats/Heif/HeifItem.cs index dd4ecd9c4..80f095722 100644 --- a/src/ImageSharp/Formats/Heif/HeifItem.cs +++ b/src/ImageSharp/Formats/Heif/HeifItem.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Heif.Av1; -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.Metadata.Profiles.Cicp; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -127,12 +126,6 @@ internal class HeifItem(Heif4CharCode type, uint id) /// public Av1LayeredImageIndex? Av1LayeredImageIndex { get; set; } - /// - /// Gets or sets the HEVC codec configuration associated with this coded image item, or - /// when the item has no HEVC codec-configuration property. - /// - public HevcCodecConfiguration? HevcCodecConfiguration { get; set; } - /// /// Gets or sets the relative pixel spacing associated with this image item, or when the /// item has no pixel-aspect-ratio property. diff --git a/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs b/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs index d8d2a51fc..c9b2fb4a6 100644 --- a/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs +++ b/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Heif.Av1; -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -847,30 +846,10 @@ internal sealed class HeifSequenceParser this.ParseDirectReferences(stream, sampleGroupDescriptions, sampleToGroup, track, scratch); } - if (compositionOffsets.IsPresent) + if (compositionOffsets.IsPresent || compositionToDecode.IsPresent) { - if (track.CodecType == Heif4CharCode.Av01) - { - // AV1-ISOBMFF defines AV1 sample composition time as decode time and explicitly prohibits ctts. - throw new InvalidImageContentException("An AV1 image-sequence track contains a prohibited composition-offset box."); - } - - stream.Position = compositionOffsets.Offset; - CompositionSummary composition = ParseCompositionOffsets(stream, compositionOffsets.Length, track, scratch); - if (composition.HasHiddenSamples && (!compositionToDecode.IsPresent || !track.HasEditList)) - { - throw new InvalidImageContentException("A HEVC image-sequence track has hidden samples without the required composition and edit boxes."); - } - - if (compositionToDecode.IsPresent) - { - stream.Position = compositionToDecode.Offset; - ParseCompositionToDecode(stream, compositionToDecode.Length, composition, scratch); - } - } - else if (compositionToDecode.IsPresent) - { - throw new InvalidImageContentException("The composition-to-decode box has no composition-offset table."); + // AV1-ISOBMFF defines AV1 sample composition time as decode time and explicitly prohibits both boxes. + throw new InvalidImageContentException("An AV1 image-sequence track contains prohibited composition timing boxes."); } SetCompositionTimes(track); @@ -899,7 +878,7 @@ internal sealed class HeifSequenceParser } long entryLength = HeifBoxReader.ReadHeader(stream, descriptionEnd, scratch, out Heif4CharCode entryType); - if (entryType is not Heif4CharCode.Av01 and not Heif4CharCode.Hvc1 || entryLength < 78) + if (entryType != Heif4CharCode.Av01 || entryLength < 78) { throw new InvalidImageContentException($"The image-sequence sample entry '{entryType}' is unsupported or truncated."); } @@ -941,19 +920,6 @@ internal sealed class HeifSequenceParser track.Av1CodecConfiguration = new Av1CodecConfiguration(configuration.GetSpan(), this.options); } - configurationSeen = true; - break; - case Heif4CharCode.HvcC when entryType == Heif4CharCode.Hvc1: - if (configurationSeen) - { - throw new InvalidImageContentException("The HEVC image-sequence sample entry has duplicate codec configurations."); - } - - using (IMemoryOwner configuration = this.boxReader.ReadPayload(stream, childLength)) - { - track.HevcCodecConfiguration = new HevcCodecConfiguration(configuration.GetSpan()); - } - configurationSeen = true; break; case Heif4CharCode.Ccst: @@ -1232,7 +1198,7 @@ internal sealed class HeifSequenceParser /// /// The stream positioned at the auxiliary-type payload. /// The validated auxiliary-type payload length. - /// when the payload contains either registered HEIF alpha URN. + /// when the payload contains the registered HEIF alpha URN. private bool ParseAuxiliaryType(Stream stream, long boxLength) { if (boxLength < 5 || boxLength > int.MaxValue) @@ -1250,8 +1216,7 @@ internal sealed class HeifSequenceParser } type = type[..^1]; - return type.SequenceEqual("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"u8) - || type.SequenceEqual("urn:mpeg:hevc:2015:auxid:1"u8); + return type.SequenceEqual("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"u8); } /// @@ -1990,114 +1955,6 @@ internal sealed class HeifSequenceParser return (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(prefix[4..]); } - /// - /// Parses HEVC decode-to-composition offsets and marks non-output reference samples. - /// - /// The stream positioned at the composition-offset payload. - /// The validated composition-offset payload length. - /// The selected HEVC track receiving retained composition offsets. - /// The parser-owned reusable scratch span. - /// The complete visible-offset range and hidden-sample state. - private static CompositionSummary ParseCompositionOffsets(Stream stream, long boxLength, HeifSequenceTrack track, Span scratch) - { - ReadOnlySpan prefix = ReadPrefix(stream, boxLength, scratch, 8, "composition offsets"); - byte version = prefix[0]; - if (version is not 0 and not 1 || ReadFlags(prefix) != 0) - { - throw new InvalidImageContentException("The composition-offset box has an unsupported version or flags."); - } - - uint entryCount = BinaryPrimitives.ReadUInt32BigEndian(prefix[4..]); - long entryBytes = checked((long)entryCount * 8); - if (entryCount == 0 || boxLength != 8 + entryBytes) - { - throw new InvalidImageContentException("The composition-offset table is empty or has an invalid length."); - } - - HeifBoxPayloadReader reader = new(stream, entryBytes, scratch, "composition offsets"); - ulong describedSamples = 0; - int retainedOffset = 0; - long leastOffset = long.MaxValue; - long greatestOffset = long.MinValue; - bool hasHiddenSamples = false; - for (uint entry = 0; entry < entryCount; entry++) - { - uint sampleCount = reader.ReadUInt32(); - uint rawOffset = reader.ReadUInt32(); - if (sampleCount == 0) - { - throw new InvalidImageContentException("The composition-offset table contains a zero-length run."); - } - - bool hidden = version == 1 && rawOffset == 0x80000000; - long compositionOffset = version == 0 ? rawOffset : unchecked((int)rawOffset); - describedSamples = checked(describedSamples + sampleCount); - hasHiddenSamples |= hidden; - if (!hidden) - { - leastOffset = Math.Min(leastOffset, compositionOffset); - greatestOffset = Math.Max(greatestOffset, compositionOffset); - } - - int retainedRun = Math.Min((int)Math.Min(sampleCount, int.MaxValue), track.Samples.Length - retainedOffset); - Span samples = track.Samples; - for (int i = 0; i < retainedRun; i++) - { - samples[retainedOffset + i].CompositionOffset = compositionOffset; - samples[retainedOffset + i].IsHidden = hidden; - } - - retainedOffset += retainedRun; - } - - if (describedSamples != track.TotalSampleCount || leastOffset == long.MaxValue) - { - throw new InvalidImageContentException("The composition-offset table does not describe every sample or contains no output sample."); - } - - return new CompositionSummary(leastOffset, greatestOffset, hasHiddenSamples); - } - - /// - /// Validates the track-wide composition bounds associated with HEVC non-output and reordered samples. - /// - /// The stream positioned at the composition-to-decode payload. - /// The validated composition-to-decode payload length. - /// The offset range derived from the complete composition-offset table. - /// The parser-owned reusable scratch span. - private static void ParseCompositionToDecode(Stream stream, long boxLength, CompositionSummary composition, Span scratch) - { - ReadOnlySpan prefix = ReadPrefix(stream, boxLength, scratch, 4, "composition-to-decode"); - byte version = prefix[0]; - int fieldSize = version switch - { - 0 => 4, - 1 => 8, - _ => throw new InvalidImageContentException($"The composition-to-decode box has unsupported version {version}.") - }; - - int requiredLength = 4 + (fieldSize * 5); - prefix = ReadPrefixFromStart(stream, boxLength, scratch, requiredLength, "composition-to-decode"); - if (boxLength != requiredLength || ReadFlags(prefix) != 0) - { - throw new InvalidImageContentException("The composition-to-decode box has unsupported flags or length."); - } - - long shift = ReadSignedInteger(prefix[4..], fieldSize); - long leastOffset = ReadSignedInteger(prefix[(4 + fieldSize)..], fieldSize); - long greatestOffset = ReadSignedInteger(prefix[(4 + (fieldSize * 2))..], fieldSize); - long compositionStart = ReadSignedInteger(prefix[(4 + (fieldSize * 3))..], fieldSize); - long compositionEnd = ReadSignedInteger(prefix[(4 + (fieldSize * 4))..], fieldSize); - long requiredShift = composition.LeastOffset < 0 ? checked(-composition.LeastOffset) : 0; - if (shift < requiredShift - || leastOffset != composition.LeastOffset - || greatestOffset != composition.GreatestOffset - || (compositionEnd != 0 && compositionEnd < compositionStart)) - { - throw new InvalidImageContentException("The composition-to-decode box does not match the track's composition offsets."); - } - } - /// /// Computes retained sample composition times while preserving decode-order storage. /// @@ -2114,15 +1971,6 @@ internal sealed class HeifSequenceParser } } - /// - /// Reads one signed composition field of the version-selected fixed width. - /// - /// The field bytes. - /// The four-byte or eight-byte field width. - /// The signed field value. - private static long ReadSignedInteger(ReadOnlySpan data, int fieldSize) - => fieldSize == 4 ? BinaryPrimitives.ReadInt32BigEndian(data) : BinaryPrimitives.ReadInt64BigEndian(data); - /// /// Parses the single normal-rate edit list used to signal image-sequence repetition. /// @@ -2590,38 +2438,4 @@ internal sealed class HeifSequenceParser /// A value indicating the relative sort order. public int CompareTo(SampleIdIndexEntry other) => this.Id.CompareTo(other.Id); } - - /// - /// Contains the visible composition-offset range derived from a complete HEVC track. - /// - private readonly struct CompositionSummary - { - /// - /// Initializes a new instance of the struct. - /// - /// The smallest visible composition offset. - /// The greatest visible composition offset. - /// Whether the track contains non-output samples. - public CompositionSummary(long leastOffset, long greatestOffset, bool hasHiddenSamples) - { - this.LeastOffset = leastOffset; - this.GreatestOffset = greatestOffset; - this.HasHiddenSamples = hasHiddenSamples; - } - - /// - /// Gets the smallest visible composition offset. - /// - public long LeastOffset { get; } - - /// - /// Gets the greatest visible composition offset. - /// - public long GreatestOffset { get; } - - /// - /// Gets a value indicating whether the track contains non-output samples. - /// - public bool HasHiddenSamples { get; } - } } diff --git a/src/ImageSharp/Formats/Heif/HeifSequenceTrack.cs b/src/ImageSharp/Formats/Heif/HeifSequenceTrack.cs index 311139f8a..63f2ee7dd 100644 --- a/src/ImageSharp/Formats/Heif/HeifSequenceTrack.cs +++ b/src/ImageSharp/Formats/Heif/HeifSequenceTrack.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Heif.Av1; -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.Metadata.Profiles.Cicp; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -70,11 +69,6 @@ internal sealed class HeifSequenceTrack /// public Av1CodecConfiguration? Av1CodecConfiguration { get; set; } - /// - /// Gets or sets the parsed HEVC configuration when is . - /// - public HevcCodecConfiguration? HevcCodecConfiguration { get; set; } - /// /// Gets or sets the ICC profile associated with the image sequence. /// diff --git a/src/ImageSharp/Formats/Heif/Hevc/Color/HevcPlanarSampleBuffer.cs b/src/ImageSharp/Formats/Heif/Hevc/Color/HevcPlanarSampleBuffer.cs deleted file mode 100644 index 3d36e6491..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/Color/HevcPlanarSampleBuffer.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Components; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color; - -/// -/// Adapts reconstructed HEVC planes to the shared HEIF planar color pipeline. -/// -internal struct HevcPlanarSampleBuffer : IHeifPlanarSampleBuffer -{ - /// - /// The reconstructed HEVC picture containing the component planes. - /// - private readonly HevcPictureBuffer picture; - - /// - /// The progressive-frame 4:2:0 chroma sample location. - /// - private readonly HevcChromaSampleLocation chromaSampleLocation; - - /// - /// Initializes a new instance of the struct. - /// - /// The reconstructed HEVC picture. - /// The progressive-frame 4:2:0 chroma sample location. - public HevcPlanarSampleBuffer(HevcPictureBuffer picture, HevcChromaSampleLocation chromaSampleLocation) - { - this.picture = picture; - this.chromaSampleLocation = chromaSampleLocation; - } - - /// - /// Gets the horizontal offset in half-luma-sample units for each HEVC 4:2:0 chroma-location code. - /// - private static ReadOnlySpan ChromaLocationX => [0, 1, 0, 1, 0, 1]; - - /// - /// Gets the vertical offset in half-luma-sample units for each HEVC 4:2:0 chroma-location code. - /// - private static ReadOnlySpan ChromaLocationY => [1, 1, 0, 0, 2, 2]; - - /// - public readonly int Width => this.picture.Width; - - /// - public readonly int Height => this.picture.Height; - - /// - public readonly int LumaBitDepth => this.picture.BitDepthLuma; - - /// - public readonly int ChromaBitDepth => this.picture.BitDepthChroma; - - /// - public readonly bool IsMonochrome => this.picture.ChromaFormat == 0; - - /// - public readonly int ChromaSubsamplingX => this.picture.GetSubsamplingX(HevcPlane.Cb); - - /// - public readonly int ChromaSubsamplingY => this.picture.GetSubsamplingY(HevcPlane.Cb); - - /// - public readonly int ChromaPositionX - => this.picture.ChromaFormat == 1 && !this.picture.SeparateColorPlane ? ChromaLocationX[(int)this.chromaSampleLocation] : 0; - - /// - public readonly int ChromaPositionY - => this.picture.ChromaFormat == 1 && !this.picture.SeparateColorPlane ? ChromaLocationY[(int)this.chromaSampleLocation] : 0; - - /// - public Span GetLumaRowSpan(int row) => this.picture.GetRowSpan(HevcPlane.Y, row); - - /// - public Span GetChromaBlueRowSpan(int row) => this.picture.GetRowSpan(HevcPlane.Cb, row); - - /// - public Span GetChromaRedRowSpan(int row) => this.picture.GetRowSpan(HevcPlane.Cr, row); -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs b/src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs deleted file mode 100644 index 87923cb5c..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/Color/HevcYuvConverter.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Components; -using SixLabors.ImageSharp.Formats.Heif.Components.Alpha; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color; - -/// -/// Adapts HEVC color signaling and reconstructed planes to the shared HEIF color pipeline. -/// -internal static class HevcYuvConverter -{ - /// - /// Converts reconstructed HEVC component planes to packed pixels. - /// - /// The destination pixel type. - /// The configuration used for allocation and pixel conversion. - /// The reconstructed HEVC picture. - /// The destination image frame. - /// The effective H.273 color description. - /// The progressive-frame 4:2:0 chroma sample location. - /// The horizontal luma-sample offset of the first converted pixel. - /// The vertical luma-sample offset of the first converted pixel. - public static void ConvertToRgb( - Configuration configuration, - HevcPictureBuffer picture, - ImageFrame image, - CicpProfile colorProfile, - HevcChromaSampleLocation chromaSampleLocation, - int sourceX = 0, - int sourceY = 0) - where TPixel : unmanaged, IPixel - { - HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out HeifColorConversionMode mode); - 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( - configuration, - buffer, - image, - in parameters, - mode, - sourceX, - sourceY); - } - - /// - /// Composes a visible HEVC luma rectangle into a packed color frame as auxiliary alpha. - /// - /// The destination color pixel type. - /// The configuration used for allocation and pixel conversion. - /// The reconstructed HEVC picture containing the alpha luma plane. - /// The packed color frame receiving alpha values. - /// The effective H.273 color description defining the luma range. - /// The progressive-frame 4:2:0 chroma sample location. - /// The visible luma rectangle within the coded picture. - /// The complete presented size of the auxiliary image or grid tile. - /// The destination region receiving the top-left portion of the presented alpha image. - /// Whether stored color samples must be converted to unassociated alpha. - public static void ComposeAlpha( - Configuration configuration, - HevcPictureBuffer picture, - ImageFrame destination, - CicpProfile colorProfile, - HevcChromaSampleLocation chromaSampleLocation, - Rectangle sourceRectangle, - Size outputSize, - Rectangle destinationRectangle, - bool premultiplied) - where TPixel : unmanaged, IPixel - { - HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out _); - HevcPlanarSampleBuffer buffer = new(picture, chromaSampleLocation); - HeifPlanarAlphaCompositor.Compose( - configuration, - buffer, - destination, - in parameters, - sourceRectangle, - outputSize, - destinationRectangle, - premultiplied); - } - - /// - /// Converts packed pixels to the configured HEVC component planes. - /// - /// The source pixel type. - /// The configuration used for allocation and pixel conversion. - /// The source image frame. - /// The destination HEVC picture. - /// The H.273 color description to encode. - /// The progressive-frame 4:2:0 chroma sample location. - public static void ConvertFromRgb( - Configuration configuration, - ImageFrame image, - HevcPictureBuffer picture, - CicpProfile colorProfile, - HevcChromaSampleLocation chromaSampleLocation) - where TPixel : unmanaged, IPixel - { - HeifColorConversionParameters parameters = GetConversionParameters(picture, colorProfile, out HeifColorConversionMode mode); - HevcPlanarSampleBuffer buffer = new(picture, chromaSampleLocation); - HeifPlanarColorConverter.ConvertFromRgb( - configuration, - image, - buffer, - in parameters, - mode); - } - - /// - /// Resolves the shared H.273 conversion parameters for an HEVC picture. - /// - /// The picture defining component precision and sampling. - /// The effective H.273 color description. - /// The resolved color conversion operation. - /// The immutable scalar and SIMD conversion parameters. - private static HeifColorConversionParameters GetConversionParameters( - HevcPictureBuffer picture, - CicpProfile colorProfile, - out HeifColorConversionMode mode) - => HeifColorConversionParameters.Create( - colorProfile.ColorPrimaries, - colorProfile.TransferCharacteristics, - colorProfile.MatrixCoefficients, - colorProfile.FullRange, - picture.BitDepthLuma, - picture.ChromaFormat == 0 ? picture.BitDepthLuma : picture.BitDepthChroma, - picture.ChromaFormat == 0, - picture.ChromaFormat == 3, - out mode); -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs deleted file mode 100644 index cd2a587e2..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Reads fixed-width and Exp-Golomb HEVC syntax from a most-significant-bit-first byte span. -/// -internal ref struct HevcBitReader -{ - /// - /// The complete raw byte sequence buffer. - /// - private readonly ReadOnlySpan data; - - /// - /// The zero-based position of the next bit to read. - /// - private int bitPosition; - - /// - /// Initializes a new instance of the struct. - /// - /// The bounded HEVC syntax bytes. - public HevcBitReader(ReadOnlySpan data) - { - this.data = data; - this.bitPosition = 0; - } - - /// - /// Gets the zero-based position of the next bit to read. - /// - public readonly int BitPosition => this.bitPosition; - - /// - /// Gets the number of unread bits in the bounded byte span. - /// - public readonly int BitsRemaining => (this.data.Length * 8) - this.bitPosition; - - /// - /// Gets a value indicating whether the next bit begins a byte. - /// - public readonly bool IsByteAligned => (this.bitPosition & 7) == 0; - - /// - /// Reads an unsigned fixed-width value in most-significant-bit-first order. - /// - /// The number of bits to read. - /// The decoded unsigned value. - /// - /// The requested value extends beyond the bounded HEVC syntax. - /// - public uint ReadBits(int bitCount) - { - DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount)); - if (bitCount > this.BitsRemaining) - { - throw new InvalidImageContentException("The HEVC bitstream is truncated."); - } - - uint value = 0; - int remaining = bitCount; - while (remaining > 0) - { - // HEVC fixed-width syntax is MSB-first. Reading only the available portion of each byte keeps the - // same operation valid for both aligned parameter fields and fields that straddle byte boundaries. - int byteOffset = this.bitPosition >> 3; - int bitOffset = this.bitPosition & 7; - int bitsFromByte = Math.Min(remaining, 8 - bitOffset); - int shift = 8 - bitOffset - bitsFromByte; - uint mask = (1U << bitsFromByte) - 1; - - value = (value << bitsFromByte) | ((uint)(this.data[byteOffset] >> shift) & mask); - this.bitPosition += bitsFromByte; - remaining -= bitsFromByte; - } - - return value; - } - - /// - /// Reads a one-bit HEVC flag. - /// - /// when the coded flag is one; otherwise, . - /// The flag extends beyond the bounded HEVC syntax. - public bool ReadFlag() => this.ReadBits(1) != 0; - - /// - /// Determines whether unread syntax remains before the raw byte sequence payload trailing bits. - /// - /// - /// when the unread bits contain syntax before the stop bit; otherwise, - /// . - /// - public bool HasMoreRbspData() - { - int bitsRemaining = this.BitsRemaining; - if (bitsRemaining == 0) - { - return false; - } - - if (bitsRemaining > 8) - { - return true; - } - - int savedBitPosition = this.bitPosition; - uint remainingValue = this.ReadBits(bitsRemaining); - this.bitPosition = savedBitPosition; - - // At most one partial byte can contain only rbsp_stop_one_bit followed by alignment zeros. - return remainingValue != 1U << (bitsRemaining - 1); - } - - /// - /// Reads an unsigned exponential-Golomb value. - /// - /// The decoded unsigned value. - /// - /// The code is truncated or exceeds the range of a 32-bit unsigned integer. - /// - public uint ReadUnsignedExpGolomb() - { - int leadingZeroBits = 0; - while (!this.ReadFlag()) - { - leadingZeroBits++; - if (leadingZeroBits > 32) - { - throw new InvalidImageContentException("The HEVC unsigned Exp-Golomb value exceeds 32 bits."); - } - } - - // In ue(v), the zero-prefix length selects an all-one basis and the equally wide suffix selects the - // offset from that basis. Keeping those parts separate makes the 32-bit overflow boundary explicit. - uint suffix = this.ReadBits(leadingZeroBits); - if (leadingZeroBits == 32) - { - // Only an all-zero suffix fits after the 32-bit all-one basis. - if (suffix != 0) - { - throw new InvalidImageContentException("The HEVC unsigned Exp-Golomb value exceeds 32 bits."); - } - - return uint.MaxValue; - } - - return ((1U << leadingZeroBits) - 1) + suffix; - } - - /// - /// Reads a signed exponential-Golomb value. - /// - /// The decoded signed value. - /// - /// The code is truncated or exceeds the range of a 32-bit signed integer. - /// - public int ReadSignedExpGolomb() - { - uint codeNumber = this.ReadUnsignedExpGolomb(); - - // HEVC's se(v) mapping alternates positive and negative magnitudes: 0, 1, -1, 2, -2, and so on. - if ((codeNumber & 1) == 0) - { - return -(int)(codeNumber >> 1); - } - - ulong magnitude = ((ulong)codeNumber + 1) >> 1; - if (magnitude > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC signed Exp-Golomb value exceeds 32 bits."); - } - - return (int)magnitude; - } - - /// - /// Reads the one-bit marker and zero padding that align slice data to the next byte boundary. - /// - /// - /// The alignment marker is zero or any following alignment bit is nonzero. - /// - public void ReadByteAlignment() - { - if (!this.ReadFlag()) - { - throw new InvalidImageContentException("The HEVC slice-header alignment marker is not set."); - } - - while (!this.IsByteAligned) - { - if (this.ReadFlag()) - { - throw new InvalidImageContentException("The HEVC slice header has a nonzero alignment bit."); - } - } - } - - /// - /// Reads and validates the stop bit and zero alignment bits that terminate an HEVC raw byte sequence payload. - /// - /// - /// The trailing-bit pattern is truncated, malformed, or followed by additional data. - /// - public void ReadRbspTrailingBits() - { - // An RBSP ends with one stop bit followed only by zero bits up to the next byte boundary. - if (!this.ReadFlag()) - { - throw new InvalidImageContentException("The HEVC RBSP stop bit is not set."); - } - - while (!this.IsByteAligned) - { - if (this.ReadFlag()) - { - throw new InvalidImageContentException("The HEVC RBSP has a nonzero alignment bit."); - } - } - - // Each reader is bounded to one RBSP, so reaching alignment before the buffer end means the caller left - // syntax unread or the NAL unit contains bytes beyond its normative terminator. - if (this.BitsRemaining != 0) - { - throw new InvalidImageContentException("The HEVC RBSP contains unexpected trailing data."); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContext.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContext.cs deleted file mode 100644 index 728297f9c..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContext.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Maintains the adaptive probability state for one HEVC context-coded binary syntax element. -/// -internal struct HevcCabacContext -{ - /// - /// The packed probability-state index and most-probable-symbol value. - /// - private byte state; - - /// - /// Initializes a new instance of the struct. - /// - /// The luma quantization parameter that selects the initial probability. - /// The syntax-element initialization value. - public HevcCabacContext(int quantizationParameter, byte initializationValue) - { - int clippedQuantizationParameter = Math.Clamp(quantizationParameter, 0, 51); - int slope = ((initializationValue >> 4) * 5) - 45; - int offset = ((initializationValue & 15) << 3) - 16; - int initializationState = Math.Clamp( - ((slope * clippedQuantizationParameter) >> 4) + offset, - 1, - 126); - - bool mostProbableSymbol = initializationState >= 64; - this.state = (byte)( - ((mostProbableSymbol ? initializationState - 64 : 63 - initializationState) << 1) - + (mostProbableSymbol ? 1 : 0)); - } - - /// - /// Gets a mapping from each packed context state to the state that follows its most-probable symbol. - /// - // ReadOnlySpan allows the compiler to embed both normative tables in static data instead of allocating - // mutable arrays when this type is initialized. - private static ReadOnlySpan MostProbableStateTransitions => - [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 124, 125, 126, 127 - ]; - - /// - /// Gets a mapping from each packed context state to the state that follows its least-probable symbol. - /// - private static ReadOnlySpan LeastProbableStateTransitions => - [ - 1, 0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 18, 19, 22, 23, 22, 23, 24, 25, - 26, 27, 26, 27, 30, 31, 30, 31, 32, 33, 32, 33, 36, 37, 36, 37, - 38, 39, 38, 39, 42, 43, 42, 43, 44, 45, 44, 45, 46, 47, 48, 49, - 48, 49, 50, 51, 52, 53, 52, 53, 54, 55, 54, 55, 56, 57, 58, 59, - 58, 59, 60, 61, 60, 61, 60, 61, 62, 63, 64, 65, 64, 65, 66, 67, - 66, 67, 66, 67, 68, 69, 68, 69, 70, 71, 70, 71, 70, 71, 72, 73, - 72, 73, 72, 73, 74, 75, 74, 75, 74, 75, 76, 77, 76, 77, 126, 127 - ]; - - /// - /// Gets the probability-state index used to select the least-probable-symbol range. - /// - public readonly int StateIndex => this.state >> 1; - - /// - /// Gets a value indicating whether one is the current most-probable symbol. - /// - public readonly bool MostProbableSymbol => (this.state & 1) != 0; - - /// - /// Advances the context after decoding its most-probable symbol. - /// - public void UpdateMostProbableSymbol() => this.state = MostProbableStateTransitions[this.state]; - - /// - /// Advances the context after decoding its least-probable symbol. - /// - public void UpdateLeastProbableSymbol() => this.state = LeastProbableStateTransitions[this.state]; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs deleted file mode 100644 index c4b747de7..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Owns the adaptive CABAC probability contexts used to decode one intra-coded HEVC entropy substream. -/// -internal sealed class HevcCabacContexts -{ - /// - /// The first transquant-bypass context. - /// - private const int TransquantBypassOffset = 0; - - /// - /// The first coding-unit split context. - /// - private const int SplitOffset = 1; - - /// - /// The intra partition-size context. - /// - private const int PartitionSizeOffset = 4; - - /// - /// The luma intra-prediction context. - /// - private const int IntraPredictionOffset = 5; - - /// - /// The first chroma intra-prediction context. - /// - private const int ChromaPredictionOffset = 6; - - /// - /// The first luma quantization-delta context. - /// - private const int DeltaQuantizationOffset = 8; - - /// - /// The chroma quantization-adjustment flag context. - /// - private const int ChromaQuantizationAdjustmentFlagOffset = 11; - - /// - /// The chroma quantization-adjustment index context. - /// - private const int ChromaQuantizationAdjustmentIndexOffset = 12; - - /// - /// The first transform-tree coded-block-flag context. - /// - private const int TransformCodedBlockFlagOffset = 13; - - /// - /// The first horizontal last-significant-coefficient context. - /// - private const int LastSignificantXOffset = 23; - - /// - /// The first vertical last-significant-coefficient context. - /// - private const int LastSignificantYOffset = 53; - - /// - /// The first significant-coefficient-group context. - /// - private const int SignificantCoefficientGroupOffset = 83; - - /// - /// The first significant-coefficient context. - /// - private const int SignificantCoefficientOffset = 87; - - /// - /// The first greater-than-one coefficient-level context. - /// - private const int GreaterThanOneOffset = 131; - - /// - /// The first greater-than-two coefficient-level context. - /// - private const int GreaterThanTwoOffset = 155; - - /// - /// The sample-adaptive-offset merge context. - /// - private const int SampleAdaptiveOffsetMergeOffset = 161; - - /// - /// The sample-adaptive-offset type context. - /// - private const int SampleAdaptiveOffsetTypeOffset = 162; - - /// - /// The first transform-tree subdivision context. - /// - private const int TransformSubdivisionOffset = 163; - - /// - /// The first transform-skip context. - /// - private const int TransformSkipOffset = 166; - - /// - /// The first cross-component prediction context. - /// - private const int CrossComponentPredictionOffset = 168; - - /// - /// The number of contexts used by the independently coded intra-picture syntax. - /// - public const int ContextCount = 178; - - /// - /// The contiguous adaptive context storage owned by the entropy substream. - /// - private readonly HevcCabacContext[] contexts; - - /// - /// Initializes a new instance of the class for an intra-coded slice. - /// - /// The slice luma quantization parameter. - public HevcCabacContexts(int quantizationParameter) - { - this.contexts = new HevcCabacContext[ContextCount]; - for (int index = 0; index < this.contexts.Length; index++) - { - this.contexts[index] = new HevcCabacContext(quantizationParameter, IntraInitializationValues[index]); - } - } - - /// - /// Gets the HEVC intra-slice initialization values in the same order as the owned context ranges. - /// - private static ReadOnlySpan IntraInitializationValues => - [ - - // cu_transquant_bypass_flag - 154, - - // split_cu_flag - 139, 141, 157, - - // part_mode and prev_intra_luma_pred_flag - 184, - 184, - - // intra_chroma_pred_mode - 63, 139, - - // cu_qp_delta_abs, cu_chroma_qp_offset_flag, and cu_chroma_qp_offset_idx - 154, 154, 154, - 154, - 154, - - // cbf_luma followed by the chroma coded-block flags - 111, 141, 154, 154, 154, - 94, 138, 182, 154, 154, - - // last_sig_coeff_x_prefix: luma followed by chroma - 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, - 108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - - // last_sig_coeff_y_prefix: luma followed by chroma - 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, - 108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - - // coded_sub_block_flag: luma followed by chroma - 91, 171, 134, 141, - - // sig_coeff_flag: luma followed by chroma - 111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153, - 125, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 141, - 140, 139, 182, 182, 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 111, - - // coeff_abs_level_greater1_flag: luma followed by chroma - 140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152, - 140, 179, 166, 182, 140, 227, 122, 197, - - // coeff_abs_level_greater2_flag: luma followed by chroma - 138, 153, 136, 167, 152, 152, - - // sao_merge_flag and sao_type_idx - 153, - 200, - - // split_transform_flag - 153, 138, 138, - - // transform_skip_flag: luma followed by chroma - 139, 139, - - // cross_comp_pred: five sign/magnitude contexts for Cb followed by five for Cr - 154, 154, 154, 154, 154, 154, 154, 154, 154, 154 - ]; - - /// - /// Gets the coding-unit transquant-bypass context. - /// - public Span TransquantBypass => this.contexts.AsSpan(TransquantBypassOffset, 1); - - /// - /// Gets the coding-unit split contexts, ordered by neighboring split depth. - /// - public Span Split => this.contexts.AsSpan(SplitOffset, 3); - - /// - /// Gets the intra partition-size context. - /// - public Span PartitionSize => this.contexts.AsSpan(PartitionSizeOffset, 1); - - /// - /// Gets the luma intra-prediction context. - /// - public Span IntraPrediction => this.contexts.AsSpan(IntraPredictionOffset, 1); - - /// - /// Gets the chroma intra-prediction contexts. - /// - public Span ChromaPrediction => this.contexts.AsSpan(ChromaPredictionOffset, 2); - - /// - /// Gets the luma quantization-delta contexts. - /// - public Span DeltaQuantization => this.contexts.AsSpan(DeltaQuantizationOffset, 3); - - /// - /// Gets the chroma quantization-adjustment flag context. - /// - public Span ChromaQuantizationAdjustmentFlag => - this.contexts.AsSpan(ChromaQuantizationAdjustmentFlagOffset, 1); - - /// - /// Gets the chroma quantization-adjustment index context. - /// - public Span ChromaQuantizationAdjustmentIndex => - this.contexts.AsSpan(ChromaQuantizationAdjustmentIndexOffset, 1); - - /// - /// Gets the transform-tree coded-block-flag contexts, with luma preceding chroma. - /// - public Span TransformCodedBlockFlag => - this.contexts.AsSpan(TransformCodedBlockFlagOffset, 10); - - /// - /// Gets the horizontal last-significant-coefficient contexts, with luma preceding chroma. - /// - public Span LastSignificantX => this.contexts.AsSpan(LastSignificantXOffset, 30); - - /// - /// Gets the vertical last-significant-coefficient contexts, with luma preceding chroma. - /// - public Span LastSignificantY => this.contexts.AsSpan(LastSignificantYOffset, 30); - - /// - /// Gets the significant-coefficient-group contexts, with luma preceding chroma. - /// - public Span SignificantCoefficientGroup => - this.contexts.AsSpan(SignificantCoefficientGroupOffset, 4); - - /// - /// Gets the significant-coefficient contexts, with luma preceding chroma. - /// - public Span SignificantCoefficient => - this.contexts.AsSpan(SignificantCoefficientOffset, 44); - - /// - /// Gets the greater-than-one coefficient-level contexts, with luma preceding chroma. - /// - public Span GreaterThanOne => this.contexts.AsSpan(GreaterThanOneOffset, 24); - - /// - /// Gets the greater-than-two coefficient-level contexts, with luma preceding chroma. - /// - public Span GreaterThanTwo => this.contexts.AsSpan(GreaterThanTwoOffset, 6); - - /// - /// Gets the sample-adaptive-offset merge context. - /// - public Span SampleAdaptiveOffsetMerge => - this.contexts.AsSpan(SampleAdaptiveOffsetMergeOffset, 1); - - /// - /// Gets the sample-adaptive-offset type context. - /// - public Span SampleAdaptiveOffsetType => - this.contexts.AsSpan(SampleAdaptiveOffsetTypeOffset, 1); - - /// - /// Gets the transform-tree subdivision contexts. - /// - public Span TransformSubdivision => - this.contexts.AsSpan(TransformSubdivisionOffset, 3); - - /// - /// Gets the transform-skip contexts, with luma preceding chroma. - /// - public Span TransformSkip => this.contexts.AsSpan(TransformSkipOffset, 2); - - /// - /// Gets the cross-component prediction contexts, with Cb preceding Cr. - /// - public Span CrossComponentPrediction => - this.contexts.AsSpan(CrossComponentPredictionOffset, 10); - - /// - /// Copies every adaptive probability context to caller-owned wavefront state. - /// - /// The destination containing at least elements. - public void CopyTo(Span destination) => this.contexts.CopyTo(destination); - - /// - /// Restores every adaptive probability context from caller-owned wavefront state. - /// - /// The source containing at least elements. - public void CopyFrom(ReadOnlySpan source) => source[..ContextCount].CopyTo(this.contexts); -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacDecoder.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCabacDecoder.cs deleted file mode 100644 index f59244f15..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacDecoder.cs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Decodes context-adaptive and bypass-coded binary values from one bounded HEVC entropy substream. -/// -internal ref struct HevcCabacDecoder -{ - /// - /// The complete bounded entropy-substream bytes. - /// - private readonly ReadOnlySpan data; - - /// - /// The zero-based offset of the next byte that can refill the arithmetic value register. - /// - private int byteOffset; - - /// - /// The current arithmetic interval width. - /// - private uint range; - - /// - /// The current arithmetic code value, scaled by seven fractional bits. - /// - private uint value; - - /// - /// The number of normalization shifts remaining before the value register requires another byte. - /// - private int bitsNeeded; - - /// - /// The raw-bit position used while a pulse-code-modulated coding unit suspends arithmetic decoding. - /// - private int pcmBitOffset; - - /// - /// Initializes a new instance of the struct. - /// - /// The bytes of one independently bounded HEVC entropy substream. - /// The entropy substream is shorter than its initial value register. - public HevcCabacDecoder(ReadOnlySpan data) - { - if (data.Length < 2) - { - throw new InvalidImageContentException("The HEVC CABAC substream is truncated."); - } - - this.data = data; - this.byteOffset = 2; - this.range = 510; - this.value = ((uint)data[0] << 8) | data[1]; - this.bitsNeeded = -8; - this.pcmBitOffset = 0; - } - - /// - /// Gets the least-probable-symbol subrange for each probability state and current range class. - /// - private static ReadOnlySpan LeastProbableSymbolRanges => - [ - 128, 176, 208, 240, 128, 167, 197, 227, 128, 158, 187, 216, 123, 150, 178, 205, - 116, 142, 169, 195, 111, 135, 160, 185, 105, 128, 152, 175, 100, 122, 144, 166, - 95, 116, 137, 158, 90, 110, 130, 150, 85, 104, 123, 142, 81, 99, 117, 135, - 77, 94, 111, 128, 73, 89, 105, 122, 69, 85, 100, 116, 66, 80, 95, 110, - 62, 76, 90, 104, 59, 72, 86, 99, 56, 69, 81, 94, 53, 65, 77, 89, - 51, 62, 73, 85, 48, 59, 69, 80, 46, 56, 66, 76, 43, 53, 63, 72, - 41, 50, 59, 69, 39, 48, 56, 65, 37, 45, 54, 62, 35, 43, 51, 59, - 33, 41, 48, 56, 32, 39, 46, 53, 30, 37, 43, 50, 29, 35, 41, 48, - 27, 33, 39, 45, 26, 31, 37, 43, 24, 30, 35, 41, 23, 28, 33, 39, - 22, 27, 32, 37, 21, 26, 30, 35, 20, 24, 29, 33, 19, 23, 27, 31, - 18, 22, 26, 30, 17, 21, 25, 28, 16, 20, 23, 27, 15, 19, 22, 25, - 14, 18, 21, 24, 14, 17, 20, 23, 13, 16, 19, 22, 12, 15, 18, 21, - 12, 14, 17, 20, 11, 14, 16, 19, 11, 13, 15, 18, 10, 12, 15, 17, - 10, 12, 14, 16, 9, 11, 13, 15, 9, 11, 12, 14, 8, 10, 12, 14, - 8, 9, 11, 13, 7, 9, 11, 12, 7, 9, 10, 12, 7, 8, 10, 11, - 6, 8, 9, 11, 6, 7, 9, 10, 6, 7, 8, 9, 2, 2, 2, 2, - ]; - - /// - /// Gets the normalization shift for each quantized least-probable-symbol range. - /// - private static ReadOnlySpan LeastProbableSymbolNormalizationShifts => - [ - 6, 5, 4, 4, - 3, 3, 3, 3, - 2, 2, 2, 2, - 2, 2, 2, 2, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - ]; - - /// - /// Gets the number of whole entropy-substream bytes loaded into the arithmetic decoder. - /// - public readonly int BytesConsumed => this.byteOffset; - - /// - /// Decodes one context-adaptive binary value and advances its probability state. - /// - /// The adaptive probability context selected for the syntax element. - /// The decoded binary value. - /// The entropy substream ends while normalizing the decoded value. - public bool ReadDecision(ref HevcCabacContext context) - { - int rangeClass = ((int)this.range >> 6) - 4; - uint leastProbableSymbolRange = LeastProbableSymbolRanges[(context.StateIndex * 4) + rangeClass]; - this.range -= leastProbableSymbolRange; - uint scaledRange = this.range << 7; - - if (this.value < scaledRange) - { - bool symbol = context.MostProbableSymbol; - context.UpdateMostProbableSymbol(); - - if (scaledRange < (256U << 7)) - { - // Renormalization shifts both registers together so their comparison continues to describe the - // same arithmetic interval; a byte is loaded only when the buffered fractional bits are exhausted. - this.range = scaledRange >> 6; - this.value <<= 1; - if (++this.bitsNeeded == 0) - { - this.bitsNeeded = -8; - this.value += this.ReadByte(); - } - } - - return symbol; - } - - bool leastProbableSymbol = !context.MostProbableSymbol; - int normalizationShift = LeastProbableSymbolNormalizationShifts[(int)(leastProbableSymbolRange >> 3)]; - this.value = (this.value - scaledRange) << normalizationShift; - this.range = leastProbableSymbolRange << normalizationShift; - context.UpdateLeastProbableSymbol(); - this.bitsNeeded += normalizationShift; - if (this.bitsNeeded >= 0) - { - this.value += (uint)this.ReadByte() << this.bitsNeeded; - this.bitsNeeded -= 8; - } - - return leastProbableSymbol; - } - - /// - /// Decodes one equal-probability binary value without changing an adaptive context. - /// - /// The decoded binary value. - /// The entropy substream ends while loading the decoded value. - public bool ReadBypass() - { - if (this.range == 256) - { - return this.ReadAlignedBypassBits(1) != 0; - } - - this.value <<= 1; - if (++this.bitsNeeded >= 0) - { - this.bitsNeeded = -8; - this.value += this.ReadByte(); - } - - uint scaledRange = this.range << 7; - if (this.value < scaledRange) - { - return false; - } - - this.value -= scaledRange; - return true; - } - - /// - /// Decodes a most-significant-bit-first sequence of equal-probability binary values. - /// - /// The number of values to decode. - /// The decoded unsigned value. - /// The entropy substream ends while loading the decoded value. - public uint ReadBypassBits(int bitCount) - { - DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount)); - if (this.range == 256) - { - return this.ReadAlignedBypassBits(bitCount); - } - - uint bins = 0; - int remaining = bitCount; - while (remaining > 8) - { - this.value = (this.value << 8) + ((uint)this.ReadByte() << (8 + this.bitsNeeded)); - uint scaledRange = this.range << 15; - for (int bitIndex = 0; bitIndex < 8; bitIndex++) - { - bins <<= 1; - scaledRange >>= 1; - if (this.value >= scaledRange) - { - bins++; - this.value -= scaledRange; - } - } - - remaining -= 8; - } - - this.bitsNeeded += remaining; - this.value <<= remaining; - if (this.bitsNeeded >= 0) - { - this.value += (uint)this.ReadByte() << this.bitsNeeded; - this.bitsNeeded -= 8; - } - - uint finalScaledRange = this.range << (remaining + 7); - for (int bitIndex = 0; bitIndex < remaining; bitIndex++) - { - bins <<= 1; - finalScaledRange >>= 1; - if (this.value >= finalScaledRange) - { - bins++; - this.value -= finalScaledRange; - } - } - - return bins; - } - - /// - /// Selects the byte-aligned equal-probability range used by aligned bypass syntax. - /// - public void AlignBypass() => this.range = 256; - - /// - /// Decodes the binary value that terminates a coding-tree block or entropy substream. - /// - /// when the current entropy substream terminates; otherwise, . - /// The entropy substream ends while normalizing a non-terminating value. - public bool ReadTerminate() - { - this.range -= 2; - uint scaledRange = this.range << 7; - if (this.value >= scaledRange) - { - return true; - } - - if (scaledRange < (256U << 7)) - { - this.range = scaledRange >> 6; - this.value <<= 1; - if (++this.bitsNeeded == 0) - { - this.bitsNeeded = -8; - this.value += this.ReadByte(); - } - } - - return false; - } - - /// - /// Decodes the terminating-bin flag that enters pulse-code-modulated sample syntax. - /// - /// when raw PCM samples follow; otherwise, . - public bool ReadPcmFlag() - { - bool pcm = this.ReadTerminate(); - if (pcm) - { - // A successful terminating bin leaves the underlying byte reader at the first byte after the CABAC - // alignment pattern. PCM sample bits start there and temporarily bypass the arithmetic registers. - this.pcmBitOffset = this.byteOffset * 8; - } - - return pcm; - } - - /// - /// Reads one unsigned pulse-code-modulated sample while arithmetic decoding is suspended. - /// - /// The number of most-significant-bit-first sample bits. - /// The decoded sample value. - /// The entropy substream ends within the PCM sample. - public ushort ReadPcmSample(int bitDepth) - { - DebugGuard.MustBeBetweenOrEqualTo(bitDepth, 1, 16, nameof(bitDepth)); - if (this.pcmBitOffset > (this.data.Length * 8) - bitDepth) - { - throw new InvalidImageContentException("The HEVC pulse-code-modulated sample data is truncated."); - } - - uint sample = 0; - int bitsRemaining = bitDepth; - while (bitsRemaining > 0) - { - int byteIndex = this.pcmBitOffset >> 3; - int bitIndex = this.pcmBitOffset & 7; - int bitsFromByte = Math.Min(8 - bitIndex, bitsRemaining); - int shift = 8 - bitIndex - bitsFromByte; - uint mask = (uint)((1 << bitsFromByte) - 1); - sample = (sample << bitsFromByte) | ((uint)(this.data[byteIndex] >> shift) & mask); - this.pcmBitOffset += bitsFromByte; - bitsRemaining -= bitsFromByte; - } - - return (ushort)sample; - } - - /// - /// Restarts arithmetic decoding after a complete byte-aligned PCM coding unit. - /// - /// The following arithmetic substream is truncated. - public void RestartAfterPcm() - { - DebugGuard.IsTrue((this.pcmBitOffset & 7) == 0, "The complete HEVC PCM payload must end on a byte boundary."); - this.byteOffset = this.pcmBitOffset >> 3; - this.range = 510; - this.bitsNeeded = -8; - this.value = ((uint)this.ReadByte() << 8) | this.ReadByte(); - } - - /// - /// Validates the stop bit and zero padding following a terminating entropy-coded value. - /// - /// The entropy substream has an invalid stop or alignment bit. - public readonly void ValidateTerminationAlignment() - { - int alignmentShift = 8 + this.bitsNeeded; - - // CABAC refills whole bytes ahead of consumption. The stop bit therefore remains in the most recently - // loaded byte, and bitsNeeded identifies its exact position without rewinding the arithmetic decoder. - int alignmentPattern = (this.data[this.byteOffset - 1] << alignmentShift) & 0xFF; - if (alignmentPattern != 0x80) - { - throw new InvalidImageContentException("The HEVC CABAC substream has invalid termination alignment."); - } - } - - /// - /// Decodes equal-probability values while the arithmetic range is byte aligned. - /// - /// The number of values to decode. - /// The decoded unsigned value. - /// The entropy substream ends while loading the decoded value. - private uint ReadAlignedBypassBits(int bitCount) - { - uint bins = 0; - int remaining = bitCount; - while (remaining > 0) - { - int binsToRead = Math.Min(remaining, 8); - uint binMask = (1U << binsToRead) - 1; - - // With a range of 256 the high value bit is known to be zero, so the following bits can be copied - // directly while preserving the same register refill schedule as individual bypass decisions. - uint newBins = (this.value >> (15 - binsToRead)) & binMask; - bins = (bins << binsToRead) | newBins; - this.value = (this.value << binsToRead) & 0x7FFF; - remaining -= binsToRead; - this.bitsNeeded += binsToRead; - if (this.bitsNeeded >= 0) - { - this.value |= (uint)this.ReadByte() << this.bitsNeeded; - this.bitsNeeded -= 8; - } - } - - return bins; - } - - /// - /// Loads the next byte into the arithmetic decoder. - /// - /// The next entropy-substream byte. - /// No byte remains in the bounded entropy substream. - private byte ReadByte() - { - if ((uint)this.byteOffset >= (uint)this.data.Length) - { - throw new InvalidImageContentException("The HEVC CABAC substream is truncated."); - } - - return this.data[this.byteOffset++]; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacSyntaxReader.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCabacSyntaxReader.cs deleted file mode 100644 index bf908c4ed..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCabacSyntaxReader.cs +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Decodes the CABAC syntax values used to reconstruct one independently coded HEVC still picture. -/// -internal ref struct HevcCabacSyntaxReader -{ - /// - /// The truncated-unary cutoff for a coding-unit luma quantization delta. - /// - private const int DeltaQuantizationCutoff = 5; - - /// - /// The prefix length at which coefficient levels switch from Rice to exponential-Golomb coding. - /// - private const int CoefficientRemainingReduction = 3; - - /// - /// The binary arithmetic decoder for the current entropy substream. - /// - private HevcCabacDecoder decoder; - - /// - /// The adaptive intra-picture probability contexts for the current entropy substream. - /// - private readonly HevcCabacContexts contexts; - - /// - /// Initializes a new instance of the struct. - /// - /// The bytes of one bounded slice tile or wavefront entropy substream. - /// The slice luma quantization parameter. - /// The entropy substream is truncated. - public HevcCabacSyntaxReader(ReadOnlySpan data, int quantizationParameter) - { - this.decoder = new HevcCabacDecoder(data); - this.contexts = new HevcCabacContexts(quantizationParameter); - } - - /// - /// Gets the number of entropy-substream bytes loaded by the arithmetic decoder. - /// - public readonly int BytesConsumed => this.decoder.BytesConsumed; - - /// - /// Copies the adaptive contexts required to initialize a later wavefront row. - /// - /// The caller-owned context destination. - public readonly void CopyContextsTo(Span destination) => this.contexts.CopyTo(destination); - - /// - /// Restores adaptive contexts captured after the second coding-tree block of the preceding wavefront row. - /// - /// The saved wavefront contexts. - public readonly void CopyContextsFrom(ReadOnlySpan source) => this.contexts.CopyFrom(source); - - /// - /// Decodes the coding-unit transquant-bypass flag. - /// - /// The decoded flag value. - public bool ReadTransquantBypass() - { - Span selectedContexts = this.contexts.TransquantBypass; - return this.decoder.ReadDecision(ref selectedContexts[0]); - } - - /// - /// Decodes a coding-unit split flag. - /// - /// The context derived from the available neighboring coding-unit depths. - /// The decoded flag value. - public bool ReadSplit(int contextIndex) - { - DebugGuard.MustBeBetweenOrEqualTo(contextIndex, 0, 2, nameof(contextIndex)); - Span selectedContexts = this.contexts.Split; - return this.decoder.ReadDecision(ref selectedContexts[contextIndex]); - } - - /// - /// Decodes whether a minimum-size intra coding unit uses four square prediction partitions. - /// - /// - /// A value indicating whether the coding unit is at the minimum coding-block size. - /// - /// - /// for four square prediction partitions; for one square partition. - /// - public bool ReadIntraNxNPartition(bool isMinimumCodingBlockSize) - { - if (!isMinimumCodingBlockSize) - { - return false; - } - - Span selectedContexts = this.contexts.PartitionSize; - return !this.decoder.ReadDecision(ref selectedContexts[0]); - } - - /// - /// Decodes whether a square intra coding unit carries raw pulse-code-modulated samples. - /// - /// when PCM sample syntax follows; otherwise, . - public bool ReadPcmFlag() => this.decoder.ReadPcmFlag(); - - /// - /// Reads one pulse-code-modulated component sample. - /// - /// The PCM sample precision. - /// The decoded unsigned sample. - public ushort ReadPcmSample(int bitDepth) => this.decoder.ReadPcmSample(bitDepth); - - /// - /// Restarts arithmetic decoding after the complete PCM coding-unit payload. - /// - public void RestartAfterPcm() => this.decoder.RestartAfterPcm(); - - /// - /// Decodes whether a luma intra mode is selected from the three most-probable modes. - /// - /// The decoded flag value. - public bool ReadPreviousIntraLumaPredictionFlag() - { - Span selectedContexts = this.contexts.IntraPrediction; - return this.decoder.ReadDecision(ref selectedContexts[0]); - } - - /// - /// Decodes the zero-based selector for one of the three most-probable luma intra modes. - /// - /// The selector in the inclusive range zero through two. - public int ReadMostProbableIntraLumaPredictionIndex() - { - if (!this.decoder.ReadBypass()) - { - return 0; - } - - return this.decoder.ReadBypass() ? 2 : 1; - } - - /// - /// Decodes the five-bit selector for a luma intra mode outside the most-probable set. - /// - /// The decoded selector in the inclusive range zero through thirty-one. - public int ReadRemainingIntraLumaPredictionMode() => (int)this.decoder.ReadBypassBits(5); - - /// - /// Decodes the chroma intra prediction selector. - /// - /// - /// Negative one when chroma derives its mode from luma; otherwise, the decoded selector in the inclusive range - /// zero through three. - /// - public int ReadChromaPredictionModeIndex() - { - Span selectedContexts = this.contexts.ChromaPrediction; - if (!this.decoder.ReadDecision(ref selectedContexts[0])) - { - return -1; - } - - return (int)this.decoder.ReadBypassBits(2); - } - - /// - /// Decodes a transform-tree subdivision flag. - /// - /// The base-two logarithm of the current transform-block size. - /// The decoded flag value. - public bool ReadTransformSubdivision(int log2TransformBlockSize) - { - DebugGuard.MustBeBetweenOrEqualTo(log2TransformBlockSize, 3, 5, nameof(log2TransformBlockSize)); - Span selectedContexts = this.contexts.TransformSubdivision; - return this.decoder.ReadDecision(ref selectedContexts[5 - log2TransformBlockSize]); - } - - /// - /// Decodes a transform-tree coded-block flag. - /// - /// A value indicating whether the flag describes a chroma transform block. - /// The transform-depth-derived context index. - /// The decoded flag value. - public bool ReadTransformCodedBlockFlag(bool isChroma, int contextIndex) - { - DebugGuard.MustBeBetweenOrEqualTo(contextIndex, 0, 4, nameof(contextIndex)); - Span selectedContexts = this.contexts.TransformCodedBlockFlag; - int channelOffset = isChroma ? 5 : 0; - return this.decoder.ReadDecision(ref selectedContexts[channelOffset + contextIndex]); - } - - /// - /// Decodes whether a transform block bypasses the inverse transform. - /// - /// A value indicating whether the transform block belongs to a chroma channel. - /// The decoded flag value. - public bool ReadTransformSkip(bool isChroma) - { - Span selectedContexts = this.contexts.TransformSkip; - return this.decoder.ReadDecision(ref selectedContexts[isChroma ? 1 : 0]); - } - - /// - /// Decodes the signed coding-unit luma quantization-parameter delta. - /// - /// The signed delta value. - /// The coded magnitude exceeds a 32-bit signed value. - public int ReadDeltaQuantizationParameter() - { - Span selectedContexts = this.contexts.DeltaQuantization; - ulong magnitude = this.ReadTruncatedUnary(selectedContexts, 0, 1, DeltaQuantizationCutoff); - if (magnitude == DeltaQuantizationCutoff) - { - magnitude += this.ReadBypassExponentialGolomb(0); - } - - if (magnitude > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC coding-unit quantization delta is too large."); - } - - if (magnitude == 0) - { - return 0; - } - - int signedMagnitude = (int)magnitude; - return this.decoder.ReadBypass() ? -signedMagnitude : signedMagnitude; - } - - /// - /// Decodes the coding-unit chroma quantization-adjustment selector. - /// - /// The number of chroma offset pairs declared by the picture parameters. - /// Zero when no adjustment applies; otherwise, the one-based offset-list selector. - public int ReadChromaQuantizationAdjustment(int listLength) - { - Span flagContexts = this.contexts.ChromaQuantizationAdjustmentFlag; - if (!this.decoder.ReadDecision(ref flagContexts[0])) - { - return 0; - } - - if (listLength == 1) - { - return 1; - } - - Span indexContexts = this.contexts.ChromaQuantizationAdjustmentIndex; - return (int)this.ReadTruncatedUnary(indexContexts, 0, 0, listLength - 1) + 1; - } - - /// - /// Decodes the cross-component residual-prediction scale for one chroma plane. - /// - /// Zero for Cb or one for Cr. - /// Zero when prediction is disabled; otherwise, a signed power of two from one through eight. - public int ReadCrossComponentPredictionScale(int chromaPlaneIndex) - { - DebugGuard.MustBeBetweenOrEqualTo(chromaPlaneIndex, 0, 1, nameof(chromaPlaneIndex)); - Span selectedContexts = this.contexts.CrossComponentPrediction; - int contextOffset = chromaPlaneIndex * 5; - if (!this.decoder.ReadDecision(ref selectedContexts[contextOffset])) - { - return 0; - } - - int magnitudeLog2 = 0; - if (this.decoder.ReadDecision(ref selectedContexts[contextOffset + 1])) - { - Span magnitudeContexts = selectedContexts.Slice(contextOffset + 2, 2); - magnitudeLog2 = (int)this.ReadTruncatedUnary(magnitudeContexts, 0, 1, 2) + 1; - } - - int magnitude = 1 << magnitudeLog2; - return this.decoder.ReadDecision(ref selectedContexts[contextOffset + 4]) ? -magnitude : magnitude; - } - - /// - /// Decodes a sample-adaptive-offset merge flag. - /// - /// The decoded flag value. - public bool ReadSampleAdaptiveOffsetMerge() - { - Span selectedContexts = this.contexts.SampleAdaptiveOffsetMerge; - return this.decoder.ReadDecision(ref selectedContexts[0]); - } - - /// - /// Decodes the sample-adaptive-offset mode selector. - /// - /// Zero for off, one for band offset, or two for edge offset. - public int ReadSampleAdaptiveOffsetType() - { - Span selectedContexts = this.contexts.SampleAdaptiveOffsetType; - if (!this.decoder.ReadDecision(ref selectedContexts[0])) - { - return 0; - } - - return this.decoder.ReadBypass() ? 2 : 1; - } - - /// - /// Decodes a truncated-unary absolute sample-adaptive-offset value. - /// - /// The inclusive maximum offset magnitude. - /// The decoded offset magnitude. - public int ReadSampleAdaptiveOffsetAbsolute(int maximumValue) - { - if (maximumValue == 0 || !this.decoder.ReadBypass()) - { - return 0; - } - - int value = 1; - while (value < maximumValue && this.decoder.ReadBypass()) - { - value++; - } - - return value; - } - - /// - /// Decodes the five-bit sample-adaptive band-offset starting position. - /// - /// The decoded band position. - public int ReadSampleAdaptiveOffsetBandPosition() => (int)this.decoder.ReadBypassBits(5); - - /// - /// Decodes the two-bit sample-adaptive edge-offset class. - /// - /// The decoded edge class. - public int ReadSampleAdaptiveOffsetEdgeClass() => (int)this.decoder.ReadBypassBits(2); - - /// - /// Decodes a sample-adaptive band-offset sign. - /// - /// for a negative offset; otherwise, . - public bool ReadSampleAdaptiveOffsetSign() => this.decoder.ReadBypass(); - - /// - /// Decodes a horizontal last-significant-coefficient prefix flag. - /// - /// A value indicating whether the coefficient belongs to a chroma channel. - /// The block-size and prefix-derived context index within the channel. - /// The decoded flag value. - public bool ReadLastSignificantX(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.LastSignificantX; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 15 : 0) + contextIndex]); - } - - /// - /// Decodes a vertical last-significant-coefficient prefix flag. - /// - /// A value indicating whether the coefficient belongs to a chroma channel. - /// The block-size and prefix-derived context index within the channel. - /// The decoded flag value. - public bool ReadLastSignificantY(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.LastSignificantY; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 15 : 0) + contextIndex]); - } - - /// - /// Decodes a significant-coefficient-group flag. - /// - /// A value indicating whether the coefficient group belongs to a chroma channel. - /// The neighboring-group-derived context index. - /// The decoded flag value. - public bool ReadSignificantCoefficientGroup(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.SignificantCoefficientGroup; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 2 : 0) + contextIndex]); - } - - /// - /// Decodes a significant-coefficient flag. - /// - /// A value indicating whether the coefficient belongs to a chroma channel. - /// The scan-position-derived context index within the channel. - /// The decoded flag value. - public bool ReadSignificantCoefficient(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.SignificantCoefficient; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 28 : 0) + contextIndex]); - } - - /// - /// Decodes whether a significant coefficient has an absolute level greater than one. - /// - /// A value indicating whether the coefficient belongs to a chroma channel. - /// The coefficient-group and preceding-level-derived context index. - /// The decoded flag value. - public bool ReadCoefficientGreaterThanOne(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.GreaterThanOne; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 16 : 0) + contextIndex]); - } - - /// - /// Decodes whether the first eligible coefficient has an absolute level greater than two. - /// - /// A value indicating whether the coefficient belongs to a chroma channel. - /// The coefficient-group-derived context index within the channel. - /// The decoded flag value. - public bool ReadCoefficientGreaterThanTwo(bool isChroma, int contextIndex) - { - Span selectedContexts = this.contexts.GreaterThanTwo; - return this.decoder.ReadDecision(ref selectedContexts[(isChroma ? 4 : 0) + contextIndex]); - } - - /// - /// Decodes an absolute coefficient-level remainder. - /// - /// The current Golomb-Rice parameter. - /// - /// A value indicating whether extended-precision processing limits the prefix length. - /// - /// The channel's maximum transform dynamic range. - /// The decoded nonnegative coefficient-level remainder. - /// The coded remainder exceeds a 32-bit unsigned value. - public uint ReadCoefficientRemaining( - int riceParameter, - bool useLimitedPrefixLength, - int maximumLog2TransformDynamicRange) - { - int longestPrefix = useLimitedPrefixLength - ? 32 - maximumLog2TransformDynamicRange - : int.MaxValue; - - // Extended-precision streams cap the unary prefix at the transform dynamic range. Reaching that cap - // implies the end of the prefix even when the final bypass bin is one, so no terminating zero is required. - int prefix = 0; - while (prefix < longestPrefix && this.decoder.ReadBypass()) - { - prefix++; - } - - if (prefix < CoefficientRemainingReduction) - { - uint suffix = this.decoder.ReadBypassBits(riceParameter); - ulong value = ((ulong)prefix << riceParameter) + suffix; - if (value > uint.MaxValue) - { - throw new InvalidImageContentException("The HEVC coefficient level is too large."); - } - - return (uint)value; - } - - int prefixLength = prefix - CoefficientRemainingReduction; - int suffixLength; - if (useLimitedPrefixLength) - { - int maximumPrefixLength = 32 - - (CoefficientRemainingReduction + maximumLog2TransformDynamicRange); - - suffixLength = prefixLength == maximumPrefixLength - ? maximumLog2TransformDynamicRange - riceParameter - : prefixLength; - } - else - { - suffixLength = prefixLength; - } - - int codedSuffixLength = suffixLength + riceParameter; - if (prefixLength >= 32 || codedSuffixLength > 32) - { - throw new InvalidImageContentException("The HEVC coefficient level is too large."); - } - - // Prefixes beyond the first three represent an exponential-Golomb basis; the Rice parameter scales both - // that basis and the suffix while the bounded arithmetic reader supplies the remaining low bits. - uint codeWord = this.decoder.ReadBypassBits(codedSuffixLength); - ulong baseValue = (((1UL << prefixLength) - 1) + CoefficientRemainingReduction) << riceParameter; - ulong result = baseValue + codeWord; - if (result > uint.MaxValue) - { - throw new InvalidImageContentException("The HEVC coefficient level is too large."); - } - - return (uint)result; - } - - /// - /// Decodes a most-significant-bit-first sequence of equal-probability flags. - /// - /// The number of flags to decode. - /// The decoded unsigned value. - public uint ReadBypassBits(int bitCount) => this.decoder.ReadBypassBits(bitCount); - - /// - /// Selects the byte-aligned range used by aligned bypass syntax. - /// - public void AlignBypass() => this.decoder.AlignBypass(); - - /// - /// Decodes the flag that terminates a coding-tree block or entropy substream. - /// - /// The decoded termination flag. - public bool ReadTerminate() => this.decoder.ReadTerminate(); - - /// - /// Validates the stop bit and zero padding after a terminating entropy-coded value. - /// - /// The entropy substream has invalid termination alignment. - public readonly void ValidateTerminationAlignment() => this.decoder.ValidateTerminationAlignment(); - - /// - /// Decodes a context-adaptive truncated-unary value. - /// - /// The context set selected for the syntax element. - /// The context used by the first binary decision. - /// The context used by each subsequent decision. - /// The inclusive maximum decoded value. - /// The decoded truncated-unary value. - private uint ReadTruncatedUnary( - Span selectedContexts, - int firstContextIndex, - int continuationContextIndex, - int maximumValue) - { - if (maximumValue == 0 - || !this.decoder.ReadDecision(ref selectedContexts[firstContextIndex])) - { - return 0; - } - - uint value = 1; - while (value < maximumValue - && this.decoder.ReadDecision(ref selectedContexts[continuationContextIndex])) - { - value++; - } - - return value; - } - - /// - /// Decodes an equal-probability exponential-Golomb value. - /// - /// The initial suffix width. - /// The decoded unsigned value. - /// The coded value exceeds a 32-bit unsigned value. - private uint ReadBypassExponentialGolomb(int order) - { - ulong value = 0; - int suffixWidth = order; - while (this.decoder.ReadBypass()) - { - if (suffixWidth >= 32) - { - throw new InvalidImageContentException("The HEVC exponential-Golomb value is too large."); - } - - value += 1UL << suffixWidth; - suffixWidth++; - } - - // Each leading one adds the basis for the current order and widens the final suffix by one bit. - value += this.decoder.ReadBypassBits(suffixWidth); - if (value > uint.MaxValue) - { - throw new InvalidImageContentException("The HEVC exponential-Golomb value is too large."); - } - - return (uint)value; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcChromaSampleLocation.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcChromaSampleLocation.cs deleted file mode 100644 index bc78539e2..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcChromaSampleLocation.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies the location of a 4:2:0 chroma sample relative to its associated two-by-two luma sample region. -/// -internal enum HevcChromaSampleLocation : byte -{ - /// - /// The chroma sample is horizontally co-sited with the left luma column and vertically centered. - /// - Left = 0, - - /// - /// The chroma sample is horizontally and vertically centered. - /// - Center = 1, - - /// - /// The chroma sample is co-sited with the top-left luma sample. - /// - TopLeft = 2, - - /// - /// The chroma sample is horizontally centered and co-sited with the top luma row. - /// - Top = 3, - - /// - /// The chroma sample is horizontally co-sited with the left luma column and vertically co-sited with the bottom luma row. - /// - BottomLeft = 4, - - /// - /// The chroma sample is horizontally centered and vertically co-sited with the bottom luma row. - /// - Bottom = 5, -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs deleted file mode 100644 index c2aa475cf..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers.Binary; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the image-description fields and parameter-set arrays stored in an HEVC codec-configuration item -/// property. -/// -internal sealed class HevcCodecConfiguration -{ - /// - /// The NAL-unit arrays carried by the codec-configuration property. - /// - private readonly HevcNalUnitArray[] nalUnitArrays; - - /// - /// Initializes a new instance of the class from an HEVC - /// codec-configuration item-property payload. - /// - /// The complete bounded configuration payload. - public HevcCodecConfiguration(ReadOnlySpan data) - { - const int fixedRecordLength = 23; - if (data.Length < fixedRecordLength) - { - throw new InvalidImageContentException("The HEVC codec configuration is truncated."); - } - - int offset = 0; - if (data[offset++] != 1) - { - throw new InvalidImageContentException("The HEVC codec configuration has an unsupported version."); - } - - byte profile = data[offset++]; - this.GeneralProfileSpace = (byte)(profile >> 6); - this.GeneralTierFlag = (profile & 0x20) != 0; - this.GeneralProfileIdc = (byte)(profile & 0x1F); - this.GeneralProfileCompatibilityFlags = BinaryPrimitives.ReadUInt32BigEndian(data[offset..]); - offset += 4; - this.GeneralConstraintIndicatorFlags = ((ulong)BinaryPrimitives.ReadUInt32BigEndian(data[offset..]) << 16) - | BinaryPrimitives.ReadUInt16BigEndian(data[(offset + 4)..]); - - offset += 6; - this.GeneralLevelIdc = data[offset++]; - - ushort spatialSegmentation = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]); - offset += 2; - byte parallelism = data[offset++]; - byte chromaFormat = data[offset++]; - byte lumaBitDepth = data[offset++]; - byte chromaBitDepth = data[offset++]; - if ((spatialSegmentation & 0xF000) != 0xF000 - || (parallelism & 0xFC) != 0xFC - || (chromaFormat & 0xFC) != 0xFC - || (lumaBitDepth & 0xF8) != 0xF8 - || (chromaBitDepth & 0xF8) != 0xF8) - { - throw new InvalidImageContentException("The HEVC codec configuration has invalid reserved bits."); - } - - this.ChromaFormat = (byte)(chromaFormat & 3); - this.BitDepthLuma = 8 + (lumaBitDepth & 7); - this.BitDepthChroma = 8 + (chromaBitDepth & 7); - - // Reject precisions outside the public HEIF profile matrix before an unrepresentable value can enter the - // typed image metadata or reach a sample pipeline that only implements 8, 10, and 12-bit arithmetic. - if (this.BitDepthLuma is not 8 and not 10 and not 12 - || (this.ChromaFormat != 0 && this.BitDepthChroma is not 8 and not 10 and not 12)) - { - throw new InvalidImageContentException("The HEVC codec configuration uses an unsupported component bit depth."); - } - - // Average frame rate and temporal-layer signaling describe timed samples. Consume those fixed-record fields - // to reach the image item's NAL length width without retaining playback state in the still-image model. - offset += 2; - byte temporalAndLengthFields = data[offset++]; - int temporalLayerCount = (temporalAndLengthFields >> 3) & 7; - bool temporalIdNested = (temporalAndLengthFields & 4) != 0; - this.NalUnitLengthSize = (temporalAndLengthFields & 3) + 1; - - int arrayCount = data[offset++]; - this.nalUnitArrays = new HevcNalUnitArray[arrayCount]; - Span seenNalUnitTypes = stackalloc bool[64]; - for (int arrayIndex = 0; arrayIndex < arrayCount; arrayIndex++) - { - if (data.Length - offset < 3) - { - throw new InvalidImageContentException("The HEVC codec configuration contains a truncated NAL-unit array header."); - } - - byte arrayHeader = data[offset++]; - if ((arrayHeader & 0x40) != 0) - { - throw new InvalidImageContentException("The HEVC codec configuration NAL-unit array has a nonzero reserved bit."); - } - - bool isComplete = (arrayHeader & 0x80) != 0; - byte nalUnitType = (byte)(arrayHeader & 0x3F); - if (seenNalUnitTypes[nalUnitType]) - { - throw new InvalidImageContentException($"The HEVC codec configuration contains more than one array for NAL-unit type {nalUnitType}."); - } - - seenNalUnitTypes[nalUnitType] = true; - int nalUnitCount = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]); - offset += 2; - HevcNalUnit[] nalUnits = new HevcNalUnit[nalUnitCount]; - for (int nalUnitIndex = 0; nalUnitIndex < nalUnitCount; nalUnitIndex++) - { - if (data.Length - offset < 2) - { - throw new InvalidImageContentException("The HEVC codec configuration contains a truncated NAL-unit length."); - } - - int nalUnitLength = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]); - offset += 2; - if (nalUnitLength < 2 || nalUnitLength > data.Length - offset) - { - throw new InvalidImageContentException("The HEVC codec configuration contains an invalid NAL-unit length."); - } - - HevcNalUnit nalUnit = new(data.Slice(offset, nalUnitLength)); - - // The array header repeats the type so a damaged or misrouted parameter set is rejected before - // its RBSP syntax can affect the image configuration. - if (nalUnit.Header.NalUnitType != nalUnitType) - { - throw new InvalidImageContentException("The HEVC codec configuration NAL-unit type does not match its array."); - } - - nalUnits[nalUnitIndex] = nalUnit; - offset += nalUnitLength; - } - - this.nalUnitArrays[arrayIndex] = new HevcNalUnitArray(nalUnitType, isComplete, nalUnits); - } - - if (offset != data.Length) - { - throw new InvalidImageContentException("The HEVC codec configuration contains unexpected trailing data."); - } - - List videoParameterSets = new(); - foreach (HevcNalUnitArray nalUnitArray in this.nalUnitArrays) - { - const byte videoParameterSetNalUnitType = 32; - if (nalUnitArray.NalUnitType != videoParameterSetNalUnitType) - { - continue; - } - - foreach (HevcNalUnit nalUnit in nalUnitArray.NalUnits) - { - HevcVideoParameterSet videoParameterSet = new(nalUnit); - - // Legacy HEIC muxers commonly preserve only the original four source/packing constraint bits in - // hvcC and zero later profile-specific constraint bits. SPS validation provides the authoritative - // chroma and bit-depth checks, so do not reject otherwise matching Range Extensions images here. - if (!videoParameterSet.ProfileTierLevel.Matches(this) - || (temporalLayerCount != 0 && videoParameterSet.MaxSubLayers != temporalLayerCount) - || (temporalLayerCount != 0 && videoParameterSet.TemporalIdNestingFlag != temporalIdNested)) - { - throw new InvalidImageContentException("The HEVC video parameter set does not match its codec configuration."); - } - - videoParameterSets.Add(videoParameterSet); - } - } - - this.VideoParameterSets = videoParameterSets; - - List sequenceParameterSets = new(); - foreach (HevcNalUnitArray nalUnitArray in this.nalUnitArrays) - { - const byte sequenceParameterSetNalUnitType = 33; - if (nalUnitArray.NalUnitType != sequenceParameterSetNalUnitType) - { - continue; - } - - foreach (HevcNalUnit nalUnit in nalUnitArray.NalUnits) - { - HevcSequenceParameterSet sequenceParameterSet = new(nalUnit); - bool referencesKnownVideoParameterSet = false; - foreach (HevcVideoParameterSet videoParameterSet in videoParameterSets) - { - referencesKnownVideoParameterSet |= videoParameterSet.Id == sequenceParameterSet.VideoParameterSetId; - } - - if (!referencesKnownVideoParameterSet - || !sequenceParameterSet.ProfileTierLevel.Matches(this) - || sequenceParameterSet.ChromaFormat != this.ChromaFormat - || sequenceParameterSet.BitDepthLuma != this.BitDepthLuma - || sequenceParameterSet.BitDepthChroma != this.BitDepthChroma - || (temporalLayerCount != 0 && sequenceParameterSet.MaxSubLayers != temporalLayerCount) - || (temporalLayerCount != 0 && sequenceParameterSet.TemporalIdNestingFlag != temporalIdNested)) - { - throw new InvalidImageContentException("The HEVC sequence parameter set does not match its codec configuration."); - } - - sequenceParameterSets.Add(sequenceParameterSet); - } - } - - this.SequenceParameterSets = sequenceParameterSets; - - List pictureParameterSets = new(); - foreach (HevcNalUnitArray nalUnitArray in this.nalUnitArrays) - { - const byte pictureParameterSetNalUnitType = 34; - if (nalUnitArray.NalUnitType != pictureParameterSetNalUnitType) - { - continue; - } - - foreach (HevcNalUnit nalUnit in nalUnitArray.NalUnits) - { - pictureParameterSets.Add(new HevcPictureParameterSet(nalUnit, sequenceParameterSets)); - } - } - - this.PictureParameterSets = pictureParameterSets; - } - - /// - /// Gets the profile namespace declared by the coded image. - /// - public byte GeneralProfileSpace { get; } - - /// - /// Gets a value indicating whether the coded image uses the high tier. - /// - public bool GeneralTierFlag { get; } - - /// - /// Gets the profile identifier declared by the coded image. - /// - public byte GeneralProfileIdc { get; } - - /// - /// Gets the profile-compatibility flags declared by the coded image. - /// - public uint GeneralProfileCompatibilityFlags { get; } - - /// - /// Gets the 48-bit profile-constraint flags declared by the coded image. - /// - public ulong GeneralConstraintIndicatorFlags { get; } - - /// - /// Gets the level identifier declared by the coded image. - /// - public byte GeneralLevelIdc { get; } - - /// - /// Gets the coded chroma format, where zero denotes monochrome and one through three denote 4:2:0, 4:2:2, - /// and 4:4:4 respectively. - /// - public byte ChromaFormat { get; } - - /// - /// Gets the coded luma sample precision in bits. - /// - public int BitDepthLuma { get; } - - /// - /// Gets the coded chroma sample precision in bits. - /// - public int BitDepthChroma { get; } - - /// - /// Gets the maximum coded color-component precision in bits. - /// - public HeifBitDepth BitDepth - => (HeifBitDepth)(this.IsMonochrome ? this.BitDepthLuma : Math.Max(this.BitDepthLuma, this.BitDepthChroma)); - - /// - /// Gets a value indicating whether the coded image contains only a luma plane. - /// - public bool IsMonochrome => this.ChromaFormat == 0; - - /// - /// Gets the number of bytes used by each length-delimited NAL unit in the associated image item. - /// - public int NalUnitLengthSize { get; } - - /// - /// Gets the bounded NAL-unit arrays carried by the codec-configuration property. - /// - public IReadOnlyList NalUnitArrays => this.nalUnitArrays; - - /// - /// Gets the validated video parameter sets carried by the codec-configuration property. - /// - public IReadOnlyList VideoParameterSets { get; } - - /// - /// Gets the validated sequence parameter sets carried by the codec-configuration property. - /// - public IReadOnlyList SequenceParameterSets { get; } - - /// - /// Gets the validated picture parameter sets carried by the codec-configuration property. - /// - public IReadOnlyList PictureParameterSets { get; } - - /// - /// Validates the associated pixel-information property against the coded luma and chroma sample precisions. - /// - /// The per-channel precisions associated with the HEVC image item. - public void ValidateChannelBitDepths(ReadOnlySpan channelBitDepths) - { - int expectedChannelCount = this.IsMonochrome ? 1 : 3; - if (channelBitDepths.Length != expectedChannelCount || channelBitDepths[0] != this.BitDepthLuma) - { - throw new InvalidImageContentException("The HEVC item pixel information does not match its codec configuration."); - } - - for (int channel = 1; channel < channelBitDepths.Length; channel++) - { - if (channelBitDepths[channel] != this.BitDepthChroma) - { - throw new InvalidImageContentException("The HEVC item pixel information does not match its codec configuration."); - } - } - } - - /// - /// Determines whether another configuration describes the same coded-image sample layout. - /// - /// The configuration to compare. - /// when the profile, level, chroma format, and sample precisions match. - public bool HasMatchingImageConfiguration(HevcCodecConfiguration other) - => this.GeneralProfileSpace == other.GeneralProfileSpace - && this.GeneralTierFlag == other.GeneralTierFlag - && this.GeneralProfileIdc == other.GeneralProfileIdc - && this.GeneralProfileCompatibilityFlags == other.GeneralProfileCompatibilityFlags - && this.GeneralConstraintIndicatorFlags == other.GeneralConstraintIndicatorFlags - && this.GeneralLevelIdc == other.GeneralLevelIdc - && this.ChromaFormat == other.ChromaFormat - && this.BitDepthLuma == other.BitDepthLuma - && this.BitDepthChroma == other.BitDepthChroma; -} - -/// -/// Contains every configuration NAL unit declared for one HEVC NAL-unit type. -/// -internal sealed class HevcNalUnitArray -{ - /// - /// Initializes a new instance of the class. - /// - /// The six-bit HEVC NAL-unit type. - /// A value indicating whether the array contains every NAL unit of this type. - /// The decoded bounded NAL units. - public HevcNalUnitArray(byte nalUnitType, bool isComplete, HevcNalUnit[] nalUnits) - { - this.NalUnitType = nalUnitType; - this.IsComplete = isComplete; - this.NalUnits = nalUnits; - } - - /// - /// Gets the six-bit HEVC NAL-unit type shared by every entry in the array. - /// - public byte NalUnitType { get; } - - /// - /// Gets a value indicating whether the array contains every NAL unit of this type for the coded image. - /// - public bool IsComplete { get; } - - /// - /// Gets the decoded NAL units. - /// - public IReadOnlyList NalUnits { get; } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCodedBlockFlags.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCodedBlockFlags.cs deleted file mode 100644 index e2929df81..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCodedBlockFlags.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains one or two coded-block flags for a square or vertically split HEVC component transform section. -/// -internal readonly struct HevcCodedBlockFlags -{ - /// - /// Initializes a new instance of the struct for one square block. - /// - /// The square block's coded-block flag. - public HevcCodedBlockFlags(bool first) - { - this.First = first; - this.Second = false; - this.IsSplit = false; - } - - /// - /// Initializes a new instance of the struct for two rectangular sub-blocks. - /// - /// The first square sub-block's coded-block flag. - /// The second square sub-block's coded-block flag. - public HevcCodedBlockFlags(bool first, bool second) - { - this.First = first; - this.Second = second; - this.IsSplit = true; - } - - /// - /// Gets a value indicating whether the first or only coefficient block contains coded residual data. - /// - public bool First { get; } - - /// - /// Gets a value indicating whether the second rectangular sub-block contains coded residual data. - /// - public bool Second { get; } - - /// - /// Gets a value indicating whether two square sub-block flags are present. - /// - public bool IsSplit { get; } - - /// - /// Gets a value indicating whether either governed coefficient block contains coded residual data. - /// - public bool Any => this.First || this.Second; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCodingTreeState.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCodingTreeState.cs deleted file mode 100644 index 6871cdd3e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCodingTreeState.cs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Stores the spatial coding-unit state required by later HEVC still-picture syntax and reconstruction stages. -/// -internal sealed class HevcCodingTreeState : IDisposable -{ - /// - /// The coding-unit flag indicating transform and quantization bypass. - /// - private const byte TransquantBypassFlag = 1 << 0; - - /// - /// The coding-unit flag indicating pulse-code-modulated samples. - /// - private const byte PcmFlag = 1 << 1; - - /// - /// The decoded coding-unit depth at minimum-coding-block resolution. - /// - private readonly Buffer2D depths; - - /// - /// The effective luma quantization parameter at minimum-coding-block resolution. - /// - private readonly Buffer2D quantizationParameters; - - /// - /// The packed bypass and PCM flags at minimum-coding-block resolution. - /// - private readonly Buffer2D flags; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing the image memory allocator. - /// The coded picture and minimum coding-block geometry. - public HevcCodingTreeState(Configuration configuration, HevcSequenceParameterSet sequenceParameterSet) - { - this.MinCodingBlockLog2 = sequenceParameterSet.MinCodingBlockLog2; - this.WidthInMinCodingBlocks = DivideCeilingByPowerOfTwo( - sequenceParameterSet.Width, - this.MinCodingBlockLog2); - - this.HeightInMinCodingBlocks = DivideCeilingByPowerOfTwo( - sequenceParameterSet.Height, - this.MinCodingBlockLog2); - - Buffer2D? depths = null; - Buffer2D? quantizationParameters = null; - Buffer2D? flags = null; - try - { - depths = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinCodingBlocks, - this.HeightInMinCodingBlocks); - - quantizationParameters = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinCodingBlocks, - this.HeightInMinCodingBlocks); - - flags = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinCodingBlocks, - this.HeightInMinCodingBlocks); - - this.depths = depths; - this.quantizationParameters = quantizationParameters; - this.flags = flags; - } - catch - { - flags?.Dispose(); - quantizationParameters?.Dispose(); - depths?.Dispose(); - throw; - } - } - - /// - /// Gets the base-two logarithm of the state map's luma sample unit. - /// - public int MinCodingBlockLog2 { get; } - - /// - /// Gets the state-map width in minimum coding blocks. - /// - public int WidthInMinCodingBlocks { get; } - - /// - /// Gets the state-map height in minimum coding blocks. - /// - public int HeightInMinCodingBlocks { get; } - - /// - /// Gets the split-flag context derived from available left and above coding units. - /// - /// The current coding-unit left coordinate in luma samples. - /// The current coding-unit top coordinate in luma samples. - /// The current coding-tree depth. - /// A value indicating whether the left coding unit is available for prediction. - /// A value indicating whether the above coding unit is available for prediction. - /// The split context in the inclusive range zero through two. - public int GetSplitContext(int x, int y, int depth, bool leftAvailable, bool aboveAvailable) - { - int unitX = x >> this.MinCodingBlockLog2; - int unitY = y >> this.MinCodingBlockLog2; - int context = 0; - if (leftAvailable && this.depths.DangerousGetRowSpan(unitY)[unitX - 1] > depth) - { - context++; - } - - if (aboveAvailable && this.depths.DangerousGetRowSpan(unitY - 1)[unitX] > depth) - { - context++; - } - - return context; - } - - /// - /// Records the state shared by every minimum coding block covered by one leaf coding unit. - /// - /// The coding-unit left coordinate in luma samples. - /// The coding-unit top coordinate in luma samples. - /// The base-two logarithm of the square coding-unit size. - /// The coding-tree depth. - /// The effective luma quantization parameter. - /// A value indicating whether transform and quantization are bypassed. - /// A value indicating whether the coding unit contains pulse-code-modulated samples. - public void SetCodingUnit( - int x, - int y, - int log2Size, - int depth, - int quantizationParameter, - bool transquantBypass, - bool pcm) - { - int unitX = x >> this.MinCodingBlockLog2; - int unitY = y >> this.MinCodingBlockLog2; - int unitCount = 1 << (log2Size - this.MinCodingBlockLog2); - int endX = Math.Min(unitX + unitCount, this.WidthInMinCodingBlocks); - int endY = Math.Min(unitY + unitCount, this.HeightInMinCodingBlocks); - byte packedFlags = (byte)((transquantBypass ? TransquantBypassFlag : 0) | (pcm ? PcmFlag : 0)); - - // Edge coding units still cover a complete power-of-two block in syntax, but the state map contains only - // displayed picture coordinates. Clipping here keeps later neighbor lookup within the owned picture state. - for (int row = unitY; row < endY; row++) - { - this.depths.DangerousGetRowSpan(row)[unitX..endX].Fill((byte)depth); - this.quantizationParameters.DangerousGetRowSpan(row)[unitX..endX].Fill((sbyte)quantizationParameter); - this.flags.DangerousGetRowSpan(row)[unitX..endX].Fill(packedFlags); - } - } - - /// - /// Gets the recorded coding-tree depth at a luma sample coordinate. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// The leaf coding-unit depth. - public int GetDepth(int x, int y) - => this.depths.DangerousGetRowSpan(y >> this.MinCodingBlockLog2)[x >> this.MinCodingBlockLog2]; - - /// - /// Gets the effective luma quantization parameter at a luma sample coordinate. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// The effective luma quantization parameter. - public int GetQuantizationParameter(int x, int y) - => this.quantizationParameters.DangerousGetRowSpan(y >> this.MinCodingBlockLog2)[x >> this.MinCodingBlockLog2]; - - /// - /// Gets a value indicating whether the coding unit at a luma sample coordinate bypasses transform and quantization. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// when bypass is enabled; otherwise, . - public bool IsTransquantBypass(int x, int y) - => (this.flags.DangerousGetRowSpan(y >> this.MinCodingBlockLog2)[x >> this.MinCodingBlockLog2] - & TransquantBypassFlag) != 0; - - /// - /// Gets a value indicating whether the coding unit at a luma sample coordinate contains PCM samples. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// for pulse-code-modulated samples; otherwise, . - public bool IsPcm(int x, int y) - => (this.flags.DangerousGetRowSpan(y >> this.MinCodingBlockLog2)[x >> this.MinCodingBlockLog2] - & PcmFlag) != 0; - - /// - /// Releases the owned coding-tree state maps. - /// - public void Dispose() - { - this.depths.Dispose(); - this.quantizationParameters.Dispose(); - this.flags.Dispose(); - } - - /// - /// Divides a nonnegative sample count by a power of two with upward rounding. - /// - /// The sample count. - /// The base-two divisor logarithm. - /// The upward-rounded quotient. - private static int DivideCeilingByPowerOfTwo(int value, int shift) => (value + (1 << shift) - 1) >> shift; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientCodingParameters.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientCodingParameters.cs deleted file mode 100644 index 78d4d9027..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientCodingParameters.cs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the immutable entropy-coding parameters for one HEVC transform block. -/// -internal readonly struct HevcCoefficientCodingParameters -{ - /// - /// Initializes a new instance of the struct. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// The coefficient scan selected for the block. - /// Whether transform skip or transquant bypass selects the single significance context. - /// Whether the first coefficient sign in an eligible group is inferred. - /// Whether Rice parameters adapt across transform blocks. - /// Whether coefficient bypass data is byte aligned when escape data is present. - /// Whether coefficient remainders use the bounded extended-precision prefix. - /// The component transform dynamic range excluding its sign bit. - /// The luma/chroma and transformed/non-transformed Rice statistics selector. - public HevcCoefficientCodingParameters( - int width, - int height, - HevcPlane plane, - HevcCoefficientScanType scanType, - bool useSingleSignificanceContext, - bool signDataHidingEnabled, - bool persistentRiceAdaptationEnabled, - bool cabacBypassAlignmentEnabled, - bool extendedPrecisionProcessingEnabled, - int maximumLog2TransformDynamicRange, - int riceStatisticsIndex) - { - this.Width = width; - this.Height = height; - this.Plane = plane; - this.ScanType = scanType; - this.FirstSignificanceMapContext = GetFirstSignificanceMapContext(width, height, plane != HevcPlane.Y, scanType, useSingleSignificanceContext); - this.SignDataHidingEnabled = signDataHidingEnabled; - this.PersistentRiceAdaptationEnabled = persistentRiceAdaptationEnabled; - this.CabacBypassAlignmentEnabled = cabacBypassAlignmentEnabled; - this.ExtendedPrecisionProcessingEnabled = extendedPrecisionProcessingEnabled; - this.MaximumLog2TransformDynamicRange = maximumLog2TransformDynamicRange; - this.RiceStatisticsIndex = riceStatisticsIndex; - } - - /// - /// Gets the transform-block width. - /// - public int Width { get; } - - /// - /// Gets the transform-block height. - /// - public int Height { get; } - - /// - /// Gets the reconstructed component. - /// - public HevcPlane Plane { get; } - - /// - /// Gets the coefficient scan selected for the block. - /// - public HevcCoefficientScanType ScanType { get; } - - /// - /// Gets the first significant-coefficient context within the component context set. - /// - public int FirstSignificanceMapContext { get; } - - /// - /// Gets a value indicating whether an eligible first coefficient sign is inferred from the group parity. - /// - public bool SignDataHidingEnabled { get; } - - /// - /// Gets a value indicating whether Rice parameters adapt across transform blocks. - /// - public bool PersistentRiceAdaptationEnabled { get; } - - /// - /// Gets a value indicating whether coefficient bypass data is byte aligned when escape data is present. - /// - public bool CabacBypassAlignmentEnabled { get; } - - /// - /// Gets a value indicating whether coefficient remainders use the bounded extended-precision prefix. - /// - public bool ExtendedPrecisionProcessingEnabled { get; } - - /// - /// Gets the component transform dynamic range excluding its sign bit. - /// - public int MaximumLog2TransformDynamicRange { get; } - - /// - /// Gets the luma/chroma and transformed/non-transformed Rice statistics selector. - /// - public int RiceStatisticsIndex { get; } - - /// - /// Gets the raster-position context mapping for a 4 by 4 transform block. - /// - private static ReadOnlySpan SignificanceContexts4x4 => - [ - 0, 1, 4, 5, - 2, 3, 4, 5, - 6, 6, 8, 8, - 7, 7, 8, 8, - ]; - - /// - /// Creates the coefficient parameters selected by the active sequence, picture, and transform-unit state. - /// - /// The active picture parameters. - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// Whether the containing coding unit uses intra prediction. - /// The effective intra prediction mode, or a value ignored for inter prediction. - /// Whether the transform block bypasses the inverse transform. - /// Whether the coding unit bypasses inverse quantization and inverse transform. - /// The residual differential-pulse-code-modulation mode selected for the block. - /// Whether a separately coded color plane uses the luma coefficient context set. - /// The coefficient entropy-coding parameters for the transform block. - public static HevcCoefficientCodingParameters Create( - HevcPictureParameterSet pictureParameterSet, - int width, - int height, - HevcPlane plane, - bool isIntra, - int intraPredictionMode, - bool transformSkip, - bool transquantBypass, - HevcResidualDpcmMode residualDpcmMode, - bool useLumaSyntax = false) - { - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - HevcPlane codingPlane = useLumaSyntax ? HevcPlane.Y : plane; - bool isChroma = codingPlane != HevcPlane.Y; - bool nonTransformed = transformSkip || transquantBypass; - HevcCoefficientScanType scanType = SelectScanType( - width, - height, - codingPlane, - isIntra, - intraPredictionMode, - sequenceParameterSet.ChromaFormat, - sequenceParameterSet.SeparateColorPlaneFlag); - - return new HevcCoefficientCodingParameters( - width, - height, - plane, - scanType, - sequenceParameterSet.TransformSkipContextEnabled && nonTransformed, - pictureParameterSet.SignDataHidingEnabled && !transquantBypass && residualDpcmMode == HevcResidualDpcmMode.None, - sequenceParameterSet.PersistentRiceAdaptationEnabled, - sequenceParameterSet.CabacBypassAlignmentEnabled, - sequenceParameterSet.ExtendedPrecisionProcessingEnabled, - sequenceParameterSet.GetMaxTransformDynamicRange(plane), - (isChroma ? 2 : 0) + (nonTransformed ? 1 : 0)); - } - - /// - /// Selects the scan direction from transform geometry and the effective intra prediction direction. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// Whether the containing coding unit uses intra prediction. - /// The effective intra prediction mode. - /// The sequence chroma-format identifier. - /// Whether each 4:4:4 component is coded as an independent color plane. - /// The selected coefficient scan. - public static HevcCoefficientScanType SelectScanType( - int width, - int height, - HevcPlane plane, - bool isIntra, - int intraPredictionMode, - byte chromaFormat, - bool separateColorPlane) - { - if (!isIntra) - { - return HevcCoefficientScanType.Diagonal; - } - - bool isSubsampledChroma = plane != HevcPlane.Y && !separateColorPlane; - int subsamplingX = isSubsampledChroma && chromaFormat is 1 or 2 ? 1 : 0; - int subsamplingY = isSubsampledChroma && chromaFormat == 1 ? 1 : 0; - if (width > (8 >> subsamplingX) || height > (8 >> subsamplingY)) - { - return HevcCoefficientScanType.Diagonal; - } - - int mode = plane != HevcPlane.Y && chromaFormat == 2 && !separateColorPlane - ? HevcIntraPredictionMode.RemapChroma422(intraPredictionMode) - : intraPredictionMode; - - // Modes close to vertical place correlated residuals along rows, while modes close to horizontal use the - // transposed column scan. All other modes retain the diagonal scan. - if (Math.Abs(mode - HevcIntraPredictionMode.Vertical) <= 4) - { - return HevcCoefficientScanType.Horizontal; - } - - return Math.Abs(mode - HevcIntraPredictionMode.Horizontal) <= 4 - ? HevcCoefficientScanType.Vertical - : HevcCoefficientScanType.Diagonal; - } - - /// - /// Derives the coded-sub-block significance context from already decoded right and lower groups. - /// - /// The raster-ordered significant-group flags. - /// The current group horizontal coordinate. - /// The current group vertical coordinate. - /// Zero when neither neighbor is significant; otherwise, one. - public int GetSignificantGroupContext(ReadOnlySpan groupFlags, int groupX, int groupY) - { - int widthInGroups = this.Width / 4; - int heightInGroups = this.Height / 4; - bool rightSignificant = groupX < widthInGroups - 1 && groupFlags[(groupY * widthInGroups) + groupX + 1] != 0; - bool lowerSignificant = groupY < heightInGroups - 1 && groupFlags[((groupY + 1) * widthInGroups) + groupX] != 0; - return rightSignificant || lowerSignificant ? 1 : 0; - } - - /// - /// Derives the two-bit right-and-lower significance pattern for coefficient contexts. - /// - /// The raster-ordered significant-group flags. - /// The current group horizontal coordinate. - /// The current group vertical coordinate. - /// The right flag in bit zero and the lower flag in bit one. - public int GetSignificancePattern(ReadOnlySpan groupFlags, int groupX, int groupY) - { - int widthInGroups = this.Width / 4; - int heightInGroups = this.Height / 4; - int right = groupX < widthInGroups - 1 && groupFlags[(groupY * widthInGroups) + groupX + 1] != 0 ? 1 : 0; - int lower = groupY < heightInGroups - 1 && groupFlags[((groupY + 1) * widthInGroups) + groupX] != 0 ? 1 : 0; - return right + (lower << 1); - } - - /// - /// Derives the significant-coefficient context from its position and neighboring coefficient groups. - /// - /// The coefficient raster position. - /// The right-and-lower significant-group pattern. - /// The context index within the component significance-map context set. - public int GetSignificantCoefficientContext(int rasterPosition, int significancePattern) - { - bool isChroma = this.Plane != HevcPlane.Y; - if (this.FirstSignificanceMapContext == (isChroma ? 15 : 27)) - { - return this.FirstSignificanceMapContext; - } - - int y = rasterPosition / this.Width; - int x = rasterPosition - (y * this.Width); - if (x + y == 0) - { - return 0; - } - - if (this.Width == 4 && this.Height == 4) - { - return SignificanceContexts4x4[(y * 4) + x]; - } - - int context; - switch (significancePattern) - { - case 0: - int positionInGroup = (x & 3) + (y & 3); - context = positionInGroup >= 3 ? 0 : positionInGroup >= 1 ? 1 : 2; - break; - case 1: - int yInGroup = y & 3; - context = yInGroup >= 2 ? 0 : yInGroup >= 1 ? 1 : 2; - break; - case 2: - int xInGroup = x & 3; - context = xInGroup >= 2 ? 0 : xInGroup >= 1 ? 1 : 2; - break; - default: - context = 2; - break; - } - - bool isBeyondFirstGroup = (x >> 2) + (y >> 2) > 0; - return this.FirstSignificanceMapContext + (isBeyondFirstGroup && !isChroma ? 3 : 0) + context; - } - - /// - /// Selects the greater-than-one and greater-than-two context set for one coefficient group. - /// - /// The coefficient-group scan index. - /// Whether the preceding group ended after finding a coefficient greater than one. - /// The zero-based context set within the component context range. - public int GetLevelContextSet(int subset, bool foundGreaterThanOne) - { - int nonFirstSubsetOffset = this.Plane == HevcPlane.Y && subset > 0 ? 2 : 0; - return nonFirstSubsetOffset + (foundGreaterThanOne ? 1 : 0); - } - - /// - /// Derives the first significant-coefficient context within one component context set. - /// - /// The transform-block width. - /// The transform-block height. - /// Whether the transform block belongs to a chroma channel. - /// The selected coefficient scan. - /// Whether Range Extensions selects the single-context mode. - /// The first significant-coefficient context index. - private static int GetFirstSignificanceMapContext( - int width, - int height, - bool isChroma, - HevcCoefficientScanType scanType, - bool useSingleSignificanceContext) - { - if (useSingleSignificanceContext) - { - return isChroma ? 15 : 27; - } - - if (width == 4 && height == 4) - { - return 0; - } - - if (width == 8 && height == 8) - { - return isChroma ? 9 : scanType == HevcCoefficientScanType.Diagonal ? 9 : 15; - } - - return isChroma ? 12 : 21; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientDecoder.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientDecoder.cs deleted file mode 100644 index 09342f01d..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientDecoder.cs +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using System.Numerics; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Decodes HEVC transform coefficients while retaining entropy-substream Rice state and reusable scratch storage. -/// -internal sealed class HevcCoefficientDecoder : IDisposable -{ - /// - /// The maximum coefficient count in a 32 by 32 transform block. - /// - private const int MaximumCoefficientCount = 32 * 32; - - /// - /// The maximum number of 4 by 4 coefficient groups in a transform block. - /// - private const int MaximumCoefficientGroupCount = MaximumCoefficientCount / 16; - - /// - /// The maximum number of significant coefficients in one coefficient group. - /// - private const int CoefficientsPerGroup = 16; - - /// - /// The maximum number of greater-than-one flags coded in one coefficient group. - /// - private const int GreaterThanOneFlagCount = 8; - - /// - /// The minimum scan-position separation that enables sign-data hiding. - /// - private const int SignDataHidingThreshold = 4; - - /// - /// The divisor that converts a persistent adaptation statistic to its Rice parameter. - /// - private const int RiceAdaptationDivisor = 4; - - /// - /// The first scratch index occupied by coefficient-group significance flags. - /// - private const int CoefficientGroupFlagsOffset = MaximumCoefficientCount; - - /// - /// The first scratch index occupied by significant coefficient raster positions. - /// - private const int CoefficientPositionsOffset = CoefficientGroupFlagsOffset + MaximumCoefficientGroupCount; - - /// - /// The first scratch index occupied by absolute coefficient levels. - /// - private const int AbsoluteLevelsOffset = CoefficientPositionsOffset + CoefficientsPerGroup; - - /// - /// The total number of pooled integers used by coefficient decoding. - /// - private const int ScratchLength = AbsoluteLevelsOffset + CoefficientsPerGroup; - - /// - /// The allocator-owned scan and coefficient-group working storage reused for every transform block. - /// - private readonly IMemoryOwner scratchOwner; - - /// - /// The persistent Rice statistics for transformed and non-transformed luma and chroma blocks. - /// - private InlineArray4 riceAdaptationStatistics; - - /// - /// Initializes a new instance of the class for one entropy substream. - /// - /// The configuration providing pooled codec memory. - public HevcCoefficientDecoder(Configuration configuration) - { - this.scratchOwner = configuration.MemoryAllocator.Allocate(ScratchLength); - this.riceAdaptationStatistics = default; - } - - /// - /// Gets the minimum coordinate represented by each last-significant prefix. - /// - private static ReadOnlySpan MinimumCoordinateInGroup => [0, 1, 2, 3, 4, 6, 8, 12, 16, 24]; - - /// - /// Gets the last-significant prefix selected by each transform coordinate. - /// - private static ReadOnlySpan CoordinateGroupIndex => - [ - 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, - ]; - - /// - /// Clears all persistent Rice adaptation statistics for a newly initialized entropy substream. - /// - public void ResetRiceAdaptation() => this.riceAdaptationStatistics = default; - - /// - /// Copies the four persistent Rice adaptation statistics to caller-owned wavefront state. - /// - /// The four-element destination. - public void CopyRiceAdaptationTo(Span destination) => this.riceAdaptationStatistics[..4].CopyTo(destination); - - /// - /// Restores the four persistent Rice adaptation statistics captured for a later wavefront row. - /// - /// The four saved statistics. - public void CopyRiceAdaptationFrom(ReadOnlySpan source) => source[..4].CopyTo(this.riceAdaptationStatistics[..4]); - - /// - /// Decodes one transform block into raster-ordered signed coefficient levels. - /// - /// The current entropy-substream syntax reader. - /// The destination coefficient block. - /// The transform-block coefficient coding parameters. - /// The number of nonzero coefficients decoded into . - public int Decode(ref HevcCabacSyntaxReader reader, Span coefficients, in HevcCoefficientCodingParameters parameters) - { - int width = parameters.Width; - int height = parameters.Height; - int coefficientCount = width * height; - bool isChroma = parameters.Plane != HevcPlane.Y; - coefficients[..coefficientCount].Clear(); - - ReadLastSignificantPosition(ref reader, in parameters, out int lastX, out int lastY); - int lastRasterPosition = (lastY * width) + lastX; - Span scratch = this.scratchOwner.Memory.Span; - Span scan = scratch[..coefficientCount]; - int lastScanPosition = HevcCoefficientScanOrder.Write(scan, width, height, parameters.ScanType, lastRasterPosition); - int groupCount = coefficientCount / CoefficientsPerGroup; - Span significantGroupFlags = scratch.Slice(CoefficientGroupFlagsOffset, groupCount); - Span positions = scratch.Slice(CoefficientPositionsOffset, CoefficientsPerGroup); - Span absoluteLevels = scratch.Slice(AbsoluteLevelsOffset, CoefficientsPerGroup); - significantGroupFlags.Clear(); - - int widthInGroups = width / 4; - int lastSubset = lastScanPosition / CoefficientsPerGroup; - int significantScanPosition = lastScanPosition; - int c1 = 1; - int totalNonZero = 0; - ref int currentRiceStatistic = ref this.riceAdaptationStatistics[parameters.RiceStatisticsIndex]; - - // Coefficient groups are decoded from the last significant position toward DC. This direction makes the - // already decoded right and lower groups available to the significance-context derivation below. - for (int subset = lastSubset; subset >= 0; subset--) - { - int subsetStart = subset * CoefficientsPerGroup; - int riceParameter = currentRiceStatistic / RiceAdaptationDivisor; - bool updateRiceStatistic = parameters.PersistentRiceAdaptationEnabled; - int nonZeroCount = 0; - int lastNonZeroScanPosition = -1; - int firstNonZeroScanPosition = CoefficientsPerGroup; - bool escapeDataPresent = false; - - if (significantScanPosition == lastScanPosition) - { - lastNonZeroScanPosition = significantScanPosition; - firstNonZeroScanPosition = significantScanPosition; - significantScanPosition--; - positions[0] = lastRasterPosition; - nonZeroCount = 1; - } - - int groupRasterPosition = scan[subsetStart]; - int groupY = (groupRasterPosition / width) / 4; - int groupX = (groupRasterPosition % width) / 4; - int groupIndex = (groupY * widthInGroups) + groupX; - if (subset == lastSubset || subset == 0) - { - significantGroupFlags[groupIndex] = 1; - } - else - { - int groupContext = parameters.GetSignificantGroupContext(significantGroupFlags, groupX, groupY); - significantGroupFlags[groupIndex] = reader.ReadSignificantCoefficientGroup(isChroma, groupContext) ? 1 : 0; - } - - int significancePattern = parameters.GetSignificancePattern(significantGroupFlags, groupX, groupY); - for (; significantScanPosition >= subsetStart; significantScanPosition--) - { - int rasterPosition = scan[significantScanPosition]; - bool isSignificant = false; - if (significantGroupFlags[groupIndex] != 0) - { - if (significantScanPosition > subsetStart || subset == 0 || nonZeroCount != 0) - { - int contextIndex = parameters.GetSignificantCoefficientContext(rasterPosition, significancePattern); - isSignificant = reader.ReadSignificantCoefficient(isChroma, contextIndex); - } - else - { - // A coded significant group must contain at least one coefficient. When every later flag is - // zero, the first scan position is therefore inferred rather than consuming another CABAC bin. - isSignificant = true; - } - } - - if (isSignificant) - { - positions[nonZeroCount++] = rasterPosition; - if (lastNonZeroScanPosition < 0) - { - lastNonZeroScanPosition = significantScanPosition; - } - - firstNonZeroScanPosition = significantScanPosition; - } - } - - if (nonZeroCount == 0) - { - continue; - } - - bool hideSign = lastNonZeroScanPosition - firstNonZeroScanPosition >= SignDataHidingThreshold; - int contextSet = parameters.GetLevelContextSet(subset, c1 == 0); - c1 = 1; - absoluteLevels[..nonZeroCount].Fill(1); - int greaterThanOneCount = Math.Min(nonZeroCount, GreaterThanOneFlagCount); - int firstGreaterThanOneIndex = -1; - - for (int index = 0; index < greaterThanOneCount; index++) - { - bool greaterThanOne = reader.ReadCoefficientGreaterThanOne(isChroma, (contextSet * 4) + c1); - if (greaterThanOne) - { - c1 = 0; - if (firstGreaterThanOneIndex < 0) - { - firstGreaterThanOneIndex = index; - } - else - { - escapeDataPresent = true; - } - } - else if (c1 is > 0 and < 3) - { - c1++; - } - - absoluteLevels[index] = greaterThanOne ? 2 : 1; - } - - if (c1 == 0 && firstGreaterThanOneIndex >= 0) - { - bool greaterThanTwo = reader.ReadCoefficientGreaterThanTwo(isChroma, contextSet); - absoluteLevels[firstGreaterThanOneIndex] = greaterThanTwo ? 3 : 2; - escapeDataPresent |= greaterThanTwo; - } - - escapeDataPresent |= nonZeroCount > GreaterThanOneFlagCount; - if (escapeDataPresent && parameters.CabacBypassAlignmentEnabled) - { - reader.AlignBypass(); - } - - int signCount = hideSign && parameters.SignDataHidingEnabled ? nonZeroCount - 1 : nonZeroCount; - uint coefficientSigns = reader.ReadBypassBits(signCount); - int nextSignBit = signCount - 1; - int firstCoefficientAtLeastTwo = 1; - if (escapeDataPresent) - { - for (int index = 0; index < nonZeroCount; index++) - { - int baseLevel = index < GreaterThanOneFlagCount ? 2 + firstCoefficientAtLeastTwo : 1; - if (absoluteLevels[index] == baseLevel) - { - uint remainder = reader.ReadCoefficientRemaining( - riceParameter, - parameters.ExtendedPrecisionProcessingEnabled, - parameters.MaximumLog2TransformDynamicRange); - - ulong decodedLevel = (ulong)remainder + (uint)baseLevel; - if (decodedLevel > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC transform coefficient level is too large."); - } - - absoluteLevels[index] = (int)decodedLevel; - if (decodedLevel > (3UL << riceParameter)) - { - riceParameter = parameters.PersistentRiceAdaptationEnabled ? riceParameter + 1 : Math.Min(riceParameter + 1, 4); - } - - if (updateRiceStatistic) - { - int initialRiceParameter = currentRiceStatistic / RiceAdaptationDivisor; - if (remainder >= (3UL << initialRiceParameter)) - { - currentRiceStatistic++; - } - else if (((ulong)remainder * 2) < (1UL << initialRiceParameter) && currentRiceStatistic > 0) - { - currentRiceStatistic--; - } - - // Only the first escape value in a coefficient group updates persistent state. - updateRiceStatistic = false; - } - } - - if (absoluteLevels[index] >= 2) - { - firstCoefficientAtLeastTwo = 0; - } - } - } - - int absoluteSum = 0; - for (int index = 0; index < nonZeroCount; index++) - { - int rasterPosition = positions[index]; - int level = absoluteLevels[index]; - absoluteSum += level; - if (index == nonZeroCount - 1 && hideSign && parameters.SignDataHidingEnabled) - { - level = (absoluteSum & 1) == 0 ? level : -level; - } - else if (((coefficientSigns >> nextSignBit--) & 1U) != 0) - { - level = -level; - } - - coefficients[rasterPosition] = level; - } - - totalNonZero += nonZeroCount; - } - - return totalNonZero; - } - - /// - /// Releases the allocator-owned coefficient scratch storage. - /// - public void Dispose() => this.scratchOwner.Dispose(); - - /// - /// Decodes the raster coordinates of the final significant coefficient. - /// - /// The current entropy-substream syntax reader. - /// The transform-block coefficient coding parameters. - /// The decoded horizontal coordinate. - /// The decoded vertical coordinate. - private static void ReadLastSignificantPosition( - ref HevcCabacSyntaxReader reader, - in HevcCoefficientCodingParameters parameters, - out int x, - out int y) - { - bool verticalScan = parameters.ScanType == HevcCoefficientScanType.Vertical; - int syntaxWidth = verticalScan ? parameters.Height : parameters.Width; - int syntaxHeight = verticalScan ? parameters.Width : parameters.Height; - bool isChroma = parameters.Plane != HevcPlane.Y; - int xPrefix = ReadLastSignificantPrefix(ref reader, isChroma, syntaxWidth, true); - int yPrefix = ReadLastSignificantPrefix(ref reader, isChroma, syntaxHeight, false); - - // The HEVC syntax carries both context-coded prefixes before either bypass-coded suffix. Decoding a suffix - // immediately after its own prefix changes the arithmetic bit order whenever both coordinates need suffixes. - x = ReadLastSignificantSuffix(ref reader, xPrefix); - y = ReadLastSignificantSuffix(ref reader, yPrefix); - if (verticalScan) - { - (x, y) = (y, x); - } - } - - /// - /// Decodes one context-coded last-significant coefficient coordinate prefix. - /// - /// The current entropy-substream syntax reader. - /// Whether the coordinate belongs to a chroma transform block. - /// The transform-block extent along the coded axis. - /// Whether to use the horizontal rather than vertical context set. - /// The decoded coordinate prefix. - private static int ReadLastSignificantPrefix(ref HevcCabacSyntaxReader reader, bool isChroma, int size, bool horizontal) - { - int convertedSize = BitOperations.Log2((uint)size) - 2; - int contextOffset = isChroma ? 0 : (convertedSize * 3) + ((convertedSize + 1) >> 2); - int contextShift = isChroma ? convertedSize : (convertedSize + 3) >> 2; - int maximumPrefix = CoordinateGroupIndex[size - 1]; - int prefix; - for (prefix = 0; prefix < maximumPrefix; prefix++) - { - int contextIndex = contextOffset + (prefix >> contextShift); - bool prefixContinues = horizontal - ? reader.ReadLastSignificantX(isChroma, contextIndex) - : reader.ReadLastSignificantY(isChroma, contextIndex); - - if (!prefixContinues) - { - break; - } - } - - return prefix; - } - - /// - /// Expands one last-significant coordinate prefix with its bypass-coded suffix. - /// - /// The current entropy-substream syntax reader. - /// The context-coded coordinate prefix. - /// The decoded zero-based coefficient coordinate. - private static int ReadLastSignificantSuffix(ref HevcCabacSyntaxReader reader, int prefix) - { - if (prefix <= 3) - { - return prefix; - } - - int suffixLength = (prefix - 2) >> 1; - return MinimumCoordinateInGroup[prefix] + (int)reader.ReadBypassBits(suffixLength); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanOrder.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanOrder.cs deleted file mode 100644 index 75eccfe29..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanOrder.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Writes the grouped coefficient scan used by HEVC residual entropy coding. -/// -internal static class HevcCoefficientScanOrder -{ - /// - /// The width and height of one coefficient group. - /// - private const int CoefficientGroupSize = 4; - - /// - /// Writes the grouped scan for one transform block into caller-owned storage. - /// - /// The destination receiving raster coefficient indices in scan order. - /// The transform-block width. - /// The transform-block height. - /// The scan direction selected for the transform block. - /// The raster index of the last significant coefficient. - /// The scan position of . - public static int Write(Span destination, int width, int height, HevcCoefficientScanType scanType, int lastRasterPosition) - { - int widthInGroups = width / CoefficientGroupSize; - int heightInGroups = height / CoefficientGroupSize; - int groupCount = widthInGroups * heightInGroups; - int lastScanPosition = -1; - ScanGenerator groupScan = new(widthInGroups, heightInGroups, scanType); - - // H.265 scans the 4x4 groups first, then applies the same direction inside each group. Keeping this grouped - // layout contiguous lets coefficient decoding walk every 16-entry subset without lookup-table allocations. - for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) - { - int groupOffsetX = groupScan.X * CoefficientGroupSize; - int groupOffsetY = groupScan.Y * CoefficientGroupSize; - int groupScanOffset = groupIndex * CoefficientGroupSize * CoefficientGroupSize; - ScanGenerator coefficientScan = new(CoefficientGroupSize, CoefficientGroupSize, scanType); - - for (int coefficientIndex = 0; coefficientIndex < CoefficientGroupSize * CoefficientGroupSize; coefficientIndex++) - { - int rasterPosition = ((groupOffsetY + coefficientScan.Y) * width) + groupOffsetX + coefficientScan.X; - int scanPosition = groupScanOffset + coefficientIndex; - destination[scanPosition] = rasterPosition; - if (rasterPosition == lastRasterPosition) - { - lastScanPosition = scanPosition; - } - - coefficientScan.MoveNext(); - } - - groupScan.MoveNext(); - } - - return lastScanPosition; - } - - /// - /// Advances through one rectangular scan without retaining a heap-backed lookup table. - /// - private struct ScanGenerator - { - /// - /// The scan width. - /// - private readonly int width; - - /// - /// The scan height. - /// - private readonly int height; - - /// - /// The selected scan direction. - /// - private readonly HevcCoefficientScanType scanType; - - /// - /// Initializes a new instance of the struct. - /// - /// The scan width. - /// The scan height. - /// The scan direction. - public ScanGenerator(int width, int height, HevcCoefficientScanType scanType) - { - this.width = width; - this.height = height; - this.scanType = scanType; - this.X = 0; - this.Y = 0; - } - - /// - /// Gets the current horizontal coordinate. - /// - public int X { get; private set; } - - /// - /// Gets the current vertical coordinate. - /// - public int Y { get; private set; } - - /// - /// Advances to the next coordinate in the selected scan direction. - /// - public void MoveNext() - { - switch (this.scanType) - { - case HevcCoefficientScanType.Diagonal: - if (this.X == this.width - 1 || this.Y == 0) - { - this.Y += this.X + 1; - this.X = 0; - if (this.Y >= this.height) - { - this.X += this.Y - (this.height - 1); - this.Y = this.height - 1; - } - } - else - { - this.X++; - this.Y--; - } - - break; - case HevcCoefficientScanType.Horizontal: - if (++this.X == this.width) - { - this.X = 0; - this.Y++; - } - - break; - default: - if (++this.Y == this.height) - { - this.Y = 0; - this.X++; - } - - break; - } - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanType.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanType.cs deleted file mode 100644 index 93ceca238..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcCoefficientScanType.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies the coefficient scan used by one HEVC transform block. -/// -internal enum HevcCoefficientScanType -{ - /// - /// The up-right diagonal scan. - /// - Diagonal, - - /// - /// The row-major horizontal scan. - /// - Horizontal, - - /// - /// The column-major vertical scan. - /// - Vertical, -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.HorizontalEdgeOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.HorizontalEdgeOperator.cs deleted file mode 100644 index d2f62c337..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.HorizontalEdgeOperator.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcDeblockingFilter -{ - /// - /// Accesses four columns across a horizontal edge. - /// - private readonly struct HorizontalEdgeOperator : IEdgeOperator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count) - { - ref ushort source = ref picture.GetRowSpan(plane, y + distance)[x]; - if (count == 2) - { - // The packed load used by full-width segments would read two samples beyond a subsampled edge. - return Vector128.Create((int)source, Unsafe.Add(ref source, 1), 0, 0); - } - - Vector64 packed = Unsafe.As>(ref source); - return Vector128.WidenLower(Vector128.Create(packed, Vector64.Zero)).AsInt32(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreVector( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int distance, - Vector128 value, - int count) - { - ref ushort destination = ref picture.GetRowSpan(plane, y + distance)[x]; - if (count == 4) - { - Vector64 packed = Vector128.Narrow(value, Vector128.Zero).AsUInt16().GetLower(); - Unsafe.As>(ref destination) = packed; - return; - } - - destination = (ushort)value.GetElement(0); - Unsafe.Add(ref destination, 1) = (ushort)value.GetElement(1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index) - => picture.GetRowSpan(plane, y + distance)[x + index]; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value) - => picture.GetRowSpan(plane, y + distance)[x + index] = (ushort)value; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.Operator.cs deleted file mode 100644 index 19581b352..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.Operator.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcDeblockingFilter -{ - /// - /// Defines orientation-specific access to the four samples running along one deblocking edge segment. - /// - private interface IEdgeOperator - { - /// - /// Loads samples at one signed distance across the edge. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The signed sample distance across the edge. - /// The number of valid low lanes to load. - /// The widened samples ordered along the edge. - public static abstract Vector128 LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count); - - /// - /// Stores four samples at one signed distance across the edge. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The signed sample distance across the edge. - /// The four widened samples ordered along the edge. - /// The number of low lanes to store. - public static abstract void StoreVector( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int distance, - Vector128 value, - int count); - - /// - /// Loads one scalar sample at a signed distance across and an offset along the edge. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The signed sample distance across the edge. - /// The sample offset along the edge. - /// The selected sample. - public static abstract int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index); - - /// - /// Stores one scalar sample at a signed distance across and an offset along the edge. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The signed sample distance across the edge. - /// The sample offset along the edge. - /// The filtered sample. - public static abstract void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.VerticalEdgeOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.VerticalEdgeOperator.cs deleted file mode 100644 index e5dccfafb..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.VerticalEdgeOperator.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcDeblockingFilter -{ - /// - /// Accesses four rows across a vertical edge. - /// - private readonly struct VerticalEdgeOperator : IEdgeOperator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count) - { - if (count == 4) - { - return Vector128.Create( - (int)picture.GetRowSpan(plane, y)[x + distance], - picture.GetRowSpan(plane, y + 1)[x + distance], - picture.GetRowSpan(plane, y + 2)[x + distance], - picture.GetRowSpan(plane, y + 3)[x + distance]); - } - - // Subsampled chroma edges contain two samples. Zeroing the unused lanes keeps the vector path within - // the plane while allowing the shared kernel to operate on both valid samples in one instruction stream. - return Vector128.Create( - (int)picture.GetRowSpan(plane, y)[x + distance], - picture.GetRowSpan(plane, y + 1)[x + distance], - 0, - 0); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreVector( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int distance, - Vector128 value, - int count) - { - for (int index = 0; index < count; index++) - { - picture.GetRowSpan(plane, y + index)[x + distance] = (ushort)value.GetElement(index); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index) - => picture.GetRowSpan(plane, y + index)[x + distance]; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value) - => picture.GetRowSpan(plane, y + index)[x + distance] = (ushort)value; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs deleted file mode 100644 index 39ccc31d3..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Applies the HEVC luma and chroma deblocking kernels to four-sample edge segments. -/// -/// -/// Each 32-bit lane represents one position along an edge segment. Orientation-specific operators gather the samples -/// at a common signed distance across that edge; the luma and chroma masks and adjustments then execute lane-wise. -/// Loads with fewer than four valid positions populate only the low lanes, which the matching store writes without -/// touching samples beyond the picture boundary. -/// -internal static partial class HevcDeblockingFilter -{ - /// - /// Filters four rows crossing one vertical luma boundary. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The top sample Y coordinate. - /// The scaled discontinuity threshold. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - public static void FilterVerticalLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth) - => FilterLuma(picture, plane, x, y, beta, tc, partPNoFilter, partQNoFilter, bitDepth); - - /// - /// Filters four columns crossing one horizontal luma boundary. - /// - /// The reconstructed picture. - /// The component plane. - /// The left sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled discontinuity threshold. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - public static void FilterHorizontalLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth) - => FilterLuma(picture, plane, x, y, beta, tc, partPNoFilter, partQNoFilter, bitDepth); - - /// - /// Filters four rows crossing one vertical chroma boundary. - /// - /// The reconstructed picture. - /// The Cb or Cr component plane. - /// The first Q-side sample X coordinate. - /// The top sample Y coordinate. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - /// The number of samples in the edge segment. - public static void FilterVerticalChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count) - => FilterChroma(picture, plane, x, y, tc, partPNoFilter, partQNoFilter, bitDepth, count); - - /// - /// Filters four columns crossing one horizontal chroma boundary. - /// - /// The reconstructed picture. - /// The Cb or Cr component plane. - /// The left sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - /// The number of samples in the edge segment. - public static void FilterHorizontalChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count) - => FilterChroma(picture, plane, x, y, tc, partPNoFilter, partQNoFilter, bitDepth, count); - - /// - /// Applies the strong or weak luma kernel through one closed edge-orientation operator. - /// - /// The vertical or horizontal sample-access operator. - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled discontinuity threshold. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - private static void FilterLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth) - where TOperator : struct, IEdgeOperator - { - if (beta == 0) - { - return; - } - - int p2Start = TOperator.LoadScalar(picture, plane, x, y, -3, 0); - int p1Start = TOperator.LoadScalar(picture, plane, x, y, -2, 0); - int p0Start = TOperator.LoadScalar(picture, plane, x, y, -1, 0); - int q0Start = TOperator.LoadScalar(picture, plane, x, y, 0, 0); - int q1Start = TOperator.LoadScalar(picture, plane, x, y, 1, 0); - int q2Start = TOperator.LoadScalar(picture, plane, x, y, 2, 0); - int p2End = TOperator.LoadScalar(picture, plane, x, y, -3, 3); - int p1End = TOperator.LoadScalar(picture, plane, x, y, -2, 3); - int p0End = TOperator.LoadScalar(picture, plane, x, y, -1, 3); - int q0End = TOperator.LoadScalar(picture, plane, x, y, 0, 3); - int q1End = TOperator.LoadScalar(picture, plane, x, y, 1, 3); - int q2End = TOperator.LoadScalar(picture, plane, x, y, 2, 3); - int dpStart = Math.Abs(p2Start - (2 * p1Start) + p0Start); - int dqStart = Math.Abs(q0Start - (2 * q1Start) + q2Start); - int dpEnd = Math.Abs(p2End - (2 * p1End) + p0End); - int dqEnd = Math.Abs(q0End - (2 * q1End) + q2End); - int dp = dpStart + dpEnd; - int dq = dqStart + dqEnd; - int discontinuity = dp + dq; - if (discontinuity >= beta) - { - return; - } - - int sideThreshold = (beta + (beta >> 1)) >> 3; - bool filterSecondP = dp < sideThreshold; - bool filterSecondQ = dq < sideThreshold; - bool strong = UsesStrongFiltering(picture, plane, x, y, 0, 2 * (dpStart + dqStart), beta, tc) - && UsesStrongFiltering(picture, plane, x, y, 3, 2 * (dpEnd + dqEnd), beta, tc); - - if (!Vector128.IsHardwareAccelerated) - { - for (int index = 0; index < 4; index++) - { - FilterLumaScalar( - picture, - plane, - x, - y, - index, - tc, - strong, - partPNoFilter, - partQNoFilter, - tc * 10, - filterSecondP, - filterSecondQ, - bitDepth); - } - - return; - } - - Vector128 p3 = TOperator.LoadVector(picture, plane, x, y, -4, 4); - Vector128 p2 = TOperator.LoadVector(picture, plane, x, y, -3, 4); - Vector128 p1 = TOperator.LoadVector(picture, plane, x, y, -2, 4); - Vector128 p0 = TOperator.LoadVector(picture, plane, x, y, -1, 4); - Vector128 q0 = TOperator.LoadVector(picture, plane, x, y, 0, 4); - Vector128 q1 = TOperator.LoadVector(picture, plane, x, y, 1, 4); - Vector128 q2 = TOperator.LoadVector(picture, plane, x, y, 2, 4); - Vector128 q3 = TOperator.LoadVector(picture, plane, x, y, 3, 4); - - // Each Int32 lane is one row or column along the edge. The threshold decision is shared by all four lanes, - // while the filter arithmetic stays lane-local and exactly matches the scalar equations below. - if (strong) - { - Vector128 twiceTc = Vector128.Create(2 * tc); - Vector128 four = Vector128.Create(4); - Vector128 two = Vector128.Create(2); - Vector128 filteredP0 = Vector128.Clamp((p2 + (p1 * 2) + (p0 * 2) + (q0 * 2) + q1 + four) >> 3, p0 - twiceTc, p0 + twiceTc); - Vector128 filteredQ0 = Vector128.Clamp((p1 + (p0 * 2) + (q0 * 2) + (q1 * 2) + q2 + four) >> 3, q0 - twiceTc, q0 + twiceTc); - Vector128 filteredP1 = Vector128.Clamp((p2 + p1 + p0 + q0 + two) >> 2, p1 - twiceTc, p1 + twiceTc); - Vector128 filteredQ1 = Vector128.Clamp((p0 + q0 + q1 + q2 + two) >> 2, q1 - twiceTc, q1 + twiceTc); - Vector128 filteredP2 = Vector128.Clamp(((p3 * 2) + (p2 * 3) + p1 + p0 + q0 + four) >> 3, p2 - twiceTc, p2 + twiceTc); - Vector128 filteredQ2 = Vector128.Clamp((p0 + q0 + q1 + (q2 * 3) + (q3 * 2) + four) >> 3, q2 - twiceTc, q2 + twiceTc); - - TOperator.StoreVector(picture, plane, x, y, -3, partPNoFilter ? p2 : filteredP2, 4); - TOperator.StoreVector(picture, plane, x, y, -2, partPNoFilter ? p1 : filteredP1, 4); - TOperator.StoreVector(picture, plane, x, y, -1, partPNoFilter ? p0 : filteredP0, 4); - TOperator.StoreVector(picture, plane, x, y, 0, partQNoFilter ? q0 : filteredQ0, 4); - TOperator.StoreVector(picture, plane, x, y, 1, partQNoFilter ? q1 : filteredQ1, 4); - TOperator.StoreVector(picture, plane, x, y, 2, partQNoFilter ? q2 : filteredQ2, 4); - return; - } - - Vector128 primaryDifference = (q0 - p0) * 9; - Vector128 secondaryDifference = (q1 - p1) * 3; - Vector128 delta = (primaryDifference - secondaryDifference + Vector128.Create(8)) >> 4; - Vector128 filterMask = Vector128.LessThan(Vector128.Abs(delta), Vector128.Create(tc * 10)); - delta = Vector128.Clamp(delta, Vector128.Create(-tc), Vector128.Create(tc)); - Vector128 minimum = Vector128.Zero; - Vector128 maximum = Vector128.Create((1 << bitDepth) - 1); - Vector128 filteredP0Weak = Vector128.ConditionalSelect(filterMask, Vector128.Clamp(p0 + delta, minimum, maximum), p0); - Vector128 filteredQ0Weak = Vector128.ConditionalSelect(filterMask, Vector128.Clamp(q0 - delta, minimum, maximum), q0); - TOperator.StoreVector(picture, plane, x, y, -1, partPNoFilter ? p0 : filteredP0Weak, 4); - TOperator.StoreVector(picture, plane, x, y, 0, partQNoFilter ? q0 : filteredQ0Weak, 4); - - int halfTc = tc >> 1; - if (filterSecondP && !partPNoFilter) - { - Vector128 secondary = (((p2 + p0 + Vector128.One) >> 1) - p1 + delta) >> 1; - secondary = Vector128.Clamp(secondary, Vector128.Create(-halfTc), Vector128.Create(halfTc)); - Vector128 filtered = Vector128.ConditionalSelect(filterMask, Vector128.Clamp(p1 + secondary, minimum, maximum), p1); - TOperator.StoreVector(picture, plane, x, y, -2, filtered, 4); - } - - if (filterSecondQ && !partQNoFilter) - { - Vector128 secondary = (((q2 + q0 + Vector128.One) >> 1) - q1 - delta) >> 1; - secondary = Vector128.Clamp(secondary, Vector128.Create(-halfTc), Vector128.Create(halfTc)); - Vector128 filtered = Vector128.ConditionalSelect(filterMask, Vector128.Clamp(q1 + secondary, minimum, maximum), q1); - TOperator.StoreVector(picture, plane, x, y, 1, filtered, 4); - } - } - - /// - /// Applies the chroma kernel through one closed edge-orientation operator. - /// - /// The vertical or horizontal sample-access operator. - /// The reconstructed picture. - /// The Cb or Cr component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - /// The number of samples in the edge segment. - private static void FilterChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count) - where TOperator : struct, IEdgeOperator - { - if (tc == 0) - { - return; - } - - if (!Vector128.IsHardwareAccelerated) - { - int maximum = (1 << bitDepth) - 1; - for (int index = 0; index < count; index++) - { - int p1 = TOperator.LoadScalar(picture, plane, x, y, -2, index); - int p0 = TOperator.LoadScalar(picture, plane, x, y, -1, index); - int q0 = TOperator.LoadScalar(picture, plane, x, y, 0, index); - int q1 = TOperator.LoadScalar(picture, plane, x, y, 1, index); - int delta = Math.Clamp((((q0 - p0) << 2) + p1 - q1 + 4) >> 3, -tc, tc); - if (!partPNoFilter) - { - TOperator.StoreScalar(picture, plane, x, y, -1, index, Math.Clamp(p0 + delta, 0, maximum)); - } - - if (!partQNoFilter) - { - TOperator.StoreScalar(picture, plane, x, y, 0, index, Math.Clamp(q0 - delta, 0, maximum)); - } - } - - return; - } - - Vector128 p1Vector = TOperator.LoadVector(picture, plane, x, y, -2, count); - Vector128 p0Vector = TOperator.LoadVector(picture, plane, x, y, -1, count); - Vector128 q0Vector = TOperator.LoadVector(picture, plane, x, y, 0, count); - Vector128 q1Vector = TOperator.LoadVector(picture, plane, x, y, 1, count); - Vector128 deltaVector = (((q0Vector - p0Vector) * 4) + p1Vector - q1Vector + Vector128.Create(4)) >> 3; - deltaVector = Vector128.Clamp(deltaVector, Vector128.Create(-tc), Vector128.Create(tc)); - Vector128 minimum = Vector128.Zero; - Vector128 maximumVector = Vector128.Create((1 << bitDepth) - 1); - - if (!partPNoFilter) - { - TOperator.StoreVector(picture, plane, x, y, -1, Vector128.Clamp(p0Vector + deltaVector, minimum, maximumVector), count); - } - - if (!partQNoFilter) - { - TOperator.StoreVector(picture, plane, x, y, 0, Vector128.Clamp(q0Vector - deltaVector, minimum, maximumVector), count); - } - } - - /// - /// Applies the scalar luma equations to one sample along an edge. - /// - /// The vertical or horizontal sample-access operator. - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The sample offset along the edge. - /// The scaled clipping threshold. - /// Whether the strong six-sample filter is selected. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The weak-filter delta threshold. - /// Whether the second P-side sample is filtered. - /// Whether the second Q-side sample is filtered. - /// The component sample precision. - private static void FilterLumaScalar( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int index, - int tc, - bool strong, - bool partPNoFilter, - bool partQNoFilter, - int thresholdCut, - bool filterSecondP, - bool filterSecondQ, - int bitDepth) - where TOperator : struct, IEdgeOperator - { - int p3 = TOperator.LoadScalar(picture, plane, x, y, -4, index); - int p2 = TOperator.LoadScalar(picture, plane, x, y, -3, index); - int p1 = TOperator.LoadScalar(picture, plane, x, y, -2, index); - int p0 = TOperator.LoadScalar(picture, plane, x, y, -1, index); - int q0 = TOperator.LoadScalar(picture, plane, x, y, 0, index); - int q1 = TOperator.LoadScalar(picture, plane, x, y, 1, index); - int q2 = TOperator.LoadScalar(picture, plane, x, y, 2, index); - int q3 = TOperator.LoadScalar(picture, plane, x, y, 3, index); - if (strong) - { - if (!partPNoFilter) - { - int filteredP0 = Math.Clamp( - (p2 + (2 * p1) + (2 * p0) + (2 * q0) + q1 + 4) >> 3, - p0 - (2 * tc), - p0 + (2 * tc)); - - TOperator.StoreScalar(picture, plane, x, y, -1, index, filteredP0); - TOperator.StoreScalar(picture, plane, x, y, -2, index, Math.Clamp((p2 + p1 + p0 + q0 + 2) >> 2, p1 - (2 * tc), p1 + (2 * tc))); - TOperator.StoreScalar(picture, plane, x, y, -3, index, Math.Clamp(((2 * p3) + (3 * p2) + p1 + p0 + q0 + 4) >> 3, p2 - (2 * tc), p2 + (2 * tc))); - } - - if (!partQNoFilter) - { - int filteredQ0 = Math.Clamp( - (p1 + (2 * p0) + (2 * q0) + (2 * q1) + q2 + 4) >> 3, - q0 - (2 * tc), - q0 + (2 * tc)); - - TOperator.StoreScalar(picture, plane, x, y, 0, index, filteredQ0); - TOperator.StoreScalar(picture, plane, x, y, 1, index, Math.Clamp((p0 + q0 + q1 + q2 + 2) >> 2, q1 - (2 * tc), q1 + (2 * tc))); - TOperator.StoreScalar(picture, plane, x, y, 2, index, Math.Clamp((p0 + q0 + q1 + (3 * q2) + (2 * q3) + 4) >> 3, q2 - (2 * tc), q2 + (2 * tc))); - } - - return; - } - - int delta = ((9 * (q0 - p0)) - (3 * (q1 - p1)) + 8) >> 4; - if (Math.Abs(delta) >= thresholdCut) - { - return; - } - - delta = Math.Clamp(delta, -tc, tc); - int maximum = (1 << bitDepth) - 1; - if (!partPNoFilter) - { - TOperator.StoreScalar(picture, plane, x, y, -1, index, Math.Clamp(p0 + delta, 0, maximum)); - if (filterSecondP) - { - int secondary = (((p2 + p0 + 1) >> 1) - p1 + delta) >> 1; - secondary = Math.Clamp(secondary, -(tc >> 1), tc >> 1); - TOperator.StoreScalar(picture, plane, x, y, -2, index, Math.Clamp(p1 + secondary, 0, maximum)); - } - } - - if (!partQNoFilter) - { - TOperator.StoreScalar(picture, plane, x, y, 0, index, Math.Clamp(q0 - delta, 0, maximum)); - if (filterSecondQ) - { - int secondary = (((q2 + q0 + 1) >> 1) - q1 - delta) >> 1; - secondary = Math.Clamp(secondary, -(tc >> 1), tc >> 1); - TOperator.StoreScalar(picture, plane, x, y, 1, index, Math.Clamp(q1 + secondary, 0, maximum)); - } - } - } - - /// - /// Determines whether one endpoint satisfies the strong-filter conditions. - /// - /// The vertical or horizontal sample-access operator. - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The endpoint offset along the edge. - /// Twice the endpoint's second-derivative sum. - /// The scaled discontinuity threshold. - /// The scaled clipping threshold. - /// when strong filtering is permitted; otherwise, . - private static bool UsesStrongFiltering( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int index, - int discontinuity, - int beta, - int tc) - where TOperator : struct, IEdgeOperator - { - int p3 = TOperator.LoadScalar(picture, plane, x, y, -4, index); - int p0 = TOperator.LoadScalar(picture, plane, x, y, -1, index); - int q0 = TOperator.LoadScalar(picture, plane, x, y, 0, index); - int q3 = TOperator.LoadScalar(picture, plane, x, y, 3, index); - int strongDiscontinuity = Math.Abs(p3 - p0) + Math.Abs(q3 - q0); - int strongThreshold = ((5 * tc) + 1) >> 1; - return strongDiscontinuity < (beta >> 3) - && discontinuity < (beta >> 2) - && Math.Abs(p0 - q0) < strongThreshold; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingState.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingState.cs deleted file mode 100644 index 37deb0664..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingState.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Tracks luma transform and prediction boundaries at the four-sample resolution used to derive HEVC deblocking edges. -/// -internal sealed class HevcDeblockingState : IDisposable -{ - /// - /// The base-two logarithm of the boundary-map unit side. - /// - private const int UnitLog2 = 2; - - /// - /// The packed flag identifying a vertical boundary at a unit's left edge. - /// - private const byte VerticalBoundary = 1 << 0; - - /// - /// The packed flag identifying a horizontal boundary at a unit's top edge. - /// - private const byte HorizontalBoundary = 1 << 1; - - /// - /// The boundary maps for the primary plane of combined coding or each independently coded color plane. - /// - private readonly Buffer2D[] boundaries; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing the image memory allocator. - /// The coded picture dimensions. - public HevcDeblockingState(Configuration configuration, HevcSequenceParameterSet sequenceParameterSet) - { - int width = DivideCeilingByPowerOfTwo(sequenceParameterSet.Width, UnitLog2); - int height = DivideCeilingByPowerOfTwo(sequenceParameterSet.Height, UnitLog2); - - Buffer2D? lumaBoundaries = null; - Buffer2D? chromaBlueBoundaries = null; - Buffer2D? chromaRedBoundaries = null; - try - { - // MarkBlock combines sparse edge flags with existing values, so zero initialization is part of the state contract. - lumaBoundaries = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); - chromaBlueBoundaries = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); - chromaRedBoundaries = configuration.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); - this.boundaries = [lumaBoundaries, chromaBlueBoundaries, chromaRedBoundaries]; - } - catch - { - chromaRedBoundaries?.Dispose(); - chromaBlueBoundaries?.Dispose(); - lumaBoundaries?.Dispose(); - throw; - } - } - - /// - /// Records the left and top edges of one leaf transform or pulse-code-modulated coding block. - /// - /// The primary coding plane. - /// The block left coordinate in full-resolution primary-plane samples. - /// The block top coordinate in full-resolution primary-plane samples. - /// The block width in samples. - /// The block height in samples. - public void MarkBlock(HevcPlane plane, int x, int y, int width, int height) - { - Buffer2D map = this.boundaries[(int)plane]; - int unitX = x >> UnitLog2; - int unitY = y >> UnitLog2; - int endX = Math.Min(DivideCeilingByPowerOfTwo(x + width, UnitLog2), map.Width); - int endY = Math.Min(DivideCeilingByPowerOfTwo(y + height, UnitLog2), map.Height); - - // A transform boundary covers every four-sample segment along its edge. Packing both orientations into one - // byte keeps the decoder state contiguous and lets the later eight-sample deblocking traversal reject edges cheaply. - for (int row = unitY; row < endY; row++) - { - map.DangerousGetRowSpan(row)[unitX] |= VerticalBoundary; - } - - Span top = map.DangerousGetRowSpan(unitY); - for (int column = unitX; column < endX; column++) - { - top[column] |= HorizontalBoundary; - } - } - - /// - /// Gets whether a four-sample segment begins at a vertical transform or prediction boundary. - /// - /// The primary coding plane. - /// The segment left coordinate in full-resolution primary-plane samples. - /// The segment top coordinate in full-resolution primary-plane samples. - /// when the segment is a vertical boundary; otherwise, . - public bool IsVerticalBoundary(HevcPlane plane, int x, int y) - => (this.boundaries[(int)plane].DangerousGetRowSpan(y >> UnitLog2)[x >> UnitLog2] & VerticalBoundary) != 0; - - /// - /// Gets whether a four-sample segment begins at a horizontal transform or prediction boundary. - /// - /// The primary coding plane. - /// The segment left coordinate in full-resolution primary-plane samples. - /// The segment top coordinate in full-resolution primary-plane samples. - /// when the segment is a horizontal boundary; otherwise, . - public bool IsHorizontalBoundary(HevcPlane plane, int x, int y) - => (this.boundaries[(int)plane].DangerousGetRowSpan(y >> UnitLog2)[x >> UnitLog2] & HorizontalBoundary) != 0; - - /// - /// Releases the allocator-owned boundary maps. - /// - public void Dispose() - { - foreach (Buffer2D map in this.boundaries) - { - map.Dispose(); - } - } - - /// - /// Divides a nonnegative sample count by a power of two with upward rounding. - /// - /// The sample count. - /// The base-two divisor logarithm. - /// The upward-rounded quotient. - private static int DivideCeilingByPowerOfTwo(int value, int shift) => (value + (1 << shift) - 1) >> shift; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcImageItemBitstream.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcImageItemBitstream.cs deleted file mode 100644 index 03ac96fb0..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcImageItemBitstream.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the length-delimited NAL units and IDR slice segments carried by one HEVC still-image item. -/// -internal sealed class HevcImageItemBitstream -{ - /// - /// Initializes a new instance of the class. - /// - /// The complete bounded payload of one hvc1 image item. - /// The codec configuration associated with the same image item. - /// - /// NAL-unit framing is malformed, the payload contains sequence or layered coding, or the item does not contain - /// exactly one independently decodable IDR picture. - /// - public HevcImageItemBitstream(ReadOnlySpan data, HevcCodecConfiguration configuration) - { - List nalUnits = []; - List sliceSegments = []; - HevcSupplementalEnhancementInformation supplementalEnhancementInformation = new(); - int offset = 0; - while (offset < data.Length) - { - if (data.Length - offset < configuration.NalUnitLengthSize) - { - throw new InvalidImageContentException("The HEVC image item has a truncated NAL-unit length."); - } - - int nalUnitLength = ReadNalUnitLength(data[offset..], configuration.NalUnitLengthSize); - offset += configuration.NalUnitLengthSize; - if (nalUnitLength < 2 || nalUnitLength > data.Length - offset) - { - throw new InvalidImageContentException("The HEVC image item has an invalid NAL-unit length."); - } - - HevcNalUnit nalUnit = new(data.Slice(offset, nalUnitLength)); - offset += nalUnitLength; - if (nalUnit.Header.LayerId != 0 || nalUnit.Header.TemporalId != 0) - { - throw new InvalidImageContentException("The HEVC image item contains layered or temporal-substream NAL units."); - } - - nalUnits.Add(nalUnit); - if (nalUnit.Header.IsVideoCodingLayer) - { - if (!nalUnit.Header.IsInstantaneousDecoderRefresh) - { - throw new InvalidImageContentException("The HEVC image item contains a coded picture that is not independently decodable."); - } - - HevcSliceSegmentHeader sliceSegment = new(nalUnit, configuration.PictureParameterSets); - if (sliceSegments.Count == 0 && !sliceSegment.FirstSliceSegmentInPicture) - { - throw new InvalidImageContentException("The first HEVC image-item slice is not marked as the first picture segment."); - } - - if (sliceSegments.Count != 0 && sliceSegment.FirstSliceSegmentInPicture) - { - throw new InvalidImageContentException("The HEVC image item contains more than one coded picture."); - } - - sliceSegments.Add(sliceSegment); - continue; - } - - if (nalUnit.Header.NalUnitType is 32 or 33 or 34) - { - // hvc1 image items obtain all parameter sets from the associated hvcC property. Accepting in-band - // replacements would silently apply the more permissive hev1 sample contract to this still image. - throw new InvalidImageContentException("The HEVC hvc1 image item contains an in-band parameter set."); - } - - if (nalUnit.Header.NalUnitType is 36 or 37) - { - throw new InvalidImageContentException("The HEVC image item contains an end-of-sequence NAL unit."); - } - - if (nalUnit.Header.NalUnitType == 39) - { - // Prefix SEI belongs to the following VCL NAL unit. Once this bounded item has started its only - // picture, another prefix unit would describe a second access unit that the item is not allowed to carry. - if (sliceSegments.Count != 0) - { - throw new InvalidImageContentException("The HEVC image item contains prefix SEI after its first coded slice."); - } - - // Prefix SEI messages are associated with this item's only access unit. Parse the observable still-image - // state in NAL and message order without retaining generic video persistence or timing state. - supplementalEnhancementInformation.ReadPrefixNalUnit(nalUnit.Rbsp.Span); - } - } - - if (sliceSegments.Count == 0) - { - throw new InvalidImageContentException("The HEVC image item contains no independently decodable picture."); - } - - this.NalUnits = nalUnits; - this.SliceSegments = sliceSegments; - this.SupplementalEnhancementInformation = supplementalEnhancementInformation; - } - - /// - /// Gets every decoded NAL unit in item order, including permitted delimiter, filler, and supplemental units. - /// - public IReadOnlyList NalUnits { get; } - - /// - /// Gets the ordered slice segments that reconstruct the item's single IDR picture. - /// - public IReadOnlyList SliceSegments { get; } - - /// - /// Gets the presentation and exposed metadata decoded from prefix SEI NAL units. - /// - public HevcSupplementalEnhancementInformation SupplementalEnhancementInformation { get; } - - /// - /// Reads an unsigned one-through-four-byte NAL-unit length without assuming four-byte item framing. - /// - /// The item bytes beginning at the length field. - /// The codec-configuration-selected length-field width. - /// The bounded signed integer NAL-unit length. - /// The unsigned length exceeds the supported item-buffer range. - private static int ReadNalUnitLength(ReadOnlySpan data, int lengthSize) - { - uint value = 0; - for (int byteIndex = 0; byteIndex < lengthSize; byteIndex++) - { - value = (value << 8) | data[byteIndex]; - } - - if (value > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC image-item NAL-unit length is too large."); - } - - return (int)value; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionMode.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionMode.cs deleted file mode 100644 index 8dc56dad0..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionMode.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Provides the HEVC intra-mode values and chroma-format mapping shared by entropy decoding and reconstruction. -/// -internal static class HevcIntraPredictionMode -{ - /// - /// The horizontal angular prediction mode. - /// - public const int Horizontal = 10; - - /// - /// The vertical angular prediction mode. - /// - public const int Vertical = 26; - - /// - /// Gets the 4:2:2 chroma intra-angle remapping defined by H.265 Table 8-4. - /// - private static ReadOnlySpan Chroma422AngleMap => - [ - 0, 1, 2, 2, 2, 2, 3, 5, 7, 8, 10, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 28, 29, 29, 30, 31, - ]; - - /// - /// Maps a coded chroma intra mode to the angular mode used by a 4:2:2 chroma block. - /// - /// The effective coded chroma intra mode. - /// The prediction angle used by the rectangular chroma block. - public static int RemapChroma422(int mode) => Chroma422AngleMap[mode]; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs deleted file mode 100644 index e71e80db7..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Stores and decodes the luma and chroma intra prediction modes for one HEVC still picture. -/// -internal sealed class HevcIntraPredictionState : IDisposable -{ - /// - /// The base-two logarithm of the minimum luma prediction-block size. - /// - private const int MinPredictionBlockLog2 = 2; - - /// - /// The planar intra prediction mode. - /// - private const byte PlanarMode = 0; - - /// - /// The DC intra prediction mode. - /// - private const byte DcMode = 1; - - /// - /// The horizontal intra prediction mode. - /// - private const byte HorizontalMode = 10; - - /// - /// The vertical intra prediction mode. - /// - private const byte VerticalMode = 26; - - /// - /// The replacement chroma mode used when an explicit chroma candidate equals the luma mode. - /// - private const byte ChromaReplacementMode = 34; - - /// - /// The chroma mode that derives its direction from the colocated luma prediction block. - /// - private const byte DerivedChromaMode = 36; - - /// - /// The luma intra mode at minimum-prediction-block resolution. - /// - private readonly Buffer2D lumaModes; - - /// - /// The coded chroma intra mode at minimum-prediction-block resolution in luma coordinates. - /// - private readonly Buffer2D chromaModes; - - /// - /// The resolved chroma intra mode at minimum-prediction-block resolution in luma coordinates. - /// - private readonly Buffer2D effectiveChromaModes; - - /// - /// Whether derived chroma prediction selects the colocated luma prediction block. - /// - private readonly bool derivedChromaUsesColocatedLuma; - - /// - /// The mask selecting a luma coordinate within its coding-tree block. - /// - private readonly int codingTreeBlockMask; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing the image memory allocator. - /// The coded luma picture dimensions. - public HevcIntraPredictionState(Configuration configuration, HevcSequenceParameterSet sequenceParameterSet) - { - this.WidthInMinPredictionBlocks = DivideCeilingByPowerOfTwo( - sequenceParameterSet.Width, - MinPredictionBlockLog2); - - this.HeightInMinPredictionBlocks = DivideCeilingByPowerOfTwo( - sequenceParameterSet.Height, - MinPredictionBlockLog2); - - Buffer2D? lumaModes = null; - Buffer2D? chromaModes = null; - Buffer2D? effectiveChromaModes = null; - try - { - lumaModes = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinPredictionBlocks, - this.HeightInMinPredictionBlocks); - - chromaModes = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinPredictionBlocks, - this.HeightInMinPredictionBlocks); - - effectiveChromaModes = configuration.MemoryAllocator.Allocate2D( - this.WidthInMinPredictionBlocks, - this.HeightInMinPredictionBlocks); - - // PCM coding units skip intra-mode syntax but remain available as most-probable-mode neighbors. HM - // initializes every luma direction to DC so those units provide the required default until syntax replaces it. - for (int row = 0; row < this.HeightInMinPredictionBlocks; row++) - { - lumaModes.DangerousGetRowSpan(row).Fill(DcMode); - } - - this.lumaModes = lumaModes; - this.chromaModes = chromaModes; - this.effectiveChromaModes = effectiveChromaModes; - } - catch - { - effectiveChromaModes?.Dispose(); - chromaModes?.Dispose(); - lumaModes?.Dispose(); - throw; - } - - this.derivedChromaUsesColocatedLuma = sequenceParameterSet.ChromaFormat == 3; - this.codingTreeBlockMask = (1 << sequenceParameterSet.CodingTreeBlockLog2) - 1; - } - - /// - /// Gets the map width in minimum luma prediction blocks. - /// - public int WidthInMinPredictionBlocks { get; } - - /// - /// Gets the map height in minimum luma prediction blocks. - /// - public int HeightInMinPredictionBlocks { get; } - - /// - /// Decodes and records the luma intra modes of one leaf coding unit. - /// - /// The current entropy-substream syntax reader. - /// The coding-unit left coordinate in luma samples. - /// The coding-unit top coordinate in luma samples. - /// The base-two logarithm of the square coding-unit size. - /// A value indicating whether the coding unit has four square prediction blocks. - /// A value indicating whether the external left prediction block is available. - /// A value indicating whether the external above prediction block is available. - public void DecodeLumaModes( - ref HevcCabacSyntaxReader reader, - int x, - int y, - int log2Size, - bool usesNxNPartitions, - bool leftAvailable, - bool aboveAvailable) - { - int predictionBlockLog2 = usesNxNPartitions ? log2Size - 1 : log2Size; - int predictionBlockSize = 1 << predictionBlockLog2; - int predictionBlockCount = usesNxNPartitions ? 4 : 1; - InlineArray4 mostProbableFlags = default; - - // HEVC codes every prev_intra_luma_pred_flag before any associated mode suffix. Preserve that two-pass - // ordering because decoding one complete mode at a time would consume a different CABAC bit sequence. - for (int index = 0; index < predictionBlockCount; index++) - { - mostProbableFlags[index] = reader.ReadPreviousIntraLumaPredictionFlag() ? (byte)1 : (byte)0; - } - - InlineArray4 mostProbableModes = default; - Span mostProbableModeSpan = mostProbableModes[..3]; - - for (int index = 0; index < predictionBlockCount; index++) - { - int offsetX = (index & 1) * predictionBlockSize; - int offsetY = (index >> 1) * predictionBlockSize; - int predictionX = x + offsetX; - int predictionY = y + offsetY; - bool predictionLeftAvailable = offsetX != 0 || leftAvailable; - - // Luma MPM derivation treats an above prediction unit across a CTB boundary as unavailable. This is - // narrower than sample reconstruction availability and keeps the candidate order synchronized with CABAC. - bool predictionAboveAvailable = (predictionY & this.codingTreeBlockMask) != 0 && (offsetY != 0 || aboveAvailable); - - this.GetMostProbableLumaModes( - predictionX, - predictionY, - predictionLeftAvailable, - predictionAboveAvailable, - mostProbableModeSpan); - - int mode; - if (mostProbableFlags[index] != 0) - { - mode = mostProbableModeSpan[reader.ReadMostProbableIntraLumaPredictionIndex()]; - } - else - { - SortThree(mostProbableModeSpan); - mode = reader.ReadRemainingIntraLumaPredictionMode(); - for (int candidate = 0; candidate < mostProbableModeSpan.Length; candidate++) - { - // The remaining-mode code omits the three probable values, so each candidate at or below the - // provisional result advances the decoded mode over that omitted slot. - mode += mode >= mostProbableModeSpan[candidate] ? 1 : 0; - } - } - - this.SetMode(this.lumaModes, predictionX, predictionY, predictionBlockLog2, (byte)mode); - } - } - - /// - /// Decodes and records the chroma intra modes of one leaf coding unit. - /// - /// The current entropy-substream syntax reader. - /// The coding-unit left coordinate in luma samples. - /// The coding-unit top coordinate in luma samples. - /// The base-two logarithm of the square coding-unit size. - /// Whether the coding unit contains four luma prediction units. - public void DecodeChromaModes(ref HevcCabacSyntaxReader reader, int x, int y, int log2Size, bool usesNxNPartitions) - { - bool usesFourChromaPredictionUnits = this.derivedChromaUsesColocatedLuma && usesNxNPartitions; - int predictionBlockLog2 = usesFourChromaPredictionUnits ? log2Size - 1 : log2Size; - int predictionBlockSize = 1 << predictionBlockLog2; - int predictionBlockCount = usesFourChromaPredictionUnits ? 4 : 1; - for (int index = 0; index < predictionBlockCount; index++) - { - int predictionX = x + ((index & 1) * predictionBlockSize); - int predictionY = y + ((index >> 1) * predictionBlockSize); - int selector = reader.ReadChromaPredictionModeIndex(); - byte mode; - if (selector < 0) - { - mode = DerivedChromaMode; - } - else - { - ReadOnlySpan candidates = [PlanarMode, VerticalMode, HorizontalMode, DcMode]; - mode = candidates[selector]; - if (mode == this.GetLumaMode(predictionX, predictionY)) - { - mode = ChromaReplacementMode; - } - } - - // Combined 4:4:4 follows the four luma prediction partitions of an NxN coding unit. Subsampled formats - // carry one chroma mode for the coding unit and derive it from the top-left luma partition when requested. - this.SetMode(this.chromaModes, predictionX, predictionY, predictionBlockLog2, mode); - byte effectiveMode = mode == DerivedChromaMode ? this.GetLumaMode(predictionX, predictionY) : mode; - this.SetMode(this.effectiveChromaModes, predictionX, predictionY, predictionBlockLog2, effectiveMode); - } - } - - /// - /// Gets the luma intra mode at a luma sample coordinate. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// The luma intra mode in the inclusive range zero through thirty-four. - public byte GetLumaMode(int x, int y) - => this.lumaModes.DangerousGetRowSpan(y >> MinPredictionBlockLog2)[x >> MinPredictionBlockLog2]; - - /// - /// Gets the coded chroma intra mode at a luma sample coordinate. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// An explicit chroma direction or the derived-mode value. - public byte GetChromaMode(int x, int y) - => this.chromaModes.DangerousGetRowSpan(y >> MinPredictionBlockLog2)[x >> MinPredictionBlockLog2]; - - /// - /// Gets the effective chroma intra mode at a luma sample coordinate. - /// - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// The explicit chroma mode, or the colocated luma mode when chroma uses derived mode. - public byte GetEffectiveChromaMode(int x, int y) - => this.effectiveChromaModes.DangerousGetRowSpan(y >> MinPredictionBlockLog2)[x >> MinPredictionBlockLog2]; - - /// - /// Releases the owned intra-mode maps. - /// - public void Dispose() - { - this.lumaModes.Dispose(); - this.chromaModes.Dispose(); - this.effectiveChromaModes.Dispose(); - } - - /// - /// Derives the three most-probable luma intra modes from available spatial neighbors. - /// - /// The prediction-block left coordinate in luma samples. - /// The prediction-block top coordinate in luma samples. - /// A value indicating whether the left prediction block is available. - /// A value indicating whether the above prediction block is available. - /// The three-element destination span. - private void GetMostProbableLumaModes( - int x, - int y, - bool leftAvailable, - bool aboveAvailable, - Span modes) - { - byte leftMode = leftAvailable ? this.GetLumaMode(x - 1, y) : DcMode; - byte aboveMode = aboveAvailable ? this.GetLumaMode(x, y - 1) : DcMode; - if (leftMode == aboveMode) - { - if (leftMode > DcMode) - { - modes[0] = leftMode; - modes[1] = (byte)(((leftMode + 29) % 32) + 2); - modes[2] = (byte)(((leftMode - 1) % 32) + 2); - } - else - { - modes[0] = PlanarMode; - modes[1] = DcMode; - modes[2] = VerticalMode; - } - - return; - } - - modes[0] = leftMode; - modes[1] = aboveMode; - if (leftMode != PlanarMode && aboveMode != PlanarMode) - { - modes[2] = PlanarMode; - } - else - { - modes[2] = leftMode + aboveMode < 2 ? VerticalMode : DcMode; - } - } - - /// - /// Records one prediction mode over a square luma-coordinate region. - /// - /// The luma or chroma mode map. - /// The region left coordinate in luma samples. - /// The region top coordinate in luma samples. - /// The base-two logarithm of the square region size. - /// The prediction mode. - private void SetMode(Buffer2D map, int x, int y, int log2Size, byte mode) - { - int unitX = x >> MinPredictionBlockLog2; - int unitY = y >> MinPredictionBlockLog2; - int unitCount = 1 << (log2Size - MinPredictionBlockLog2); - int endX = Math.Min(unitX + unitCount, this.WidthInMinPredictionBlocks); - int endY = Math.Min(unitY + unitCount, this.HeightInMinPredictionBlocks); - for (int row = unitY; row < endY; row++) - { - map.DangerousGetRowSpan(row)[unitX..endX].Fill(mode); - } - } - - /// - /// Sorts three intra-mode values into ascending order. - /// - /// The three-element mode span. - private static void SortThree(Span values) - { - if (values[0] > values[1]) - { - byte value = values[0]; - values[0] = values[1]; - values[1] = value; - } - - if (values[0] > values[2]) - { - byte value = values[0]; - values[0] = values[2]; - values[2] = value; - } - - if (values[1] > values[2]) - { - byte value = values[1]; - values[1] = values[2]; - values[2] = value; - } - } - - /// - /// Divides a nonnegative sample count by a power of two with upward rounding. - /// - /// The sample count. - /// The base-two divisor logarithm. - /// The upward-rounded quotient. - private static int DivideCeilingByPowerOfTwo(int value, int shift) => (value + (1 << shift) - 1) >> shift; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.AngularOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.AngularOperator.cs deleted file mode 100644 index e4ea8a0f1..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.AngularOperator.cs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines angular intra-prediction arithmetic. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// Implements the thirty-three directional intra-prediction modes. - /// - private readonly struct AngularOperator : IHevcIntraPredictionOperator - { - /// - public static void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch) - { - if (mode == VerticalMode) - { - PredictVertical(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); - return; - } - - if (mode == HorizontalMode) - { - PredictHorizontal(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); - return; - } - - bool vertical = mode >= FirstVerticalMode; - int angleMode = vertical ? mode - VerticalMode : HorizontalMode - mode; - int absoluteAngleMode = Math.Abs(angleMode); - int angle = PredictionAngles[absoluteAngleMode] * Math.Sign(angleMode); - ReadOnlySpan main = vertical ? top : left; - ReadOnlySpan side = vertical ? left : top; - Span temporaryBlock = scratch[..(size * size)]; - Span extendedReference = scratch.Slice(size * size, (4 * size) + 1); - int mainOrigin = 0; - - if (angle < 0) - { - mainOrigin = size * 2; - main[..(size + 1)].CopyTo(extendedReference[mainOrigin..]); - int inverseAngle = InversePredictionAngles[absoluteAngleMode]; - int inverseAngleSum = 128; - int minimumIndex = (size * angle) >> 5; - for (int index = -1; index > minimumIndex; index--) - { - inverseAngleSum += inverseAngle; - extendedReference[mainOrigin + index] = side[inverseAngleSum >> 8]; - } - - main = extendedReference; - } - - Span prediction = vertical ? destination : temporaryBlock; - int predictionStride = vertical ? destinationStride : size; - PredictAngularRows(main, mainOrigin, prediction, predictionStride, size, angle); - if (!vertical) - { - TransposeBlock(temporaryBlock, destination, destinationStride, size); - } - } - - /// - /// Copies the top reference into every row and optionally filters the first column. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The reconstructed component precision. - /// Whether the vertical luma edge filter applies. - private static void PredictVertical( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int bitDepth, - bool filterPredictionEdges) - { - ReadOnlySpan row = top.Slice(1, size); - int maximum = (1 << bitDepth) - 1; - for (int y = 0; y < size; y++) - { - row.CopyTo(destination.Slice(y * destinationStride, size)); - if (filterPredictionEdges) - { - int sample = destination[y * destinationStride] + ((left[y + 1] - left[0]) >> 1); - destination[y * destinationStride] = (ushort)Math.Clamp(sample, 0, maximum); - } - } - } - - /// - /// Fills each row from its left reference and optionally filters the first row. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The reconstructed component precision. - /// Whether the horizontal luma edge filter applies. - private static void PredictHorizontal( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int bitDepth, - bool filterPredictionEdges) - { - for (int y = 0; y < size; y++) - { - destination.Slice(y * destinationStride, size).Fill(left[y + 1]); - } - - if (!filterPredictionEdges) - { - return; - } - - int maximum = (1 << bitDepth) - 1; - for (int x = 0; x < size; x++) - { - int sample = destination[x] + ((top[x + 1] - top[0]) >> 1); - destination[x] = (ushort)Math.Clamp(sample, 0, maximum); - } - } - - /// - /// Generates a vertical-oriented angular block using contiguous SIMD interpolation within each row. - /// - /// The main reference beginning at logical index zero. - /// The span index corresponding to logical reference index zero. - /// The contiguous destination or transposition scratch block. - /// The destination row stride. - /// The square block side. - /// The signed prediction displacement in thirty-second-sample units. - private static void PredictAngularRows( - ReadOnlySpan main, - int mainOrigin, - Span destination, - int destinationStride, - int size, - int angle) - { - for (int y = 0, deltaPosition = angle; y < size; y++, deltaPosition += angle) - { - int deltaInteger = deltaPosition >> 5; - int deltaFraction = deltaPosition & 31; - int sourceOffset = mainOrigin + deltaInteger + 1; - Span row = destination.Slice(y * destinationStride, size); - if (deltaFraction == 0) - { - main.Slice(sourceOffset, size).CopyTo(row); - } - else - { - InterpolateAngularRow(main[sourceOffset..], row, deltaFraction); - } - } - } - - /// - /// Interpolates one angular prediction row between consecutive main-reference samples. - /// - /// The first main-reference sample for the row. - /// The destination prediction row. - /// The right-hand weight with a denominator of thirty-two. - private static void InterpolateAngularRow(ReadOnlySpan source, Span destination, int fraction) - { - ref ushort sourceBase = ref MemoryMarshal.GetReference(source); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - uint leftWeight = (uint)(32 - fraction); - uint rightWeight = (uint)fraction; - int i = 0; - - // Adjacent source vectors overlap by one sample, aligning each left/right reference pair in the same lane. - // Widening keeps the largest 12-bit Q5 weighted sum below the UInt32 limit before narrowing to sample storage. - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = destination.Length - Vector512.Count; - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - Vector512 left = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); - Vector512 right = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector512 leftLow, Vector512 leftHigh) = Vector512.Widen(left); - (Vector512 rightLow, Vector512 rightHigh) = Vector512.Widen(right); - Vector512 low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector512.Create(16U)) >> 5; - Vector512 high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector512.Create(16U)) >> 5; - Vector512.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = destination.Length - Vector256.Count; - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - Vector256 left = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); - Vector256 right = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector256 leftLow, Vector256 leftHigh) = Vector256.Widen(left); - (Vector256 rightLow, Vector256 rightHigh) = Vector256.Widen(right); - Vector256 low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector256.Create(16U)) >> 5; - Vector256 high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector256.Create(16U)) >> 5; - Vector256.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = destination.Length - Vector128.Count; - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - Vector128 left = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); - Vector128 right = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector128 leftLow, Vector128 leftHigh) = Vector128.Widen(left); - (Vector128 rightLow, Vector128 rightHigh) = Vector128.Widen(right); - Vector128 low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector128.Create(16U)) >> 5; - Vector128 high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector128.Create(16U)) >> 5; - Vector128.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - for (; i < destination.Length; i++) - { - Unsafe.Add(ref destinationBase, i) = (ushort)(((source[i] * leftWeight) + (source[i + 1] * rightWeight) + 16) >> 5); - } - } - - /// - /// Transposes a square horizontal prediction block into the reconstructed destination. - /// - /// The contiguous transposed prediction block. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - private static void TransposeBlock(ReadOnlySpan source, Span destination, int destinationStride, int size) - { - if (Vector128.IsHardwareAccelerated && size >= Vector128.Count) - { - for (int y = 0; y < size; y += Vector128.Count) - { - for (int x = 0; x < size; x += Vector128.Count) - { - Transpose8x8(source, destination, destinationStride, size, x, y); - } - } - - return; - } - - for (int y = 0; y < size; y++) - { - for (int x = 0; x < size; x++) - { - destination[(x * destinationStride) + y] = source[(y * size) + x]; - } - } - } - - /// - /// Transposes one eight-by-eight tile of 16-bit prediction samples. - /// - /// The contiguous source block. - /// The destination block origin. - /// The destination row stride. - /// The contiguous source row stride. - /// The tile X coordinate in the source block. - /// The tile Y coordinate in the source block. - private static void Transpose8x8( - ReadOnlySpan source, - Span destination, - int destinationStride, - int sourceStride, - int x, - int y) - { - ref ushort sourceBase = ref MemoryMarshal.GetReference(source); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - Vector128 row0 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 0) * sourceStride) + x)).AsInt16(); - Vector128 row1 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 1) * sourceStride) + x)).AsInt16(); - Vector128 row2 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 2) * sourceStride) + x)).AsInt16(); - Vector128 row3 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 3) * sourceStride) + x)).AsInt16(); - Vector128 row4 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 4) * sourceStride) + x)).AsInt16(); - Vector128 row5 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 5) * sourceStride) + x)).AsInt16(); - Vector128 row6 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 6) * sourceStride) + x)).AsInt16(); - Vector128 row7 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 7) * sourceStride) + x)).AsInt16(); - - // Three zip stages exchange one, two, then four 16-bit coordinates. The resulting vectors are the eight - // source columns in row order, so each can be stored contiguously into one destination row. - Vector128 pair0 = Vector128_.UnpackLow(row0, row1); - Vector128 pair1 = Vector128_.UnpackHigh(row0, row1); - Vector128 pair2 = Vector128_.UnpackLow(row2, row3); - Vector128 pair3 = Vector128_.UnpackHigh(row2, row3); - Vector128 pair4 = Vector128_.UnpackLow(row4, row5); - Vector128 pair5 = Vector128_.UnpackHigh(row4, row5); - Vector128 pair6 = Vector128_.UnpackLow(row6, row7); - Vector128 pair7 = Vector128_.UnpackHigh(row6, row7); - Vector128 quad0 = Vector128_.UnpackLow(pair0.AsInt32(), pair2.AsInt32()); - Vector128 quad1 = Vector128_.UnpackHigh(pair0.AsInt32(), pair2.AsInt32()); - Vector128 quad2 = Vector128_.UnpackLow(pair1.AsInt32(), pair3.AsInt32()); - Vector128 quad3 = Vector128_.UnpackHigh(pair1.AsInt32(), pair3.AsInt32()); - Vector128 quad4 = Vector128_.UnpackLow(pair4.AsInt32(), pair6.AsInt32()); - Vector128 quad5 = Vector128_.UnpackHigh(pair4.AsInt32(), pair6.AsInt32()); - Vector128 quad6 = Vector128_.UnpackLow(pair5.AsInt32(), pair7.AsInt32()); - Vector128 quad7 = Vector128_.UnpackHigh(pair5.AsInt32(), pair7.AsInt32()); - Vector128 column0 = Vector128_.UnpackLow(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); - Vector128 column1 = Vector128_.UnpackHigh(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); - Vector128 column2 = Vector128_.UnpackLow(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); - Vector128 column3 = Vector128_.UnpackHigh(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); - Vector128 column4 = Vector128_.UnpackLow(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); - Vector128 column5 = Vector128_.UnpackHigh(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); - Vector128 column6 = Vector128_.UnpackLow(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); - Vector128 column7 = Vector128_.UnpackHigh(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); - column0.StoreUnsafe(ref destinationBase, (nuint)(((x + 0) * destinationStride) + y)); - column1.StoreUnsafe(ref destinationBase, (nuint)(((x + 1) * destinationStride) + y)); - column2.StoreUnsafe(ref destinationBase, (nuint)(((x + 2) * destinationStride) + y)); - column3.StoreUnsafe(ref destinationBase, (nuint)(((x + 3) * destinationStride) + y)); - column4.StoreUnsafe(ref destinationBase, (nuint)(((x + 4) * destinationStride) + y)); - column5.StoreUnsafe(ref destinationBase, (nuint)(((x + 5) * destinationStride) + y)); - column6.StoreUnsafe(ref destinationBase, (nuint)(((x + 6) * destinationStride) + y)); - column7.StoreUnsafe(ref destinationBase, (nuint)(((x + 7) * destinationStride) + y)); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.DcOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.DcOperator.cs deleted file mode 100644 index 02fe02def..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.DcOperator.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines DC intra-prediction arithmetic. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// Implements DC prediction and its optional luma boundary filter. - /// - private readonly struct DcOperator : IHevcIntraPredictionOperator - { - /// - public static void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch) - { - uint sum = SumSamples(top.Slice(1, size)) + SumSamples(left.Slice(1, size)); - ushort dc = (ushort)((sum + (uint)size) >> (BitOperations.Log2((uint)size) + 1)); - for (int y = 0; y < size; y++) - { - destination.Slice(y * destinationStride, size).Fill(dc); - } - - if (!filterPredictionEdges) - { - return; - } - - destination[0] = (ushort)((top[1] + left[1] + (2 * dc) + 2) >> 2); - for (int x = 1; x < size; x++) - { - destination[x] = (ushort)((top[x + 1] + (3 * dc) + 2) >> 2); - } - - for (int y = 1; y < size; y++) - { - destination[y * destinationStride] = (ushort)((left[y + 1] + (3 * dc) + 2) >> 2); - } - } - - /// - /// Sums reconstructed reference samples without overflowing their 16-bit storage. - /// - /// The samples to sum. - /// The exact unsigned sum. - private static uint SumSamples(ReadOnlySpan samples) - { - ref ushort samplesBase = ref MemoryMarshal.GetReference(samples); - uint sum = 0; - int i = 0; - - // Widen before reduction because a complete 64-sample, 12-bit reference edge exceeds UInt16. The shared index - // lets narrower vectors consume only the remainder from the widest available path. - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = samples.Length - Vector512.Count; - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - (Vector512 low, Vector512 high) = Vector512.Widen(Vector512.LoadUnsafe(ref samplesBase, (nuint)i)); - sum += Vector512.Sum(low) + Vector512.Sum(high); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = samples.Length - Vector256.Count; - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - (Vector256 low, Vector256 high) = Vector256.Widen(Vector256.LoadUnsafe(ref samplesBase, (nuint)i)); - sum += Vector256.Sum(low) + Vector256.Sum(high); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = samples.Length - Vector128.Count; - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - (Vector128 low, Vector128 high) = Vector128.Widen(Vector128.LoadUnsafe(ref samplesBase, (nuint)i)); - sum += Vector128.Sum(low) + Vector128.Sum(high); - } - } - - for (; i < samples.Length; i++) - { - sum += Unsafe.Add(ref samplesBase, i); - } - - return sum; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operator.cs deleted file mode 100644 index bf9a712c6..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operator.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the HEVC intra-prediction operator contract. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// Defines one closed intra-prediction operation selected by the decoded mode. - /// - private interface IHevcIntraPredictionOperator - { - /// - /// Reconstructs one square prediction block. - /// - /// The top-left, top, and top-right reference samples. - /// The top-left, left, and below-left reference samples. - /// The destination buffer beginning at the block origin. - /// The destination row stride in samples. - /// The square block side in samples. - /// The decoded prediction mode. - /// The reconstructed component precision. - /// Whether the luma edge filter applies to the selected block. - /// The caller-owned block and extended-reference scratch space. - public static abstract void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.PlanarOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.PlanarOperator.cs deleted file mode 100644 index 951dbb40e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.PlanarOperator.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines planar intra-prediction arithmetic. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// Implements planar interpolation between the top, left, bottom-left, and top-right references. - /// - private readonly struct PlanarOperator : IHevcIntraPredictionOperator - { - /// - public static void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch) - { - ref ushort topBase = ref MemoryMarshal.GetReference(top); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - - // Index zero is the shared corner, so the planar endpoint at coordinate N is stored at N + 1. - uint bottomLeft = left[size + 1]; - uint topRight = top[size + 1]; - int shift = BitOperations.Log2((uint)size) + 1; - uint rounding = (uint)size; - - for (int y = 0; y < size; y++) - { - uint leftSample = left[y + 1]; - uint topWeight = (uint)(size - y - 1); - uint bottomWeight = (uint)(y + 1); - ref ushort rowBase = ref Unsafe.Add(ref destinationBase, y * destinationStride); - int x = 0; - - // The two widened halves carry consecutive X coordinates. Each lane evaluates the normative - // horizontal and vertical ramps, then narrows after the common rounded power-of-two division. - if (Vector512.IsHardwareAccelerated) - { - Vector512 indices = CreateIndicesVector512(); - int oneVectorFromEnd = size - Vector512.Count; - for (; x <= oneVectorFromEnd; x += Vector512.Count) - { - Vector512 topSamples = Vector512.LoadUnsafe(ref topBase, (nuint)(x + 1)); - (Vector512 topLow, Vector512 topHigh) = Vector512.Widen(topSamples); - Vector512 lowIndices = indices + Vector512.Create((uint)x); - Vector512 highIndices = lowIndices + Vector512.Create((uint)Vector512.Count); - Vector512 low = CalculatePlanarVector( - topLow, - lowIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector512 high = CalculatePlanarVector( - topHigh, - highIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector512.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref rowBase, x)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 indices = CreateIndicesVector256(); - int oneVectorFromEnd = size - Vector256.Count; - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector256 topSamples = Vector256.LoadUnsafe(ref topBase, (nuint)(x + 1)); - (Vector256 topLow, Vector256 topHigh) = Vector256.Widen(topSamples); - Vector256 lowIndices = indices + Vector256.Create((uint)x); - Vector256 highIndices = lowIndices + Vector256.Create((uint)Vector256.Count); - Vector256 low = CalculatePlanarVector( - topLow, - lowIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector256 high = CalculatePlanarVector( - topHigh, - highIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector256.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref rowBase, x)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 indices = CreateIndicesVector128(); - int oneVectorFromEnd = size - Vector128.Count; - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - Vector128 topSamples = Vector128.LoadUnsafe(ref topBase, (nuint)(x + 1)); - (Vector128 topLow, Vector128 topHigh) = Vector128.Widen(topSamples); - Vector128 lowIndices = indices + Vector128.Create((uint)x); - Vector128 highIndices = lowIndices + Vector128.Create((uint)Vector128.Count); - Vector128 low = CalculatePlanarVector( - topLow, - lowIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector128 high = CalculatePlanarVector( - topHigh, - highIndices, - leftSample, - topRight, - bottomLeft, - topWeight, - bottomWeight, - (uint)size, - rounding, - shift); - - Vector128.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref rowBase, x)); - } - } - - for (; x < size; x++) - { - uint horizontal = ((uint)(size - x - 1) * leftSample) + ((uint)(x + 1) * topRight); - uint vertical = ((uint)(size - y - 1) * top[x + 1]) + ((uint)(y + 1) * bottomLeft); - Unsafe.Add(ref rowBase, x) = (ushort)((horizontal + vertical + (uint)size) >> shift); - } - } - } - - /// - /// Calculates one 512-bit half of a planar prediction row. - /// - /// The top reference samples. - /// The zero-based X coordinates. - /// The left reference sample for the row. - /// The top-right reference sample. - /// The bottom-left reference sample. - /// The top-reference weight. - /// The bottom-left-reference weight. - /// The square block side. - /// The division rounding constant. - /// The division shift. - /// The predicted samples as widened lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 CalculatePlanarVector( - Vector512 top, - Vector512 indices, - uint left, - uint topRight, - uint bottomLeft, - uint topWeight, - uint bottomWeight, - uint size, - uint rounding, - int shift) - { - Vector512 horizontal = ((Vector512.Create(size - 1) - indices) * left) + ((indices + Vector512.One) * topRight); - Vector512 vertical = (top * topWeight) + Vector512.Create(bottomLeft * bottomWeight); - return (horizontal + vertical + Vector512.Create(rounding)) >> shift; - } - - /// - /// Calculates one 256-bit half of a planar prediction row. - /// - /// The top reference samples. - /// The zero-based X coordinates. - /// The left reference sample for the row. - /// The top-right reference sample. - /// The bottom-left reference sample. - /// The top-reference weight. - /// The bottom-left-reference weight. - /// The square block side. - /// The division rounding constant. - /// The division shift. - /// The predicted samples as widened lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 CalculatePlanarVector( - Vector256 top, - Vector256 indices, - uint left, - uint topRight, - uint bottomLeft, - uint topWeight, - uint bottomWeight, - uint size, - uint rounding, - int shift) - { - Vector256 horizontal = ((Vector256.Create(size - 1) - indices) * left) + ((indices + Vector256.One) * topRight); - Vector256 vertical = (top * topWeight) + Vector256.Create(bottomLeft * bottomWeight); - return (horizontal + vertical + Vector256.Create(rounding)) >> shift; - } - - /// - /// Calculates one 128-bit half of a planar prediction row. - /// - /// The top reference samples. - /// The zero-based X coordinates. - /// The left reference sample for the row. - /// The top-right reference sample. - /// The bottom-left reference sample. - /// The top-reference weight. - /// The bottom-left-reference weight. - /// The square block side. - /// The division rounding constant. - /// The division shift. - /// The predicted samples as widened lanes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 CalculatePlanarVector( - Vector128 top, - Vector128 indices, - uint left, - uint topRight, - uint bottomLeft, - uint topWeight, - uint bottomWeight, - uint size, - uint rounding, - int shift) - { - Vector128 horizontal = ((Vector128.Create(size - 1) - indices) * left) + ((indices + Vector128.One) * topRight); - Vector128 vertical = (top * topWeight) + Vector128.Create(bottomLeft * bottomWeight); - return (horizontal + vertical + Vector128.Create(rounding)) >> shift; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs deleted file mode 100644 index b6e1f985f..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Provides reference-sample preparation and filter selection. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// Gets the angular-distance threshold for reference filtering at each supported block size. - /// - private static ReadOnlySpan ReferenceFilterThresholds => [10, 7, 1, 0]; - - /// - /// Gets the temporary sample count required while preparing prediction references. - /// - /// The base-two logarithm of the square prediction-block side. - /// The horizontal availability-unit width in plane samples. - /// The required number of elements. - public static int GetReferenceScratchLength(int log2Size, int unitWidth) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - return (4 << log2Size) + unitWidth; - } - - /// - /// Determines whether the selected mode uses filtered prediction references. - /// - /// The reconstructed plane. - /// The effective prediction mode in the inclusive range zero through thirty-four. - /// The base-two logarithm of the square prediction-block side. - /// The HEVC chroma-format identifier. - /// Whether the sequence disables intra-reference smoothing. - /// when the prepared references require filtering; otherwise, . - public static bool ShouldFilterReferenceSamples(HevcPlane plane, int mode, int log2Size, byte chromaFormat, bool intraSmoothingDisabled) - { - DebugGuard.MustBeBetweenOrEqualTo(mode, PlanarMode, 34, nameof(mode)); - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - if (intraSmoothingDisabled || (plane != HevcPlane.Y && chromaFormat != 3) || mode == DcMode) - { - return false; - } - - int angularDistance = Math.Min(Math.Abs(mode - HorizontalMode), Math.Abs(mode - VerticalMode)); - return angularDistance > ReferenceFilterThresholds[log2Size - 2]; - } - - /// - /// Prepares substituted top and left references from one reconstructed picture plane. - /// - /// The reconstructed still-picture planes. - /// The plane containing the prediction block. - /// The prediction-block left coordinate in plane samples. - /// The prediction-block top coordinate in plane samples. - /// The base-two logarithm of the square prediction-block side. - /// The horizontal availability-unit width in plane samples. - /// The vertical availability-unit height in plane samples. - /// - /// The availability flags ordered from the bottom-most below-left unit upward through top-left, then from the - /// left-most above unit through the right-most above-right unit. - /// - /// The destination top-left, top, and top-right reference samples. - /// The destination top-left, left, and below-left reference samples. - /// The caller-owned temporary storage sized by . - public static void PrepareReferenceSamples( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int log2Size, - int unitWidth, - int unitHeight, - ReadOnlySpan availableUnits, - Span top, - Span left, - Span scratch) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - int size = 1 << log2Size; - int referenceLength = (size * 2) + 1; - int leftUnitCount = (size * 2) / unitHeight; - int aboveUnitCount = (size * 2) / unitWidth; - int totalUnitCount = leftUnitCount + aboveUnitCount + 1; - ReadOnlySpan availability = availableUnits[..totalUnitCount]; - - if (availability.IndexOf(true) < 0) - { - ushort midpoint = (ushort)(1 << (picture.GetBitDepth(plane) - 1)); - top[..referenceLength].Fill(midpoint); - left[..referenceLength].Fill(midpoint); - return; - } - - if (availability.IndexOf(false) < 0) - { - picture.GetRowSpan(plane, y - 1).Slice(x - 1, referenceLength).CopyTo(top); - left[0] = top[0]; - for (int i = 1; i < referenceLength; i++) - { - left[i] = picture.GetRowSpan(plane, y + i - 1)[x - 1]; - } - - return; - } - - int leftSampleCount = size * 2; - int lineLength = leftSampleCount + unitWidth + (size * 2); - Span line = scratch[..lineLength]; - line.Fill((ushort)(1 << (picture.GetBitDepth(plane) - 1))); - - // The logical line runs from the bottom-most below-left sample towards the corner and then to the farthest - // above-right sample. This makes substitution a forward fill across availability units. - for (int unit = 0; unit < leftUnitCount; unit++) - { - if (!availability[unit]) - { - continue; - } - - int sourceY = y + ((leftUnitCount - unit - 1) * unitHeight); - int destinationEnd = ((unit + 1) * unitHeight) - 1; - for (int offset = 0; offset < unitHeight; offset++) - { - line[destinationEnd - offset] = picture.GetRowSpan(plane, sourceY + offset)[x - 1]; - } - } - - int cornerUnit = leftUnitCount; - if (availability[cornerUnit]) - { - line.Slice(leftSampleCount, unitWidth).Fill(picture.GetRowSpan(plane, y - 1)[x - 1]); - } - - int topStart = leftSampleCount + unitWidth; - ReadOnlySpan aboveAvailability = availability.Slice(cornerUnit + 1, aboveUnitCount); - if (aboveAvailability.IndexOf(true) >= 0) - { - // A top-edge block can still have reconstructed left references. Load the preceding row only when - // the availability derivation proves that at least one above or above-right unit exists. - ReadOnlySpan aboveRow = picture.GetRowSpan(plane, y - 1); - for (int unit = 0; unit < aboveUnitCount; unit++) - { - if (aboveAvailability[unit]) - { - aboveRow.Slice(x + (unit * unitWidth), unitWidth).CopyTo(line.Slice(topStart + (unit * unitWidth), unitWidth)); - } - } - } - - int firstAvailableUnit = availability.IndexOf(true); - int firstAvailableOffset = firstAvailableUnit < leftUnitCount - ? firstAvailableUnit * unitHeight - : leftSampleCount + ((firstAvailableUnit - leftUnitCount) * unitWidth); - - int lineOffset = 0; - ushort precedingSample = line[firstAvailableOffset]; - for (int unit = 0; unit < totalUnitCount; unit++) - { - int sampleCount = unit < leftUnitCount ? unitHeight : unitWidth; - Span unitSamples = line.Slice(lineOffset, sampleCount); - if (!availability[unit]) - { - unitSamples.Fill(precedingSample); - } - - precedingSample = unitSamples[^1]; - lineOffset += sampleCount; - } - - int cornerOffset = leftSampleCount + unitWidth - 1; - top[0] = left[0] = line[cornerOffset]; - line.Slice(topStart, size * 2).CopyTo(top[1..]); - for (int i = 1; i < referenceLength; i++) - { - left[i] = line[leftSampleCount - i]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs deleted file mode 100644 index 11cc57074..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Reconstructs HEVC intra-prediction blocks from prepared neighboring samples. -/// -/// -/// Closed static operators select planar, DC, or angular arithmetic once per block. SIMD rows keep neighboring output -/// columns in consecutive lanes; broadcast left endpoints and vector top references then evaluate the interpolation -/// without per-sample mode dispatch. Horizontal angular prediction reuses the vertical kernel in contiguous scratch -/// storage and transposes once into the strided destination. -/// -internal static partial class HevcIntraPredictor -{ - /// - /// The HEVC planar prediction mode. - /// - private const int PlanarMode = 0; - - /// - /// The HEVC DC prediction mode. - /// - private const int DcMode = 1; - - /// - /// The HEVC horizontal prediction mode. - /// - private const int HorizontalMode = 10; - - /// - /// The first prediction mode whose main reference is the top row. - /// - private const int FirstVerticalMode = 18; - - /// - /// The HEVC vertical prediction mode. - /// - private const int VerticalMode = 26; - - /// - /// The largest transform-block side supported by HEVC intra prediction. - /// - private const int MaximumBlockSize = 32; - - /// - /// Gets the angle selected by each absolute angular-mode displacement. - /// - private static ReadOnlySpan PredictionAngles => [0, 2, 5, 9, 13, 17, 21, 26, 32]; - - /// - /// Gets the reciprocal angle used to extend the main reference for negative directions. - /// - private static ReadOnlySpan InversePredictionAngles => [0, 4096, 1638, 910, 630, 482, 390, 315, 256]; - - /// - /// Gets the scratch length required to predict a block of the specified size. - /// - /// The base-two logarithm of the square block side. - /// The required number of elements. - public static int GetScratchLength(int log2Size) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - int size = 1 << log2Size; - return (size * size) + (4 * size) + 1; - } - - /// - /// Reconstructs one square intra-prediction block using a closed operator selected by the decoded mode. - /// - /// The top-left, top, and top-right reference samples. - /// The top-left, left, and below-left reference samples. - /// The destination buffer beginning at the block origin. - /// The destination row stride in samples. - /// The base-two logarithm of the square block side. - /// The decoded prediction mode in the inclusive range zero through thirty-four. - /// The reconstructed component precision. - /// Whether the luma edge filter applies to the selected block. - /// The caller-owned scratch returned by . - public static void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int log2Size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - DebugGuard.MustBeBetweenOrEqualTo(mode, PlanarMode, 34, nameof(mode)); - int size = 1 << log2Size; - - switch (mode) - { - case PlanarMode: - Predict( - top, - left, - destination, - destinationStride, - size, - mode, - bitDepth, - filterPredictionEdges, - scratch); - break; - case DcMode: - Predict( - top, - left, - destination, - destinationStride, - size, - mode, - bitDepth, - filterPredictionEdges, - scratch); - break; - default: - Predict( - top, - left, - destination, - destinationStride, - size, - mode, - bitDepth, - filterPredictionEdges, - scratch); - break; - } - } - - /// - /// Filters prepared reference samples using the normative three-tap or strong bilinear filter. - /// - /// The unfiltered top-left, top, and top-right samples. - /// The unfiltered top-left, left, and below-left samples. - /// The destination top reference. - /// The destination left reference. - /// The base-two logarithm of the square prediction-block side. - /// The reconstructed luma precision. - /// Whether the sequence permits strong intra smoothing. - public static void FilterReferenceSamples( - ReadOnlySpan top, - ReadOnlySpan left, - Span filteredTop, - Span filteredLeft, - int log2Size, - int bitDepth, - bool strongIntraSmoothingEnabled) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - int size = 1 << log2Size; - int referenceLength = (size * 2) + 1; - bool useStrongSmoothing = strongIntraSmoothingEnabled && size == MaximumBlockSize; - if (useStrongSmoothing) - { - int threshold = 1 << (bitDepth - 5); - int last = referenceLength - 1; - bool leftIsBilinear = Math.Abs((left[last] + left[0]) - (2 * left[size])) < threshold; - bool topIsBilinear = Math.Abs((top[0] + top[last]) - (2 * top[size])) < threshold; - useStrongSmoothing = leftIsBilinear && topIsBilinear; - } - - if (useStrongSmoothing) - { - FilterReferenceBilinear(top[..referenceLength], filteredTop, size); - FilterReferenceBilinear(left[..referenceLength], filteredLeft, size); - return; - } - - // The corner belongs to both references. Filtering it once from the first samples on both sides keeps the - // two logical arrays identical at index zero before their independent one-dimensional filters continue. - ushort filteredCorner = (ushort)((left[1] + (2 * top[0]) + top[1] + 2) >> 2); - filteredTop[0] = filteredCorner; - filteredLeft[0] = filteredCorner; - FilterReferenceThreeTap(top[..referenceLength], filteredTop); - FilterReferenceThreeTap(left[..referenceLength], filteredLeft); - } - - /// - /// Invokes one statically selected prediction operator without interface dispatch in the block loop. - /// - /// The selected prediction operator. - /// The prepared top reference. - /// The prepared left reference. - /// The destination block origin. - /// The destination row stride in samples. - /// The square block side in samples. - /// The decoded prediction mode. - /// The reconstructed component precision. - /// Whether the luma edge filter applies. - /// The caller-owned prediction scratch. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Predict( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges, - Span scratch) - where TOperator : struct, IHevcIntraPredictionOperator - => TOperator.Predict( - top, - left, - destination, - destinationStride, - size, - mode, - bitDepth, - filterPredictionEdges, - scratch); - - /// - /// Applies the strong bilinear filter between the reference endpoints. - /// - /// The complete unfiltered reference. - /// The complete filtered reference. - /// The prediction-block side in samples. - private static void FilterReferenceBilinear(ReadOnlySpan source, Span destination, int size) - { - int last = source.Length - 1; - destination[0] = source[0]; - destination[last] = source[last]; - ref ushort sourceBase = ref MemoryMarshal.GetReference(source); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - uint first = source[0]; - uint final = source[last]; - int shift = BitOperations.Log2((uint)(size * 2)); - uint rounding = (uint)size; - int i = 1; - - // Each widened lane represents one reference coordinate. The weights sum to 2N, so narrowing is exact - // after the rounded shift for every supported 8, 10, and 12-bit sample. - if (Vector512.IsHardwareAccelerated) - { - Vector512 indices = CreateIndicesVector512(); - int oneVectorFromEnd = last - Vector512.Count; - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - Vector512 lowerIndices = indices + Vector512.Create((uint)i); - Vector512 upperIndices = lowerIndices + Vector512.Create((uint)Vector512.Count); - Vector512 lower = (((Vector512.Create((uint)last) - lowerIndices) * first) + (lowerIndices * final) + Vector512.Create(rounding)) >> shift; - Vector512 upper = (((Vector512.Create((uint)last) - upperIndices) * first) + (upperIndices * final) + Vector512.Create(rounding)) >> shift; - Vector512.Narrow(lower, upper).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 indices = CreateIndicesVector256(); - int oneVectorFromEnd = last - Vector256.Count; - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - Vector256 lowerIndices = indices + Vector256.Create((uint)i); - Vector256 upperIndices = lowerIndices + Vector256.Create((uint)Vector256.Count); - Vector256 lower = (((Vector256.Create((uint)last) - lowerIndices) * first) + (lowerIndices * final) + Vector256.Create(rounding)) >> shift; - Vector256 upper = (((Vector256.Create((uint)last) - upperIndices) * first) + (upperIndices * final) + Vector256.Create(rounding)) >> shift; - Vector256.Narrow(lower, upper).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 indices = CreateIndicesVector128(); - int oneVectorFromEnd = last - Vector128.Count; - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - Vector128 lowerIndices = indices + Vector128.Create((uint)i); - Vector128 upperIndices = lowerIndices + Vector128.Create((uint)Vector128.Count); - Vector128 lower = (((Vector128.Create((uint)last) - lowerIndices) * first) + (lowerIndices * final) + Vector128.Create(rounding)) >> shift; - Vector128 upper = (((Vector128.Create((uint)last) - upperIndices) * first) + (upperIndices * final) + Vector128.Create(rounding)) >> shift; - Vector128.Narrow(lower, upper).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - for (; i < last; i++) - { - Unsafe.Add(ref destinationBase, i) = (ushort)((((last - i) * first) + (i * final) + rounding) >> shift); - } - } - - /// - /// Applies the normal three-tap reference filter to every non-endpoint sample. - /// - /// The complete unfiltered reference. - /// The complete filtered reference with its corner already initialized. - private static void FilterReferenceThreeTap(ReadOnlySpan source, Span destination) - { - int last = source.Length - 1; - destination[last] = source[last]; - ref ushort sourceBase = ref MemoryMarshal.GetReference(source); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - int i = 1; - - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = last - Vector512.Count; - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - Vector512 previous = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i - 1)); - Vector512 current = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); - Vector512 next = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector512 previousLow, Vector512 previousHigh) = Vector512.Widen(previous); - (Vector512 currentLow, Vector512 currentHigh) = Vector512.Widen(current); - (Vector512 nextLow, Vector512 nextHigh) = Vector512.Widen(next); - Vector512 low = (previousLow + (currentLow << 1) + nextLow + Vector512.Create(2U)) >> 2; - Vector512 high = (previousHigh + (currentHigh << 1) + nextHigh + Vector512.Create(2U)) >> 2; - Vector512.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = last - Vector256.Count; - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - Vector256 previous = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i - 1)); - Vector256 current = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); - Vector256 next = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector256 previousLow, Vector256 previousHigh) = Vector256.Widen(previous); - (Vector256 currentLow, Vector256 currentHigh) = Vector256.Widen(current); - (Vector256 nextLow, Vector256 nextHigh) = Vector256.Widen(next); - Vector256 low = (previousLow + (currentLow << 1) + nextLow + Vector256.Create(2U)) >> 2; - Vector256 high = (previousHigh + (currentHigh << 1) + nextHigh + Vector256.Create(2U)) >> 2; - Vector256.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = last - Vector128.Count; - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - Vector128 previous = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i - 1)); - Vector128 current = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); - Vector128 next = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); - (Vector128 previousLow, Vector128 previousHigh) = Vector128.Widen(previous); - (Vector128 currentLow, Vector128 currentHigh) = Vector128.Widen(current); - (Vector128 nextLow, Vector128 nextHigh) = Vector128.Widen(next); - Vector128 low = (previousLow + (currentLow << 1) + nextLow + Vector128.Create(2U)) >> 2; - Vector128 high = (previousHigh + (currentHigh << 1) + nextHigh + Vector128.Create(2U)) >> 2; - Vector128.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); - } - } - - for (; i < last; i++) - { - Unsafe.Add(ref destinationBase, i) = (ushort)((source[i - 1] + (2 * source[i]) + source[i + 1] + 2) >> 2); - } - } - - /// - /// Creates the zero-through-fifteen lane indices used by 512-bit weighted interpolation. - /// - /// The ordered lane indices. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 CreateIndicesVector512() - => Vector512.Create(0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U); - - /// - /// Creates the zero-through-seven lane indices used by 256-bit weighted interpolation. - /// - /// The ordered lane indices. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 CreateIndicesVector256() => Vector256.Create(0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U); - - /// - /// Creates the zero-through-three lane indices used by 128-bit weighted interpolation. - /// - /// The ordered lane indices. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 CreateIndicesVector128() => Vector128.Create(0U, 1U, 2U, 3U); -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs deleted file mode 100644 index 24bcadec4..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs +++ /dev/null @@ -1,412 +0,0 @@ -// 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.Hevc; - -/// -/// Reconstructs dequantized HEVC transform coefficients. -/// -/// -/// Consecutive quantized coefficients occupy consecutive 32-bit lanes. Scaling-list values are widened to the same lane -/// shape before the inverse scale, quantization shift, rounding, and transform-range clamp are applied. Vector-width -/// loops advance the complete coefficient prefix and leave only the final incomplete group to the scalar equation. -/// -internal static class HevcInverseQuantizer -{ - /// - /// Gets the inverse quantization scale selected by the quantization-parameter remainder. - /// - private static ReadOnlySpan InverseQuantizationScales => [40, 45, 51, 57, 64, 72]; - - /// - /// Dequantizes one square transform block using the effective component quantization parameter. - /// - /// The decoded quantized coefficients in raster order. - /// The destination dequantized coefficients in raster order. - /// The base-two logarithm of the transform-block side. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The effective nonnegative component quantization parameter including its bit-depth offset. - /// Whether the governing sequence enables scaling lists. - /// The effective picture scaling matrices. - /// The reconstructed color plane. - /// Whether the transform block belongs to an intra-predicted coding unit. - /// Whether the transform block bypasses the inverse transform. - /// Whether transform-skip precision is extended by the sequence. - public static void Dequantize( - ReadOnlySpan quantized, - Span destination, - int log2Size, - int bitDepth, - int maxTransformDynamicRange, - int quantizationParameter, - bool scalingListEnabled, - HevcScalingList scalingList, - HevcPlane plane, - bool isIntraPredicted, - bool transformSkip, - bool extendedPrecisionProcessingEnabled) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - DebugGuard.MustBeGreaterThanOrEqualTo(quantizationParameter, 0, nameof(quantizationParameter)); - int size = 1 << log2Size; - int sampleCount = size * size; - DebugGuard.IsTrue(quantized.Length >= sampleCount, "The quantized coefficient span is shorter than the transform block."); - DebugGuard.IsTrue(destination.Length >= sampleCount, "The dequantized coefficient span is shorter than the transform block."); - - int transformShift = maxTransformDynamicRange - bitDepth - log2Size; - if (transformSkip && extendedPrecisionProcessingEnabled) - { - transformShift = Math.Max(0, transformShift); - } - - int quantizationParameterPer = quantizationParameter / 6; - int quantizationParameterRemainder = quantizationParameter % 6; - int inverseQuantizationScale = InverseQuantizationScales[quantizationParameterRemainder]; - bool useScalingList = scalingListEnabled && (!transformSkip || log2Size == 2); - int rightShift = 6 - (transformShift + quantizationParameterPer) + (useScalingList ? 4 : 0); - int outputMinimum = -(1 << maxTransformDynamicRange); - int outputMaximum = (1 << maxTransformDynamicRange) - 1; - - // The input clip is part of the normative dequantization process. Its right-shift dependency ensures the - // following signed 32-bit multiplication and optional left shift cannot overflow for any valid coefficient. - int scaleBits = useScalingList ? 15 : 7; - int targetInputBitDepth = Math.Min(maxTransformDynamicRange + 1, 32 + rightShift - scaleBits); - int inputMinimum = -(1 << (targetInputBitDepth - 1)); - int inputMaximum = (1 << (targetInputBitDepth - 1)) - 1; - - if (!useScalingList) - { - DequantizeUniform( - quantized[..sampleCount], - destination[..sampleCount], - inverseQuantizationScale, - rightShift, - inputMinimum, - inputMaximum, - outputMinimum, - outputMaximum); - - return; - } - - int sizeId = log2Size - 2; - int matrixId = (isIntraPredicted ? 0 : 3) + (int)plane; - ReadOnlySpan matrix = scalingList.GetExpandedMatrix(sizeId, matrixId); - DequantizeScalingList( - quantized[..sampleCount], - destination[..sampleCount], - matrix, - inverseQuantizationScale, - rightShift, - inputMinimum, - inputMaximum, - outputMinimum, - outputMaximum); - } - - /// - /// Dequantizes coefficients using one uniform inverse-quantization scale. - /// - /// The complete quantized coefficient block. - /// The complete dequantized coefficient block. - /// The inverse-quantization scale. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - private static void DequantizeUniform( - ReadOnlySpan source, - Span destination, - int scale, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - int i = 0; - - // Descending widths advance one shared coefficient offset. Smaller registers consume complete groups left by a - // wider path, so the scalar loop sees fewer than four values without any overlapping dequantization stores. - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector512.Count; - Vector512 weights = Vector512.Create(scale); - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - Vector512 values = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector256.Count; - Vector256 weights = Vector256.Create(scale); - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - Vector256 values = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector128.Count; - Vector128 weights = Vector128.Create(scale); - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - Vector128 values = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - for (; i < source.Length; i++) - { - destination[i] = Dequantize(source[i], scale, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum); - } - } - - /// - /// Dequantizes coefficients using the expanded scaling matrix selected for the transform block. - /// - /// The complete quantized coefficient block. - /// The complete dequantized coefficient block. - /// The scaling matrix expanded to the transform dimensions. - /// The inverse-quantization scale. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - private static void DequantizeScalingList( - ReadOnlySpan source, - Span destination, - ReadOnlySpan matrix, - int inverseQuantizationScale, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - ref byte matrixBase = ref MemoryMarshal.GetReference(matrix); - int i = 0; - - // Scaling matrices use one unsigned byte per coefficient. Each width loads exactly its matching byte count, - // widens in source order, and multiplies by the common inverse-quantization scale before signed arithmetic. - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector512.Count; - for (; i <= oneVectorFromEnd; i += Vector512.Count) - { - Vector512 values = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); - Vector512 weights = LoadScalingWeightsVector512(ref Unsafe.Add(ref matrixBase, i), inverseQuantizationScale); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector256.Count; - for (; i <= oneVectorFromEnd; i += Vector256.Count) - { - Vector256 values = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); - Vector256 weights = LoadScalingWeightsVector256(ref Unsafe.Add(ref matrixBase, i), inverseQuantizationScale); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = source.Length - Vector128.Count; - for (; i <= oneVectorFromEnd; i += Vector128.Count) - { - Vector128 values = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); - Vector128 weights = LoadScalingWeightsVector128(ref Unsafe.Add(ref matrixBase, i), inverseQuantizationScale); - Dequantize(values, weights, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum).StoreUnsafe(ref destinationBase, (nuint)i); - } - } - - for (; i < source.Length; i++) - { - int weight = Unsafe.Add(ref matrixBase, i) * inverseQuantizationScale; - destination[i] = Dequantize(source[i], weight, rightShift, inputMinimum, inputMaximum, outputMinimum, outputMaximum); - } - } - - /// - /// Loads and widens sixteen scaling coefficients for a 512-bit coefficient vector. - /// - /// The first scaling coefficient. - /// The inverse-quantization scale. - /// The sixteen ordered dequantization weights. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 LoadScalingWeightsVector512(ref byte source, int scale) - { - Vector128 packed = Vector128.LoadUnsafe(ref source); - (Vector128 lower16, Vector128 upper16) = Vector128.Widen(packed); - Vector256 lower32 = Vector256.Create(Vector128.WidenLower(lower16), Vector128.WidenUpper(lower16)); - Vector256 upper32 = Vector256.Create(Vector128.WidenLower(upper16), Vector128.WidenUpper(upper16)); - return Vector512.Create(lower32, upper32).AsInt32() * Vector512.Create(scale); - } - - /// - /// Loads and widens eight scaling coefficients for a 256-bit coefficient vector. - /// - /// The first scaling coefficient. - /// The inverse-quantization scale. - /// The eight ordered dequantization weights. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 LoadScalingWeightsVector256(ref byte source, int scale) - { - ulong packed = Unsafe.ReadUnaligned(ref source); - Vector128 values16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); - Vector256 values32 = Vector256.Create(Vector128.WidenLower(values16), Vector128.WidenUpper(values16)); - return values32.AsInt32() * Vector256.Create(scale); - } - - /// - /// Loads and widens four scaling coefficients for a 128-bit coefficient vector. - /// - /// The first scaling coefficient. - /// The inverse-quantization scale. - /// The four ordered dequantization weights. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 LoadScalingWeightsVector128(ref byte source, int scale) - { - uint packed = Unsafe.ReadUnaligned(ref source); - Vector128 values16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); - Vector128 values32 = Vector128.WidenLower(values16); - return values32.AsInt32() * Vector128.Create(scale); - } - - /// - /// Dequantizes sixteen signed coefficients with independent scaling weights. - /// - /// The quantized coefficients. - /// The dequantization weights. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - /// The dequantized coefficients. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 Dequantize( - Vector512 values, - Vector512 weights, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - // A positive rightShift applies nearest-integer rounding before division. A nonpositive value represents an - // exact left shift; the earlier input clamp guarantees that multiplication and shifting remain in Int32 range. - values = Vector512.Clamp(values, Vector512.Create(inputMinimum), Vector512.Create(inputMaximum)); - Vector512 result = values * weights; - result = rightShift > 0 - ? (result + Vector512.Create(1 << (rightShift - 1))) >> rightShift - : result << -rightShift; - - return Vector512.Clamp(result, Vector512.Create(outputMinimum), Vector512.Create(outputMaximum)); - } - - /// - /// Dequantizes eight signed coefficients with independent scaling weights. - /// - /// The quantized coefficients. - /// The dequantization weights. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - /// The dequantized coefficients. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 Dequantize( - Vector256 values, - Vector256 weights, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - values = Vector256.Clamp(values, Vector256.Create(inputMinimum), Vector256.Create(inputMaximum)); - Vector256 result = values * weights; - result = rightShift > 0 - ? (result + Vector256.Create(1 << (rightShift - 1))) >> rightShift - : result << -rightShift; - - return Vector256.Clamp(result, Vector256.Create(outputMinimum), Vector256.Create(outputMaximum)); - } - - /// - /// Dequantizes four signed coefficients with independent scaling weights. - /// - /// The quantized coefficients. - /// The dequantization weights. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - /// The dequantized coefficients. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 Dequantize( - Vector128 values, - Vector128 weights, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - values = Vector128.Clamp(values, Vector128.Create(inputMinimum), Vector128.Create(inputMaximum)); - Vector128 result = values * weights; - result = rightShift > 0 - ? (result + Vector128.Create(1 << (rightShift - 1))) >> rightShift - : result << -rightShift; - - return Vector128.Clamp(result, Vector128.Create(outputMinimum), Vector128.Create(outputMaximum)); - } - - /// - /// Dequantizes one signed coefficient. - /// - /// The quantized coefficient. - /// The dequantization weight. - /// The signed normalization shift. - /// The inclusive quantized input minimum. - /// The inclusive quantized input maximum. - /// The inclusive dequantized output minimum. - /// The inclusive dequantized output maximum. - /// The dequantized coefficient. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Dequantize( - int value, - int weight, - int rightShift, - int inputMinimum, - int inputMaximum, - int outputMinimum, - int outputMaximum) - { - int result = Math.Clamp(value, inputMinimum, inputMaximum) * weight; - result = rightShift > 0 ? (result + (1 << (rightShift - 1))) >> rightShift : result << -rightShift; - return Math.Clamp(result, outputMinimum, outputMaximum); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine16Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine16Operator.cs deleted file mode 100644 index e93d20ec1..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine16Operator.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the sixteen-point inverse discrete cosine transform operator. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Implements the sixteen-point inverse discrete cosine transform. - /// - private readonly struct DiscreteCosine16Operator : IHevcInverseTransformOperator - { - /// - public static int Size => 16; - - /// - public static bool UsesButterfly => true; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCoefficient(int frequency, int position) - { - if (frequency == 0) - { - return 64; - } - - int angle = ((2 * position) + 1) * frequency * 2; - angle &= 127; - if (angle > 64) - { - angle = 128 - angle; - } - - // The second quadrant reuses the first-quadrant magnitude with a negative sign. - if (angle > 32) - { - return -DiscreteCosineMagnitudes[64 - angle]; - } - - return DiscreteCosineMagnitudes[angle]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine32Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine32Operator.cs deleted file mode 100644 index dc1277890..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine32Operator.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the thirty-two-point inverse discrete cosine transform operator. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Implements the thirty-two-point inverse discrete cosine transform. - /// - private readonly struct DiscreteCosine32Operator : IHevcInverseTransformOperator - { - /// - public static int Size => 32; - - /// - public static bool UsesButterfly => true; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCoefficient(int frequency, int position) - { - if (frequency == 0) - { - return 64; - } - - int angle = ((2 * position) + 1) * frequency * 1; - angle &= 127; - if (angle > 64) - { - angle = 128 - angle; - } - - // The second quadrant reuses the first-quadrant magnitude with a negative sign. - if (angle > 32) - { - return -DiscreteCosineMagnitudes[64 - angle]; - } - - return DiscreteCosineMagnitudes[angle]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine4Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine4Operator.cs deleted file mode 100644 index 7bd767cc1..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine4Operator.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the four-point inverse discrete cosine transform operator. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Implements the four-point inverse discrete cosine transform. - /// - private readonly struct DiscreteCosine4Operator : IHevcInverseTransformOperator - { - /// - public static int Size => 4; - - /// - public static bool UsesButterfly => true; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCoefficient(int frequency, int position) - { - if (frequency == 0) - { - return 64; - } - - int angle = ((2 * position) + 1) * frequency * 8; - angle &= 127; - if (angle > 64) - { - angle = 128 - angle; - } - - // The second quadrant reuses the first-quadrant magnitude with a negative sign. - if (angle > 32) - { - return -DiscreteCosineMagnitudes[64 - angle]; - } - - return DiscreteCosineMagnitudes[angle]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine8Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine8Operator.cs deleted file mode 100644 index 7137d7843..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteCosine8Operator.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the eight-point inverse discrete cosine transform operator. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Implements the eight-point inverse discrete cosine transform. - /// - private readonly struct DiscreteCosine8Operator : IHevcInverseTransformOperator - { - /// - public static int Size => 8; - - /// - public static bool UsesButterfly => true; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCoefficient(int frequency, int position) - { - if (frequency == 0) - { - return 64; - } - - int angle = ((2 * position) + 1) * frequency * 4; - angle &= 127; - if (angle > 64) - { - angle = 128 - angle; - } - - // The second quadrant reuses the first-quadrant magnitude with a negative sign. - if (angle > 32) - { - return -DiscreteCosineMagnitudes[64 - angle]; - } - - return DiscreteCosineMagnitudes[angle]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteSine4Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteSine4Operator.cs deleted file mode 100644 index 222914d4b..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.DiscreteSine4Operator.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the four-point inverse discrete sine transform operator. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Implements the four-point inverse discrete sine transform. - /// - private readonly struct DiscreteSine4Operator : IHevcInverseTransformOperator - { - /// - /// Gets the inverse-DST matrix in frequency-major order. - /// - private static ReadOnlySpan Coefficients => - [ - 29, 55, 74, 84, - 74, 74, 0, -74, - 84, -29, -74, 55, - 55, -84, 74, -29 - ]; - - /// - public static int Size => 4; - - /// - public static bool UsesButterfly => false; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCoefficient(int frequency, int position) - { - return Coefficients[(frequency * Size) + position]; - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs deleted file mode 100644 index e300b6956..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs +++ /dev/null @@ -1,547 +0,0 @@ -// 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.Formats.Heif.Av1.Transform; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Provides the shared HEVC inverse-transform stage and transpose operations. Vector lanes represent independent -/// transform lines, while consecutive scratch rows represent frequency groups in the partial-butterfly factorization. -/// Arithmetic never mixes lines; transposition is the only operation that exchanges row and column coordinates. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Gets the common inverse-DCT magnitudes ordered on the pi-over-sixty-four angle grid. - /// - private static ReadOnlySpan DiscreteCosineMagnitudes => - [ - 90, 90, 90, 90, 89, 88, 87, 85, 83, 82, 80, 78, 75, 73, 70, 67, 64, - 61, 57, 54, 50, 46, 43, 38, 36, 31, 25, 22, 18, 13, 9, 4, 0 - ]; - - /// - /// Calculates the disjoint odd-frequency groups that seed the HEVC partial-butterfly reconstruction. - /// - /// The selected inverse-DCT operator. - /// The frequency rows followed by contiguous independent lines. - /// The destination group rows. - /// The number of independent lines transformed together. - private static void PopulateButterflyGroups(ReadOnlySpan source, Span groups, int lineCount) - where TOperator : struct, IHevcInverseTransformOperator - { - int size = TOperator.Size; - int groupOffset = 0; - for (int frequencyStep = 2; frequencyStep < size; frequencyStep <<= 1) - { - int outputCount = size / frequencyStep; - int firstFrequency = frequencyStep >> 1; - for (int position = 0; position < outputCount; position++) - { - PopulateButterflyGroupRow( - source, - groups.Slice((groupOffset + position) * lineCount, lineCount), - lineCount, - firstFrequency, - frequencyStep, - position); - } - - groupOffset += outputCount; - } - - // The deepest even group contains the DC term and the transform's Nyquist-frequency term. It remains a - // two-element group for every supported DCT size and closes the recursive butterfly hierarchy. - for (int position = 0; position < 2; position++) - { - PopulateButterflyGroupRow( - source, - groups.Slice((groupOffset + position) * lineCount, lineCount), - lineCount, - 0, - size >> 1, - position); - } - } - - /// - /// Calculates one partial-butterfly group row across all independent lines. - /// - /// The selected inverse-DCT operator. - /// The complete frequency-row input. - /// The destination group row. - /// The number of independent lines. - /// The first frequency included by the group. - /// The distance between included frequencies. - /// The group-relative spatial coordinate. - private static void PopulateButterflyGroupRow( - ReadOnlySpan source, - Span destination, - int lineCount, - int firstFrequency, - int frequencyStep, - int position) - where TOperator : struct, IHevcInverseTransformOperator - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - int x = 0; - - // Source storage is frequency-major: advancing one lane moves to the same frequency in another independent - // transform line. The shared X offset lets each narrower width continue exactly where the wider loop stopped. - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector512.Count; - for (; x <= oneVectorFromEnd; x += Vector512.Count) - { - Vector512 sum = Vector512.Zero; - for (int frequency = firstFrequency; frequency < TOperator.Size; frequency += frequencyStep) - { - Vector512 values = Vector512.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector512.Create(TOperator.GetCoefficient(frequency, position)); - } - - sum.StoreUnsafe(ref destinationBase, (nuint)x); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector256.Count; - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector256 sum = Vector256.Zero; - for (int frequency = firstFrequency; frequency < TOperator.Size; frequency += frequencyStep) - { - Vector256 values = Vector256.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector256.Create(TOperator.GetCoefficient(frequency, position)); - } - - sum.StoreUnsafe(ref destinationBase, (nuint)x); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector128.Count; - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - Vector128 sum = Vector128.Zero; - for (int frequency = firstFrequency; frequency < TOperator.Size; frequency += frequencyStep) - { - Vector128 values = Vector128.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector128.Create(TOperator.GetCoefficient(frequency, position)); - } - - sum.StoreUnsafe(ref destinationBase, (nuint)x); - } - } - - for (; x < lineCount; x++) - { - int sum = 0; - for (int frequency = firstFrequency; frequency < TOperator.Size; frequency += frequencyStep) - { - sum += source[(frequency * lineCount) + x] * TOperator.GetCoefficient(frequency, position); - } - - destination[x] = sum; - } - } - - /// - /// Expands the disjoint partial-butterfly groups into spatial rows. - /// - /// The selected inverse-DCT operator. - /// The buffer containing every disjoint group. - /// The alternate expansion buffer. - /// The number of independent lines transformed together. - /// The rounded right shift applied at the final hierarchy level. - /// The inclusive output minimum. - /// The inclusive output maximum. - /// The buffer containing the completed spatial rows. - private static Span CombineButterflyGroups( - Span initial, - Span alternate, - int lineCount, - int shift, - int minimum, - int maximum) - where TOperator : struct, IHevcInverseTransformOperator - { - int size = TOperator.Size; - int combinedSize = 2; - int combinedStart = size - combinedSize; - bool currentIsInitial = true; - while (combinedSize < size) - { - int oddStart = combinedStart - combinedSize; - bool finalLevel = (combinedSize << 1) == size; - ReadOnlySpan even = currentIsInitial ? initial : alternate; - Span destination = currentIsInitial ? alternate : initial; - - // Odd rows remain in the initial disjoint-group buffer while expanded even rows alternate buffers. This - // preserves every source row needed by later hierarchy levels without allocating another transform block. - for (int position = 0; position < combinedSize; position++) - { - ReadOnlySpan evenRow = even.Slice((combinedStart + position) * lineCount, lineCount); - ReadOnlySpan oddRow = initial.Slice((oddStart + position) * lineCount, lineCount); - Span positiveRow = destination.Slice((oddStart + position) * lineCount, lineCount); - Span negativeRow = destination.Slice((oddStart + (2 * combinedSize) - 1 - position) * lineCount, lineCount); - CombineButterflyRows(evenRow, oddRow, positiveRow, negativeRow, finalLevel, shift, minimum, maximum); - } - - combinedStart = oddStart; - combinedSize <<= 1; - currentIsInitial = !currentIsInitial; - } - - return currentIsInitial ? initial : alternate; - } - - /// - /// Combines one symmetric pair of partial-butterfly rows. - /// - /// The even-frequency contribution. - /// The odd-frequency contribution. - /// The destination receiving the added contribution. - /// The destination receiving the subtracted contribution. - /// Whether this is the final hierarchy level. - /// The rounded right shift applied at the final hierarchy level. - /// The inclusive output minimum. - /// The inclusive output maximum. - private static void CombineButterflyRows( - ReadOnlySpan even, - ReadOnlySpan odd, - Span positive, - Span negative, - bool roundAndClip, - int shift, - int minimum, - int maximum) - { - ref int evenBase = ref MemoryMarshal.GetReference(even); - ref int oddBase = ref MemoryMarshal.GetReference(odd); - ref int positiveBase = ref MemoryMarshal.GetReference(positive); - ref int negativeBase = ref MemoryMarshal.GetReference(negative); - int x = 0; - - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = even.Length - Vector512.Count; - for (; x <= oneVectorFromEnd; x += Vector512.Count) - { - Vector512 evenValues = Vector512.LoadUnsafe(ref evenBase, (nuint)x); - Vector512 oddValues = Vector512.LoadUnsafe(ref oddBase, (nuint)x); - Vector512 added = evenValues + oddValues; - Vector512 subtracted = evenValues - oddValues; - if (roundAndClip) - { - added = RoundShiftAndClamp(added, shift, minimum, maximum); - subtracted = RoundShiftAndClamp(subtracted, shift, minimum, maximum); - } - - added.StoreUnsafe(ref positiveBase, (nuint)x); - subtracted.StoreUnsafe(ref negativeBase, (nuint)x); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = even.Length - Vector256.Count; - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector256 evenValues = Vector256.LoadUnsafe(ref evenBase, (nuint)x); - Vector256 oddValues = Vector256.LoadUnsafe(ref oddBase, (nuint)x); - Vector256 added = evenValues + oddValues; - Vector256 subtracted = evenValues - oddValues; - if (roundAndClip) - { - added = RoundShiftAndClamp(added, shift, minimum, maximum); - subtracted = RoundShiftAndClamp(subtracted, shift, minimum, maximum); - } - - added.StoreUnsafe(ref positiveBase, (nuint)x); - subtracted.StoreUnsafe(ref negativeBase, (nuint)x); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = even.Length - Vector128.Count; - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - Vector128 evenValues = Vector128.LoadUnsafe(ref evenBase, (nuint)x); - Vector128 oddValues = Vector128.LoadUnsafe(ref oddBase, (nuint)x); - Vector128 added = evenValues + oddValues; - Vector128 subtracted = evenValues - oddValues; - if (roundAndClip) - { - added = RoundShiftAndClamp(added, shift, minimum, maximum); - subtracted = RoundShiftAndClamp(subtracted, shift, minimum, maximum); - } - - added.StoreUnsafe(ref positiveBase, (nuint)x); - subtracted.StoreUnsafe(ref negativeBase, (nuint)x); - } - } - - for (; x < even.Length; x++) - { - int added = even[x] + odd[x]; - int subtracted = even[x] - odd[x]; - if (roundAndClip) - { - added = RoundShiftAndClamp(added, shift, minimum, maximum); - subtracted = RoundShiftAndClamp(subtracted, shift, minimum, maximum); - } - - positive[x] = added; - negative[x] = subtracted; - } - } - - /// - /// Applies the four-point inverse-DST matrix across all independent lines. - /// - /// The selected dense transform operator. - /// The frequency rows followed by contiguous independent lines. - /// The spatial rows followed by contiguous independent lines. - /// The number of independent lines transformed together. - /// The rounded right shift applied to each result. - /// The inclusive output minimum. - /// The inclusive output maximum. - private static void TransformDense( - ReadOnlySpan source, - Span destination, - int lineCount, - int shift, - int minimum, - int maximum) - where TOperator : struct, IHevcInverseTransformOperator - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - for (int position = 0; position < TOperator.Size; position++) - { - int x = 0; - if (Vector512.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector512.Count; - for (; x <= oneVectorFromEnd; x += Vector512.Count) - { - Vector512 sum = Vector512.Zero; - for (int frequency = 0; frequency < TOperator.Size; frequency++) - { - Vector512 values = Vector512.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector512.Create(TOperator.GetCoefficient(frequency, position)); - } - - RoundShiftAndClamp(sum, shift, minimum, maximum).StoreUnsafe(ref destinationBase, (nuint)((position * lineCount) + x)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector256.Count; - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector256 sum = Vector256.Zero; - for (int frequency = 0; frequency < TOperator.Size; frequency++) - { - Vector256 values = Vector256.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector256.Create(TOperator.GetCoefficient(frequency, position)); - } - - RoundShiftAndClamp(sum, shift, minimum, maximum).StoreUnsafe(ref destinationBase, (nuint)((position * lineCount) + x)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - int oneVectorFromEnd = lineCount - Vector128.Count; - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - Vector128 sum = Vector128.Zero; - for (int frequency = 0; frequency < TOperator.Size; frequency++) - { - Vector128 values = Vector128.LoadUnsafe(ref sourceBase, (nuint)((frequency * lineCount) + x)); - sum += values * Vector128.Create(TOperator.GetCoefficient(frequency, position)); - } - - RoundShiftAndClamp(sum, shift, minimum, maximum).StoreUnsafe(ref destinationBase, (nuint)((position * lineCount) + x)); - } - } - - for (; x < lineCount; x++) - { - int sum = 0; - for (int frequency = 0; frequency < TOperator.Size; frequency++) - { - sum += source[(frequency * lineCount) + x] * TOperator.GetCoefficient(frequency, position); - } - - destination[(position * lineCount) + x] = RoundShiftAndClamp(sum, shift, minimum, maximum); - } - } - } - - /// - /// Transposes one rectangular transform block into a separate full-block buffer. - /// - /// The source block in raster order. - /// The transposed destination block. - /// The source row count. - /// The source column count. - private static void Transpose(ReadOnlySpan source, Span destination, int sourceHeight, int sourceWidth) - { - if (Vector128.IsHardwareAccelerated) - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - - // Every supported HEVC transform dimension is a multiple of four. Complete four-by-four tiles therefore - // transpose the rectangular block without masked loads, partial stores, or access to row padding. - for (int y = 0; y < sourceHeight; y += 4) - { - for (int x = 0; x < sourceWidth; x += 4) - { - Vector128 row0 = Vector128.LoadUnsafe(ref sourceBase, (nuint)((y * sourceWidth) + x)); - Vector128 row1 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 1) * sourceWidth) + x)); - Vector128 row2 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 2) * sourceWidth) + x)); - Vector128 row3 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 3) * sourceWidth) + x)); - Av1Transform2dOperations.Transpose(ref row0, ref row1, ref row2, ref row3); - row0.StoreUnsafe(ref destinationBase, (nuint)((x * sourceHeight) + y)); - row1.StoreUnsafe(ref destinationBase, (nuint)(((x + 1) * sourceHeight) + y)); - row2.StoreUnsafe(ref destinationBase, (nuint)(((x + 2) * sourceHeight) + y)); - row3.StoreUnsafe(ref destinationBase, (nuint)(((x + 3) * sourceHeight) + y)); - } - } - - return; - } - - for (int y = 0; y < sourceHeight; y++) - { - for (int x = 0; x < sourceWidth; x++) - { - destination[(x * sourceHeight) + y] = source[(y * sourceWidth) + x]; - } - } - } - - /// - /// Adds a complete signed residual block to the predicted samples and clips to the component precision. - /// - /// The signed residual block in raster order. - /// The predicted samples beginning at the block origin. - /// The destination row stride in samples. - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component precision. - public static void AddResidual(ReadOnlySpan residual, Span destination, int destinationStride, int width, int height, int bitDepth) - { - int maximum = (1 << bitDepth) - 1; - for (int y = 0; y < height; y++) - { - ReadOnlySpan residualRow = residual.Slice(y * width, width); - Span destinationRow = destination.Slice(y * destinationStride, width); - ref int residualBase = ref MemoryMarshal.GetReference(residualRow); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destinationRow); - int x = 0; - - if (Vector256.IsHardwareAccelerated) - { - // Eight UInt16 predictions widen into one Int32 vector so residual addition cannot overflow sample - // storage. Narrowing occurs only after clipping and stores exactly the eight logical destination values. - int oneVectorFromEnd = width - Vector256.Count; - for (; x <= oneVectorFromEnd; x += Vector256.Count) - { - Vector128 predicted16 = Vector128.LoadUnsafe(ref destinationBase, (nuint)x); - Vector256 predicted = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32(); - Vector256 reconstructed = Vector256.Clamp( - predicted + Vector256.LoadUnsafe(ref residualBase, (nuint)x), - Vector256.Zero, - Vector256.Create(maximum)); - - Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32()).StoreUnsafe(ref destinationBase, (nuint)x); - } - } - - if (Vector128.IsHardwareAccelerated) - { - // The four-sample path uses exact 64-bit loads and stores; it does not depend on writable row padding. - int oneVectorFromEnd = width - Vector128.Count; - for (; x <= oneVectorFromEnd; x += Vector128.Count) - { - ulong packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, x))); - Vector128 predicted = Vector128.WidenLower(Vector128.CreateScalar(packed).AsUInt16()).AsInt32(); - Vector128 reconstructed = Vector128.Clamp( - predicted + Vector128.LoadUnsafe(ref residualBase, (nuint)x), - Vector128.Zero, - Vector128.Create(maximum)); - - Vector128 narrowed = Vector128.Narrow(reconstructed.AsUInt32(), Vector128.Zero); - Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, x)), narrowed.AsUInt64().ToScalar()); - } - } - - for (; x < width; x++) - { - destinationRow[x] = (ushort)Math.Clamp(destinationRow[x] + residualRow[x], 0, maximum); - } - } - } - - /// - /// Applies HEVC's rounded right shift and inclusive clipping to a 512-bit vector. - /// - /// The unnormalized transform values. - /// The right-shift count. - /// The inclusive result minimum. - /// The inclusive result maximum. - /// The normalized and clipped values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 RoundShiftAndClamp(Vector512 value, int shift, int minimum, int maximum) - => Vector512.Clamp((value + Vector512.Create(1 << (shift - 1))) >> shift, Vector512.Create(minimum), Vector512.Create(maximum)); - - /// - /// Applies HEVC's rounded right shift and inclusive clipping to a 256-bit vector. - /// - /// The unnormalized transform values. - /// The right-shift count. - /// The inclusive result minimum. - /// The inclusive result maximum. - /// The normalized and clipped values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 RoundShiftAndClamp(Vector256 value, int shift, int minimum, int maximum) - => Vector256.Clamp((value + Vector256.Create(1 << (shift - 1))) >> shift, Vector256.Create(minimum), Vector256.Create(maximum)); - - /// - /// Applies HEVC's rounded right shift and inclusive clipping to a 128-bit vector. - /// - /// The unnormalized transform values. - /// The right-shift count. - /// The inclusive result minimum. - /// The inclusive result maximum. - /// The normalized and clipped values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 RoundShiftAndClamp(Vector128 value, int shift, int minimum, int maximum) - => Vector128.Clamp((value + Vector128.Create(1 << (shift - 1))) >> shift, Vector128.Create(minimum), Vector128.Create(maximum)); - - /// - /// Applies HEVC's rounded right shift and inclusive clipping to one scalar value. - /// - /// The unnormalized transform value. - /// The right-shift count. - /// The inclusive result minimum. - /// The inclusive result maximum. - /// The normalized and clipped value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RoundShiftAndClamp(int value, int shift, int minimum, int maximum) - => Math.Clamp((value + (1 << (shift - 1))) >> shift, minimum, maximum); -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operator.cs deleted file mode 100644 index 8b4e814af..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operator.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Defines the HEVC inverse-transform operator contract. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// Defines one closed inverse-transform operation selected by the transform-unit syntax. - /// - private interface IHevcInverseTransformOperator - { - /// - /// Gets the transform side in samples. - /// - public static abstract int Size { get; } - - /// - /// Gets a value indicating whether the transform uses the partial-butterfly factorization. - /// - public static abstract bool UsesButterfly { get; } - - /// - /// Gets one inverse-transform matrix coefficient. - /// - /// The frequency-domain coordinate. - /// The spatial-domain coordinate. - /// The signed transform coefficient. - public static abstract int GetCoefficient(int frequency, int position); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.cs deleted file mode 100644 index 2ab255f5f..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.cs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Common.Helpers; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Applies HEVC inverse transforms and reconstructs predicted samples. -/// -internal static partial class HevcInverseTransformer -{ - /// - /// The signed residual precision used after the second inverse-transform pass. - /// - private const int ResidualPrecision = 16; - - /// - /// Gets the scratch length required for the specified rectangular transform block. - /// - /// The base-two logarithm of the transform-block width. - /// The base-two logarithm of the transform-block height. - /// The required number of signed thirty-two-bit elements. - public static int GetScratchLength(int log2Width, int log2Height) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Width, 2, 5, nameof(log2Width)); - DebugGuard.MustBeBetweenOrEqualTo(log2Height, 2, 5, nameof(log2Height)); - return 2 << (log2Width + log2Height); - } - - /// - /// Reconstructs one transform block by adding its inverse-transformed residual to the predicted samples. - /// - /// The dequantized transform coefficients in raster order. - /// The predicted samples beginning at the transform-block origin. - /// The destination row stride in samples. - /// The base-two logarithm of the transform-block width. - /// The base-two logarithm of the transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// Whether the four-by-four luma intra block uses the discrete sine transform. - /// The caller-owned scratch returned by . - public static void TransformAdd( - ReadOnlySpan coefficients, - Span destination, - int destinationStride, - int log2Width, - int log2Height, - int bitDepth, - int maxTransformDynamicRange, - bool useDiscreteSineTransform, - Span scratch) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Width, 2, 5, nameof(log2Width)); - DebugGuard.MustBeBetweenOrEqualTo(log2Height, 2, 5, nameof(log2Height)); - DebugGuard.MustBeBetweenOrEqualTo(bitDepth, 8, 16, nameof(bitDepth)); - DebugGuard.IsTrue(!useDiscreteSineTransform || (log2Width == 2 && log2Height == 2), "The HEVC inverse DST is defined only for four-by-four blocks."); - - int width = 1 << log2Width; - int height = 1 << log2Height; - int sampleCount = width * height; - DebugGuard.IsTrue(coefficients.Length >= sampleCount, "The coefficient span is shorter than the transform block."); - DebugGuard.IsTrue(scratch.Length >= sampleCount * 2, "The scratch span is shorter than the inverse-transform requirement."); - - Span first = scratch[..sampleCount]; - Span second = scratch.Slice(sampleCount, sampleCount); - Span residual = TransformCore( - coefficients[..sampleCount], - first, - second, - width, - height, - bitDepth, - maxTransformDynamicRange, - useDiscreteSineTransform); - - AddResidual(residual, destination, destinationStride, width, height, bitDepth); - } - - /// - /// Applies one two-dimensional inverse transform and writes signed residual samples in raster order. - /// - /// The dequantized transform coefficients in raster order. - /// The destination residual samples in raster order. - /// The base-two logarithm of the transform-block width. - /// The base-two logarithm of the transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// Whether the four-by-four luma intra block uses the discrete sine transform. - /// The caller-owned scratch returned by . - public static void Transform( - ReadOnlySpan coefficients, - Span residual, - int log2Width, - int log2Height, - int bitDepth, - int maxTransformDynamicRange, - bool useDiscreteSineTransform, - Span scratch) - { - int width = 1 << log2Width; - int height = 1 << log2Height; - int sampleCount = width * height; - DebugGuard.IsTrue(residual.Length >= sampleCount, "The residual span is shorter than the transform block."); - DebugGuard.IsTrue(scratch.Length >= sampleCount * 2, "The scratch span is shorter than the inverse-transform requirement."); - - Span transformed = TransformCore( - coefficients[..sampleCount], - scratch[..sampleCount], - scratch.Slice(sampleCount, sampleCount), - width, - height, - bitDepth, - maxTransformDynamicRange, - useDiscreteSineTransform); - - transformed.CopyTo(residual); - } - - /// - /// Dispatches both separable transform passes through closed operators selected from the block dimensions. - /// - /// The complete dequantized coefficient block. - /// The first full-block scratch buffer. - /// The second full-block scratch buffer. - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// Whether both four-point passes use the discrete sine transform. - /// The scratch buffer containing the raster-ordered residual. - private static Span TransformCore( - ReadOnlySpan coefficients, - Span first, - Span second, - int width, - int height, - int bitDepth, - int maxTransformDynamicRange, - bool useDiscreteSineTransform) - { - int dynamicMinimum = -(1 << maxTransformDynamicRange); - int dynamicMaximum = (1 << maxTransformDynamicRange) - 1; - - // HEVC moves one normalization bit from the second pass to the first. The intermediate clip therefore - // belongs after the vertical pass and must not be combined with the final residual clipping operation. - Span vertical = TransformDimension( - coefficients, - first, - second, - height, - width, - 7, - dynamicMinimum, - dynamicMaximum, - useDiscreteSineTransform); - - Span horizontalInput = vertical.Overlaps(first) ? second : first; - Transpose(vertical, horizontalInput, height, width); - - Span horizontalWorkspace = horizontalInput.Overlaps(first) ? second : first; - int secondShift = maxTransformDynamicRange + 5 - bitDepth; - Span horizontal = TransformDimension( - horizontalInput, - horizontalWorkspace, - horizontalInput, - width, - height, - secondShift, - -(1 << (ResidualPrecision - 1)), - (1 << (ResidualPrecision - 1)) - 1, - useDiscreteSineTransform); - - Span residual = horizontal.Overlaps(first) ? second : first; - Transpose(horizontal, residual, width, height); - return residual; - } - - /// - /// Selects the statically specialized operator for one transform dimension. - /// - /// The frequency rows followed by contiguous independent lines. - /// The initial operator output buffer. - /// The alternate combination buffer. - /// The transform dimension. - /// The number of independent lines transformed together. - /// The rounded right shift applied to the spatial results. - /// The inclusive output minimum. - /// The inclusive output maximum. - /// Whether the four-point pass uses the discrete sine transform. - /// The buffer containing spatial rows followed by contiguous independent lines. - private static Span TransformDimension( - ReadOnlySpan source, - Span initial, - Span alternate, - int size, - int lineCount, - int shift, - int minimum, - int maximum, - bool useDiscreteSineTransform) - => (size, useDiscreteSineTransform) switch - { - (4, true) => TransformDimension(source, initial, alternate, lineCount, shift, minimum, maximum), - (4, false) => TransformDimension(source, initial, alternate, lineCount, shift, minimum, maximum), - (8, _) => TransformDimension(source, initial, alternate, lineCount, shift, minimum, maximum), - (16, _) => TransformDimension(source, initial, alternate, lineCount, shift, minimum, maximum), - _ => TransformDimension(source, initial, alternate, lineCount, shift, minimum, maximum) - }; - - /// - /// Invokes one statically selected inverse-transform operator. - /// - /// The selected inverse-transform operator. - /// The frequency rows followed by contiguous independent lines. - /// The initial operator output buffer. - /// The alternate combination buffer. - /// The number of independent lines transformed together. - /// The rounded right shift applied to the spatial results. - /// The inclusive output minimum. - /// The inclusive output maximum. - /// The buffer containing spatial rows followed by contiguous independent lines. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span TransformDimension( - ReadOnlySpan source, - Span initial, - Span alternate, - int lineCount, - int shift, - int minimum, - int maximum) - where TOperator : struct, IHevcInverseTransformOperator - { - if (!TOperator.UsesButterfly) - { - TransformDense(source, initial, lineCount, shift, minimum, maximum); - return initial; - } - - PopulateButterflyGroups(source, initial, lineCount); - return CombineButterflyGroups(initial, alternate, lineCount, shift, minimum, maximum); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnit.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnit.cs deleted file mode 100644 index 5119e2144..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnit.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains one decoded HEVC network abstraction layer unit. -/// -internal sealed class HevcNalUnit -{ - /// - /// Initializes a new instance of the class. - /// - /// The complete NAL unit, including its two-byte header. - /// The NAL header or encoded payload is malformed. - public HevcNalUnit(ReadOnlySpan data) - { - this.Header = HevcNalUnitHeader.Parse(data); - - // Container and configuration NAL units carry EBSP bytes. Decode them once at the boundary so every - // parameter-set and slice parser observes the same validated RBSP representation. - ReadOnlySpan encodedPayload = data[2..]; - byte[] rbspBuffer = new byte[encodedPayload.Length]; - int rbspLength = HevcRbspDecoder.Decode( - encodedPayload, - rbspBuffer, - out ReadOnlyMemory emulationPreventionBytePositions); - - this.EncodedPayloadLength = encodedPayload.Length; - this.Rbsp = rbspBuffer.AsMemory(0, rbspLength); - this.EmulationPreventionBytePositions = emulationPreventionBytePositions; - } - - /// - /// Gets the decoded two-byte NAL-unit header. - /// - public HevcNalUnitHeader Header { get; } - - /// - /// Gets the raw byte sequence payload after removal of emulation-prevention bytes. - /// - public ReadOnlyMemory Rbsp { get; } - - /// - /// Gets the encoded byte-sequence payload length before removal of emulation-prevention bytes. - /// - public int EncodedPayloadLength { get; } - - /// - /// Gets the zero-based encoded-payload positions of removed emulation-prevention bytes. - /// - public ReadOnlyMemory EmulationPreventionBytePositions { get; } -} - -/// -/// Removes HEVC emulation-prevention bytes from an encoded raw byte sequence payload. -/// -internal static class HevcRbspDecoder -{ - /// - /// Decodes an encoded byte sequence payload into a raw byte sequence payload. - /// - /// The NAL payload following the two-byte header. - /// A buffer at least as long as . - /// - /// Receives the zero-based encoded-payload positions of removed emulation-prevention bytes. - /// - /// The number of decoded bytes written to . - /// - /// The payload contains a forbidden start-code-like byte sequence or an invalid emulation-prevention byte. - /// - public static int Decode( - ReadOnlySpan encodedPayload, - Span destination, - out ReadOnlyMemory emulationPreventionBytePositions) - { - DebugGuard.MustBeGreaterThanOrEqualTo(destination.Length, encodedPayload.Length, nameof(destination)); - - int destinationOffset = 0; - int preventionByteCount = 0; - int consecutiveZeroBytes = 0; - int[]? rentedPositions = null; - Span preventionBytePositions = []; - try - { - for (int sourceOffset = 0; sourceOffset < encodedPayload.Length; sourceOffset++) - { - byte value = encodedPayload[sourceOffset]; - - // HEVC section 7.3.1.1 forbids 00 00 00 through 00 00 02 in EBSP form. A 03 after two zeros is an - // emulation-prevention byte only when another byte in the range 00 through 03 follows it. - if (consecutiveZeroBytes == 2) - { - if (value < 3) - { - throw new InvalidImageContentException("The HEVC NAL unit contains a forbidden start-code-like byte sequence."); - } - - if (value == 3) - { - sourceOffset++; - if (sourceOffset == encodedPayload.Length || encodedPayload[sourceOffset] > 3) - { - throw new InvalidImageContentException("The HEVC NAL unit contains an invalid emulation-prevention byte."); - } - - if (preventionByteCount == preventionBytePositions.Length) - { - int[] expandedPositions = ArrayPool.Shared.Rent(preventionBytePositions.IsEmpty ? 16 : preventionBytePositions.Length * 2); - preventionBytePositions.CopyTo(expandedPositions); - if (rentedPositions is not null) - { - ArrayPool.Shared.Return(rentedPositions); - } - - rentedPositions = expandedPositions; - preventionBytePositions = rentedPositions; - } - - preventionBytePositions[preventionByteCount++] = sourceOffset - 1; - value = encodedPayload[sourceOffset]; - consecutiveZeroBytes = 0; - } - } - - destination[destinationOffset++] = value; - consecutiveZeroBytes = value == 0 ? consecutiveZeroBytes + 1 : 0; - } - - int[] retainedPositions = preventionByteCount == 0 - ? [] - : GC.AllocateUninitializedArray(preventionByteCount); - - preventionBytePositions[..preventionByteCount].CopyTo(retainedPositions); - emulationPreventionBytePositions = retainedPositions; - return destinationOffset; - } - finally - { - if (rentedPositions is not null) - { - ArrayPool.Shared.Return(rentedPositions); - } - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnitHeader.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnitHeader.cs deleted file mode 100644 index 3c6ee7921..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcNalUnitHeader.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the type, layer, and temporal identifier encoded by an HEVC NAL-unit header. -/// -internal readonly struct HevcNalUnitHeader -{ - /// - /// Initializes a new instance of the struct. - /// - /// The six-bit NAL-unit type. - /// The six-bit layer identifier. - /// The zero-based temporal identifier. - private HevcNalUnitHeader(byte nalUnitType, byte layerId, byte temporalId) - { - this.NalUnitType = nalUnitType; - this.LayerId = layerId; - this.TemporalId = temporalId; - } - - /// - /// Gets the six-bit NAL-unit type. - /// - public byte NalUnitType { get; } - - /// - /// Gets the six-bit layer identifier. - /// - public byte LayerId { get; } - - /// - /// Gets the zero-based temporal identifier. - /// - public byte TemporalId { get; } - - /// - /// Gets a value indicating whether the NAL unit contains coded slice-segment data. - /// - public bool IsVideoCodingLayer => this.NalUnitType <= 31; - - /// - /// Gets a value indicating whether the NAL unit begins an instantaneous decoder refresh picture. - /// - public bool IsInstantaneousDecoderRefresh => this.NalUnitType is 19 or 20; - - /// - /// Reads and validates an HEVC NAL-unit header. - /// - /// The complete NAL unit beginning with its two-byte header. - /// The decoded header. - /// - /// The header is truncated, its forbidden bit is set, or its temporal identifier is reserved. - /// - public static HevcNalUnitHeader Parse(ReadOnlySpan data) - { - if (data.Length < 2) - { - throw new InvalidImageContentException("The HEVC NAL-unit header is truncated."); - } - - // Use the same bounded MSB-first reader as the RBSP parsers so header truncation and field ordering have - // one behavior model instead of a second set of shifts and masks. - HevcBitReader reader = new(data[..2]); - bool forbiddenZeroBit = reader.ReadFlag(); - byte nalUnitType = (byte)reader.ReadBits(6); - byte layerId = (byte)reader.ReadBits(6); - byte temporalIdPlusOne = (byte)reader.ReadBits(3); - if (forbiddenZeroBit || temporalIdPlusOne == 0) - { - throw new InvalidImageContentException("The HEVC NAL-unit header is invalid."); - } - - return new HevcNalUnitHeader(nalUnitType, layerId, (byte)(temporalIdPlusOne - 1)); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcParameterSetSyntax.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcParameterSetSyntax.cs deleted file mode 100644 index 90a3fbab3..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcParameterSetSyntax.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Provides shared bounded syntax operations used by HEVC parameter-set readers. -/// -internal static class HevcParameterSetSyntax -{ - /// - /// Gets the horizontal conformance-window unit for an HEVC chroma layout. - /// - /// The chroma-format identifier. - /// Whether 4:4:4 components are coded as separate color planes. - /// The horizontal unit in luma samples. - public static int GetCropUnitWidth(byte chromaFormat, bool separateColorPlane) - => !separateColorPlane && chromaFormat is 1 or 2 ? 2 : 1; - - /// - /// Gets the vertical conformance-window unit for an HEVC chroma layout. - /// - /// The chroma-format identifier. - /// Whether 4:4:4 components are coded as separate color planes. - /// The vertical unit in luma samples. - public static int GetCropUnitHeight(byte chromaFormat, bool separateColorPlane) - => !separateColorPlane && chromaFormat == 1 ? 2 : 1; - - /// - /// Gets the number of coding-tree blocks needed to cover one coded picture dimension. - /// - /// The coded luma-sample count. - /// The base-two logarithm of the coding-tree-block size. - /// The covering coding-tree-block count. - public static int GetCodingTreeBlockCount(int sampleCount, int codingTreeBlockLog2) - => ((sampleCount - 1) >> codingTreeBlockLog2) + 1; - - /// - /// Gets the number of bits required to represent values below a positive exclusive upper bound. - /// - /// The positive exclusive upper bound. - /// The ceiling of the base-two logarithm, with zero returned for an upper bound of one. - public static int GetCeilingLog2(int exclusiveUpperBound) - { - DebugGuard.MustBeGreaterThan(exclusiveUpperBound, 0, nameof(exclusiveUpperBound)); - - int bitCount = 0; - int remaining = exclusiveUpperBound - 1; - while (remaining > 0) - { - bitCount++; - remaining >>= 1; - } - - return bitCount; - } - - /// - /// Reads a signed chroma quantization-parameter offset. - /// - /// The HEVC syntax reader. - /// The decoded offset in the registered range from negative twelve through twelve. - /// The offset is outside its registered range. - public static int ReadQuantizationParameterOffset(ref HevcBitReader reader) - { - int offset = reader.ReadSignedExpGolomb(); - if (offset is < -12 or > 12) - { - throw new InvalidImageContentException("The HEVC chroma quantization-parameter offset is invalid."); - } - - return offset; - } - - /// - /// Reads a signed deblocking-filter threshold offset. - /// - /// The HEVC syntax reader. - /// The decoded half-offset in the registered range from negative six through six. - /// The offset is outside its registered range. - public static int ReadDeblockingFilterOffset(ref HevcBitReader reader) - { - int offset = reader.ReadSignedExpGolomb(); - if (offset is < -6 or > 6) - { - throw new InvalidImageContentException("The HEVC deblocking-filter offset is invalid."); - } - - return offset; - } - - /// - /// Consumes hypothetical-reference-decoder syntax without adding playback state to the still-image model. - /// - /// The parameter-set raw byte sequence payload reader. - /// Whether common HRD flags are coded for this parameter set. - /// The highest declared temporal sublayer index. - /// The effective NAL HRD presence flag. - /// The effective VCL HRD presence flag. - /// The effective sub-picture HRD presence flag. - /// The HRD syntax is truncated or exceeds its registered bounds. - public static void SkipHrdParameters( - ref HevcBitReader reader, - bool commonInformationPresent, - int maxSubLayersMinusOne, - ref bool nalHrdParametersPresent, - ref bool vclHrdParametersPresent, - ref bool subPictureHrdParametersPresent) - { - if (commonInformationPresent) - { - nalHrdParametersPresent = reader.ReadFlag(); - vclHrdParametersPresent = reader.ReadFlag(); - subPictureHrdParametersPresent = false; - if (nalHrdParametersPresent || vclHrdParametersPresent) - { - subPictureHrdParametersPresent = reader.ReadFlag(); - if (subPictureHrdParametersPresent) - { - reader.ReadBits(8); - reader.ReadBits(5); - reader.ReadFlag(); - reader.ReadBits(5); - } - - reader.ReadBits(4); - reader.ReadBits(4); - if (subPictureHrdParametersPresent) - { - reader.ReadBits(4); - } - - reader.ReadBits(5); - reader.ReadBits(5); - reader.ReadBits(5); - } - } - - for (int subLayer = 0; subLayer <= maxSubLayersMinusOne; subLayer++) - { - bool fixedPictureRateGeneral = reader.ReadFlag(); - bool fixedPictureRateWithinCvs = fixedPictureRateGeneral || reader.ReadFlag(); - bool lowDelayHrd = false; - if (fixedPictureRateWithinCvs) - { - reader.ReadUnsignedExpGolomb(); - } - else - { - lowDelayHrd = reader.ReadFlag(); - } - - uint cpbCountMinusOne = 0; - if (!lowDelayHrd) - { - cpbCountMinusOne = reader.ReadUnsignedExpGolomb(); - if (cpbCountMinusOne > 31) - { - throw new InvalidImageContentException("The HEVC HRD syntax declares too many coded-picture buffers."); - } - } - - for (int hrdKind = 0; hrdKind < 2; hrdKind++) - { - bool parametersPresent = hrdKind == 0 ? nalHrdParametersPresent : vclHrdParametersPresent; - if (!parametersPresent) - { - continue; - } - - for (uint cpbIndex = 0; cpbIndex <= cpbCountMinusOne; cpbIndex++) - { - reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - if (subPictureHrdParametersPresent) - { - reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - } - - reader.ReadFlag(); - } - } - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs deleted file mode 100644 index 5a4753ca3..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Owns the native-precision luma and chroma sample planes for one reconstructed HEVC still picture. -/// -internal sealed class HevcPictureBuffer : IDisposable -{ - /// - /// The horizontal chroma subsampling shift. - /// - private readonly int chromaSubsamplingX; - - /// - /// The vertical chroma subsampling shift. - /// - private readonly int chromaSubsamplingY; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing the image memory allocator. - /// The coded dimensions, precision, and chroma layout. - public HevcPictureBuffer(Configuration configuration, HevcSequenceParameterSet sequenceParameterSet) - : this( - configuration, - sequenceParameterSet.Width, - sequenceParameterSet.Height, - sequenceParameterSet.BitDepthLuma, - sequenceParameterSet.BitDepthChroma, - sequenceParameterSet.ChromaFormat, - sequenceParameterSet.SeparateColorPlaneFlag, - 1 << sequenceParameterSet.MinCodingBlockLog2) - { - } - - /// - /// Initializes a new instance of the class for encoder-owned component planes. - /// - /// The configuration providing the image memory allocator. - /// The coded luma width. - /// The coded luma height. - /// The luma sample precision. - /// The chroma sample precision. - /// The HEVC chroma-format identifier. - /// Whether 4:4:4 components are coded as separate color planes. - /// The luma sample alignment applied to the owned reconstruction planes. - public HevcPictureBuffer( - Configuration configuration, - int width, - int height, - int bitDepthLuma, - int bitDepthChroma, - byte chromaFormat, - bool separateColorPlane, - int storageAlignment = 1) - { - this.Width = width; - this.Height = height; - this.BitDepthLuma = bitDepthLuma; - this.BitDepthChroma = bitDepthChroma; - this.ChromaFormat = chromaFormat; - this.SeparateColorPlane = separateColorPlane; - - // Separate color planes are independently coded at full resolution even though chroma_format_idc is 4:4:4. - this.chromaSubsamplingX = !this.SeparateColorPlane && this.ChromaFormat is 1 or 2 ? 1 : 0; - this.chromaSubsamplingY = !this.SeparateColorPlane && this.ChromaFormat == 1 ? 1 : 0; - int storageWidth = DivideCeilingByPowerOfTwo(this.Width, BitOperations.Log2((uint)storageAlignment)) * storageAlignment; - int storageHeight = DivideCeilingByPowerOfTwo(this.Height, BitOperations.Log2((uint)storageAlignment)) * storageAlignment; - Buffer2D? luma = null; - Buffer2D? chromaBlue = null; - Buffer2D? chromaRed = null; - try - { - luma = configuration.MemoryAllocator.Allocate2D(storageWidth, storageHeight); - if (this.ChromaFormat != 0) - { - int chromaWidth = DivideCeilingByPowerOfTwo(storageWidth, this.chromaSubsamplingX); - int chromaHeight = DivideCeilingByPowerOfTwo(storageHeight, this.chromaSubsamplingY); - - chromaBlue = configuration.MemoryAllocator.Allocate2D(chromaWidth, chromaHeight); - chromaRed = configuration.MemoryAllocator.Allocate2D(chromaWidth, chromaHeight); - } - - this.Luma = luma; - this.ChromaBlue = chromaBlue; - this.ChromaRed = chromaRed; - } - catch - { - // Construction transfers no plane ownership when a later rent fails, so unwind the unpublished owners - // here instead of relying on Dispose being reachable through a fully constructed picture buffer. - chromaRed?.Dispose(); - chromaBlue?.Dispose(); - luma?.Dispose(); - throw; - } - } - - /// - /// Gets the coded luma width in samples. - /// - public int Width { get; } - - /// - /// Gets the coded luma height in samples. - /// - public int Height { get; } - - /// - /// Gets the luma sample precision in bits. - /// - public int BitDepthLuma { get; } - - /// - /// Gets the chroma sample precision in bits. - /// - public int BitDepthChroma { get; } - - /// - /// Gets the HEVC chroma-format identifier. - /// - public byte ChromaFormat { get; } - - /// - /// Gets a value indicating whether the three planes are coded as independent full-resolution color planes. - /// - public bool SeparateColorPlane { get; } - - /// - /// Gets the luma or first separate-color-plane allocation. - /// - public Buffer2D Luma { get; } - - /// - /// Gets the blue-difference chroma or second separate-color-plane allocation. - /// - public Buffer2D? ChromaBlue { get; } - - /// - /// Gets the red-difference chroma or third separate-color-plane allocation. - /// - public Buffer2D? ChromaRed { get; } - - /// - /// Gets the horizontal chroma subsampling shift for the selected plane. - /// - /// The reconstruction plane. - /// Zero for luma and full-resolution planes; otherwise, the chroma shift. - public int GetSubsamplingX(HevcPlane plane) => plane == HevcPlane.Y ? 0 : this.chromaSubsamplingX; - - /// - /// Gets the vertical chroma subsampling shift for the selected plane. - /// - /// The reconstruction plane. - /// Zero for luma and full-resolution planes; otherwise, the chroma shift. - public int GetSubsamplingY(HevcPlane plane) => plane == HevcPlane.Y ? 0 : this.chromaSubsamplingY; - - /// - /// Gets the sample precision for the selected reconstruction plane. - /// - /// The reconstruction plane. - /// The plane sample precision in bits. - public int GetBitDepth(HevcPlane plane) => plane == HevcPlane.Y ? this.BitDepthLuma : this.BitDepthChroma; - - /// - /// Gets the selected plane width in samples. - /// - /// The reconstruction plane. - /// The coded plane width. - public int GetWidth(HevcPlane plane) => DivideCeilingByPowerOfTwo(this.Width, this.GetSubsamplingX(plane)); - - /// - /// Gets the selected plane height in samples. - /// - /// The reconstruction plane. - /// The coded plane height. - public int GetHeight(HevcPlane plane) => DivideCeilingByPowerOfTwo(this.Height, this.GetSubsamplingY(plane)); - - /// - /// Gets one coded row from the selected reconstruction plane. - /// - /// The reconstruction plane. - /// The zero-based row index in plane samples. - /// The complete coded plane row. - public Span GetRowSpan(HevcPlane plane, int row) - => plane switch - { - HevcPlane.Y => this.Luma.DangerousGetRowSpan(row), - HevcPlane.Cb => this.ChromaBlue!.DangerousGetRowSpan(row), - _ => this.ChromaRed!.DangerousGetRowSpan(row), - }; - - /// - /// Copies the complete coded component planes to another picture buffer with the same dimensions and chroma layout. - /// - /// The destination picture buffer. - public void CopyTo(HevcPictureBuffer destination) - { - int planeCount = this.ChromaFormat == 0 ? 1 : 3; - for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) - { - HevcPlane plane = (HevcPlane)planeIndex; - int width = this.GetWidth(plane); - int height = this.GetHeight(plane); - for (int row = 0; row < height; row++) - { - this.GetRowSpan(plane, row)[..width].CopyTo(destination.GetRowSpan(plane, row)); - } - } - } - - /// - /// Releases the owned luma and chroma plane allocations. - /// - public void Dispose() - { - this.Luma.Dispose(); - this.ChromaBlue?.Dispose(); - this.ChromaRed?.Dispose(); - } - - /// - /// Divides a nonnegative sample count by a power of two with upward rounding. - /// - /// The sample count. - /// The base-two divisor logarithm. - /// The upward-rounded quotient. - private static int DivideCeilingByPowerOfTwo(int value, int shift) => (value + (1 << shift) - 1) >> shift; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Deblocking.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Deblocking.cs deleted file mode 100644 index 1d3970017..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Deblocking.cs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Implements picture-level HEVC deblocking traversal and threshold derivation. -/// -internal sealed partial class HevcPictureDecoder -{ - /// - /// Defines the orientation-dependent boundary lookup and four-sample filter dispatch. - /// - private interface IDeblockingDirection - { - /// - /// Gets a value indicating whether the boundary is vertical. - /// - public static abstract bool IsVertical { get; } - - /// - /// Gets whether the selected four-sample segment is a transform or prediction boundary. - /// - /// The decoded deblocking boundary state. - /// The primary coding plane. - /// The segment left luma coordinate. - /// The segment top luma coordinate. - /// when the segment is a filter candidate; otherwise, . - public static abstract bool IsBoundary(HevcDeblockingState state, HevcPlane plane, int x, int y); - - /// - /// Applies the orientation-specific luma kernel. - /// - /// The reconstructed picture. - /// The component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled discontinuity threshold. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - public static abstract void FilterLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth); - - /// - /// Applies the orientation-specific chroma kernel. - /// - /// The reconstructed picture. - /// The Cb or Cr component plane. - /// The first Q-side sample X coordinate. - /// The first Q-side sample Y coordinate. - /// The scaled clipping threshold. - /// Whether the P-side block retains its original samples. - /// Whether the Q-side block retains its original samples. - /// The component sample precision. - /// The number of samples in the edge segment. - public static abstract void FilterChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count); - } - - /// - /// Gets the H.265 Table 8-20 clipping thresholds indexed by the effective boundary quantization parameter. - /// - private static ReadOnlySpan DeblockingTcTable => - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, - 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 24, - ]; - - /// - /// Gets the H.265 Table 8-20 discontinuity thresholds indexed by the effective boundary quantization parameter. - /// - private static ReadOnlySpan DeblockingBetaTable => - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, - 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, - ]; - - /// - /// Applies vertical edges across the complete picture before applying any horizontal edge. - /// - /// The picture tile mapping. - private void ApplyDeblockingFilter(in HevcTileLayout tileLayout) - { - this.ApplyDeblockingDirection(in tileLayout); - this.ApplyDeblockingDirection(in tileLayout); - } - - /// - /// Applies one closed deblocking direction to every coded component plane. - /// - /// The vertical or horizontal boundary operator. - /// The picture tile mapping. - private void ApplyDeblockingDirection(in HevcTileLayout tileLayout) - where TDirection : struct, IDeblockingDirection - { - if (this.sequenceParameterSet.SeparateColorPlaneFlag) - { - for (int planeIndex = 0; planeIndex < 3; planeIndex++) - { - this.ApplyLumaDeblocking((HevcPlane)planeIndex, planeIndex, in tileLayout); - } - - return; - } - - this.ApplyLumaDeblocking(HevcPlane.Y, 0, in tileLayout); - if (this.sequenceParameterSet.ChromaFormat != 0) - { - this.ApplyChromaDeblocking(HevcPlane.Cb, in tileLayout); - this.ApplyChromaDeblocking(HevcPlane.Cr, in tileLayout); - } - } - - /// - /// Applies one deblocking direction with the luma kernel to a primary coded plane. - /// - /// The vertical or horizontal boundary operator. - /// The primary coded plane. - /// The coding-tree state selected for the plane. - /// The picture tile mapping. - private void ApplyLumaDeblocking(HevcPlane plane, int codingTreeStateIndex, in HevcTileLayout tileLayout) - where TDirection : struct, IDeblockingDirection - { - int width = this.Picture.GetWidth(plane); - int height = this.Picture.GetHeight(plane); - int acrossLimit = TDirection.IsVertical ? width : height; - int alongLimit = TDirection.IsVertical ? height : width; - int bitDepth = this.Picture.GetBitDepth(plane); - int bitDepthScale = 1 << (bitDepth - 8); - int codingTreeBlockSize = 1 << this.sequenceParameterSet.CodingTreeBlockLog2; - HevcCodingTreeState codingTreeState = this.codingTreeStates[codingTreeStateIndex]; - - // Deblocking visits only eight-sample grid lines, but each candidate is retained at four-sample resolution - // because transform and prediction boundaries can differ between the two halves of that grid interval. - for (int edge = 8; edge < acrossLimit; edge += 8) - { - for (int along = 0; along < alongLimit; along += 4) - { - int x = TDirection.IsVertical ? edge : along; - int y = TDirection.IsVertical ? along : edge; - if (!TDirection.IsBoundary(this.deblockingState, plane, x, y)) - { - continue; - } - - int rasterAddress = ((y / codingTreeBlockSize) * tileLayout.Width) + (x / codingTreeBlockSize); - HevcLoopFilterRegion region = this.sampleAdaptiveOffsetState.GetLoopFilterRegion(rasterAddress, plane); - if (region.DeblockingFilterDisabled - || !this.IsDeblockingCtbBoundaryAvailable(rasterAddress, plane, x, y, codingTreeBlockSize, in tileLayout)) - { - continue; - } - - int pX = x - (TDirection.IsVertical ? 1 : 0); - int pY = y - (TDirection.IsVertical ? 0 : 1); - int qX = x; - int qY = y; - int quantizationParameterP = codingTreeState.GetQuantizationParameter(pX, pY); - int quantizationParameterQ = codingTreeState.GetQuantizationParameter(qX, qY); - int averageQuantizationParameter = (quantizationParameterP + quantizationParameterQ + 1) >> 1; - int tcIndex = Math.Clamp(averageQuantizationParameter + 2 + (region.DeblockingFilterTcOffsetDiv2 << 1), 0, 53); - int betaIndex = Math.Clamp(averageQuantizationParameter + (region.DeblockingFilterBetaOffsetDiv2 << 1), 0, 51); - int tc = DeblockingTcTable[tcIndex] * bitDepthScale; - int beta = DeblockingBetaTable[betaIndex] * bitDepthScale; - bool partPNoFilter = this.IsDeblockingSuppressed(codingTreeState, pX, pY); - bool partQNoFilter = this.IsDeblockingSuppressed(codingTreeState, qX, qY); - - TDirection.FilterLuma(this.Picture, plane, x, y, beta, tc, partPNoFilter, partQNoFilter, bitDepth); - } - } - } - - /// - /// Applies one deblocking direction with the chroma kernel to a combined Cb or Cr plane. - /// - /// The vertical or horizontal boundary operator. - /// The Cb or Cr component plane. - /// The picture tile mapping. - private void ApplyChromaDeblocking(HevcPlane plane, in HevcTileLayout tileLayout) - where TDirection : struct, IDeblockingDirection - { - int subsamplingX = this.Picture.GetSubsamplingX(plane); - int subsamplingY = this.Picture.GetSubsamplingY(plane); - int width = this.Picture.GetWidth(plane); - int height = this.Picture.GetHeight(plane); - int acrossLimit = TDirection.IsVertical ? width : height; - int alongLimit = TDirection.IsVertical ? height : width; - int alongSubsampling = TDirection.IsVertical ? subsamplingY : subsamplingX; - int segmentLength = 4 >> alongSubsampling; - int bitDepth = this.Picture.GetBitDepth(plane); - int bitDepthScale = 1 << (bitDepth - 8); - int codingTreeBlockSize = 1 << this.sequenceParameterSet.CodingTreeBlockLog2; - HevcCodingTreeState codingTreeState = this.codingTreeStates[0]; - - // Chroma deblocking uses eight-sample component-grid edges. A two-lane segment in subsampled directions still - // enters the SIMD kernel, but only its valid low lanes are committed because QP and suppression state can change next. - for (int edge = 8; edge < acrossLimit; edge += 8) - { - for (int along = 0; along < alongLimit; along += segmentLength) - { - int x = TDirection.IsVertical ? edge : along; - int y = TDirection.IsVertical ? along : edge; - int lumaX = x << subsamplingX; - int lumaY = y << subsamplingY; - if (!TDirection.IsBoundary(this.deblockingState, HevcPlane.Y, lumaX, lumaY)) - { - continue; - } - - int rasterAddress = ((lumaY / codingTreeBlockSize) * tileLayout.Width) + (lumaX / codingTreeBlockSize); - HevcLoopFilterRegion region = this.sampleAdaptiveOffsetState.GetLoopFilterRegion(rasterAddress, HevcPlane.Y); - if (region.DeblockingFilterDisabled - || !this.IsDeblockingCtbBoundaryAvailable( - rasterAddress, - HevcPlane.Y, - lumaX, - lumaY, - codingTreeBlockSize, - in tileLayout)) - { - continue; - } - - int pX = lumaX - (TDirection.IsVertical ? 1 : 0); - int pY = lumaY - (TDirection.IsVertical ? 0 : 1); - int qX = lumaX; - int qY = lumaY; - int quantizationParameterP = codingTreeState.GetQuantizationParameter(pX, pY); - int quantizationParameterQ = codingTreeState.GetQuantizationParameter(qX, qY); - int averageQuantizationParameter = (quantizationParameterP + quantizationParameterQ + 1) >> 1; - - // Chroma deblocking uses only the picture-level component offset. Slice offsets and the RExt - // coding-unit adjustment affect inverse quantization, but H.265 excludes both from tc derivation. - int componentOffset = plane == HevcPlane.Cb - ? this.pictureParameterSet.ChromaCbQuantizationParameterOffset - : this.pictureParameterSet.ChromaCrQuantizationParameterOffset; - - int chromaQuantizationParameter = HevcQuantizationParameters.GetChromaQuantizationParameter( - averageQuantizationParameter, - componentOffset, - 0, - this.sequenceParameterSet.ChromaFormat); - - int tcIndex = Math.Clamp(chromaQuantizationParameter + 2 + (region.DeblockingFilterTcOffsetDiv2 << 1), 0, 53); - int tc = DeblockingTcTable[tcIndex] * bitDepthScale; - bool partPNoFilter = this.IsDeblockingSuppressed(codingTreeState, pX, pY); - bool partQNoFilter = this.IsDeblockingSuppressed(codingTreeState, qX, qY); - - TDirection.FilterChroma(this.Picture, plane, x, y, tc, partPNoFilter, partQNoFilter, bitDepth, segmentLength); - } - } - } - - /// - /// Gets whether an edge crossing a coding-tree-block boundary is permitted by slice and tile rules. - /// - /// The vertical or horizontal boundary operator. - /// The Q-side coding-tree-block raster address. - /// The primary coding plane. - /// The edge luma X coordinate. - /// The edge luma Y coordinate. - /// The coding-tree-block side in luma samples. - /// The picture tile mapping. - /// for an internal or permitted external boundary; otherwise, . - private bool IsDeblockingCtbBoundaryAvailable( - int rasterAddress, - HevcPlane plane, - int x, - int y, - int codingTreeBlockSize, - in HevcTileLayout tileLayout) - where TDirection : struct, IDeblockingDirection - { - int acrossCoordinate = TDirection.IsVertical ? x : y; - if (acrossCoordinate % codingTreeBlockSize != 0) - { - return true; - } - - HevcLoopFilterBoundaryAvailability availability = this.sampleAdaptiveOffsetState.GetLoopFilterBoundaryAvailability( - rasterAddress, - plane, - tileLayout.Width, - tileLayout.Height, - this.pictureParameterSet.LoopFilterAcrossTilesEnabled); - - return TDirection.IsVertical ? availability.Left : availability.Above; - } - - /// - /// Gets whether PCM or transform-bypass syntax preserves one side of a filtered boundary. - /// - /// The coding-tree state for the selected primary plane. - /// The luma sample X coordinate. - /// The luma sample Y coordinate. - /// when the reconstructed side must not be modified; otherwise, . - private bool IsDeblockingSuppressed(HevcCodingTreeState state, int x, int y) - => (this.sequenceParameterSet.PcmLoopFilterDisabled && state.IsPcm(x, y)) - || (this.pictureParameterSet.TransquantizationBypassEnabled && state.IsTransquantBypass(x, y)); - - /// - /// Selects vertical boundary lookup and filtering. - /// - private readonly struct VerticalDeblockingDirection : IDeblockingDirection - { - /// - public static bool IsVertical => true; - - /// - public static bool IsBoundary(HevcDeblockingState state, HevcPlane plane, int x, int y) - => state.IsVerticalBoundary(plane, x, y); - - /// - public static void FilterLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth) - => HevcDeblockingFilter.FilterVerticalLuma(picture, plane, x, y, beta, tc, partPNoFilter, partQNoFilter, bitDepth); - - /// - public static void FilterChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count) - => HevcDeblockingFilter.FilterVerticalChroma(picture, plane, x, y, tc, partPNoFilter, partQNoFilter, bitDepth, count); - } - - /// - /// Selects horizontal boundary lookup and filtering. - /// - private readonly struct HorizontalDeblockingDirection : IDeblockingDirection - { - /// - public static bool IsVertical => false; - - /// - public static bool IsBoundary(HevcDeblockingState state, HevcPlane plane, int x, int y) - => state.IsHorizontalBoundary(plane, x, y); - - /// - public static void FilterLuma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int beta, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth) - => HevcDeblockingFilter.FilterHorizontalLuma(picture, plane, x, y, beta, tc, partPNoFilter, partQNoFilter, bitDepth); - - /// - public static void FilterChroma( - HevcPictureBuffer picture, - HevcPlane plane, - int x, - int y, - int tc, - bool partPNoFilter, - bool partQNoFilter, - int bitDepth, - int count) - => HevcDeblockingFilter.FilterHorizontalChroma(picture, plane, x, y, tc, partPNoFilter, partQNoFilter, bitDepth, count); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs deleted file mode 100644 index 754f94305..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Implements intra prediction, reconstructed-plane writes, and PCM sample reconstruction. -/// -internal sealed partial class HevcPictureDecoder -{ - /// - /// Reconstructs one packed intra-prediction block in caller-owned scratch. - /// - /// The reconstructed component plane. - /// The prediction-block left coordinate in component samples. - /// The prediction-block top coordinate in component samples. - /// The base-two logarithm of the square prediction-block side. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - /// Whether the governing coding unit bypasses inverse quantization and transform. - /// The packed predicted samples. - private Span PredictComponentBlock(HevcPlane plane, int x, int y, int log2Size, int regionId, int colorPlaneIndex, bool transquantBypass) - { - int size = 1 << log2Size; - int sampleCount = size * size; - int referenceLength = (size * 2) + 1; - Span scratch = this.predictionScratch.Memory.Span; - Span prediction = scratch[..sampleCount]; - Span top = scratch.Slice(MaximumTransformSampleCount, MaximumReferenceLength); - Span left = scratch.Slice(MaximumTransformSampleCount + MaximumReferenceLength, MaximumReferenceLength); - Span filteredTop = scratch.Slice(MaximumTransformSampleCount + (MaximumReferenceLength * 2), MaximumReferenceLength); - Span filteredLeft = scratch.Slice(MaximumTransformSampleCount + (MaximumReferenceLength * 3), MaximumReferenceLength); - int referenceScratchOffset = MaximumTransformSampleCount + (MaximumReferenceLength * 4); - int unitWidth = this.reconstructionState.GetUnitWidth(plane); - int unitHeight = this.reconstructionState.GetUnitHeight(plane); - int referenceScratchLength = HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitWidth); - Span referenceScratch = scratch.Slice(referenceScratchOffset, referenceScratchLength); - Span operationScratch = scratch[(referenceScratchOffset + referenceScratchLength)..]; - Span availability = this.availabilityScratch.Memory.Span; - int availabilityCount = this.reconstructionState.BuildReferenceAvailability( - plane, - x, - y, - log2Size, - regionId, - availability); - - HevcIntraPredictor.PrepareReferenceSamples( - this.Picture, - plane, - x, - y, - log2Size, - unitWidth, - unitHeight, - availability[..availabilityCount], - top, - left, - referenceScratch); - - int lumaX = x << this.Picture.GetSubsamplingX(plane); - int lumaY = y << this.Picture.GetSubsamplingY(plane); - bool useLumaSyntax = plane == HevcPlane.Y || this.sequenceParameterSet.SeparateColorPlaneFlag; - int mode = useLumaSyntax - ? this.intraPredictionStates[colorPlaneIndex].GetLumaMode(lumaX, lumaY) - : this.intraPredictionStates[colorPlaneIndex].GetEffectiveChromaMode(lumaX, lumaY); - - if (!useLumaSyntax && this.sequenceParameterSet.ChromaFormat == 2) - { - mode = HevcIntraPredictionMode.RemapChroma422(mode); - } - - // H.265 8.4.4.2.3 and 8.4.4.2.6 restrict prediction-edge filtering to luma blocks no larger than 16 samples. - // Implicit RDPCM bypasses that filtering for the lossless horizontal and vertical prediction modes. - bool filterPredictionEdges = useLumaSyntax - && size <= 16 - && !(transquantBypass - && this.sequenceParameterSet.ImplicitResidualDpcmEnabled - && (mode == HevcIntraPredictionMode.Horizontal || mode == HevcIntraPredictionMode.Vertical)); - - bool filterReferences = HevcIntraPredictor.ShouldFilterReferenceSamples( - useLumaSyntax ? HevcPlane.Y : plane, - mode, - log2Size, - this.sequenceParameterSet.ChromaFormat, - this.sequenceParameterSet.IntraSmoothingDisabled); - - ReadOnlySpan selectedTop = top[..referenceLength]; - ReadOnlySpan selectedLeft = left[..referenceLength]; - if (filterReferences) - { - // Normal three-tap smoothing extends to combined 4:4:4 chroma, but strong bilinear smoothing is a luma - // operation. Separate color planes use luma syntax and therefore retain the luma behavior. - bool useStrongSmoothing = useLumaSyntax && this.sequenceParameterSet.StrongIntraSmoothingEnabled; - - HevcIntraPredictor.FilterReferenceSamples( - selectedTop, - selectedLeft, - filteredTop, - filteredLeft, - log2Size, - this.Picture.GetBitDepth(plane), - useStrongSmoothing); - - selectedTop = filteredTop[..referenceLength]; - selectedLeft = filteredLeft[..referenceLength]; - } - - HevcIntraPredictor.Predict( - selectedTop, - selectedLeft, - prediction, - size, - log2Size, - mode, - this.Picture.GetBitDepth(plane), - filterPredictionEdges, - operationScratch); - - return prediction; - } - - /// - /// Copies one packed reconstructed block into the allocator-owned picture plane. - /// - /// The packed reconstructed samples. - /// The destination component plane. - /// The destination left coordinate. - /// The destination top coordinate. - /// The square block side. - private void CopyPredictionToPicture(ReadOnlySpan source, HevcPlane plane, int x, int y, int size) - { - for (int row = 0; row < size; row++) - { - source.Slice(row * size, size).CopyTo(this.Picture.GetRowSpan(plane, y + row)[x..]); - } - } - - /// - /// Reads and writes every raw sample in one PCM coding unit before arithmetic decoding restarts. - /// - /// The suspended entropy-substream reader. - /// The coding-unit left luma coordinate. - /// The coding-unit top luma coordinate. - /// The base-two logarithm of the coding-unit side. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - private void DecodePcmCodingUnit( - ref HevcCabacSyntaxReader reader, - int x, - int y, - int log2Size, - int regionId, - int colorPlaneIndex) - { - int size = 1 << log2Size; - if (this.sequenceParameterSet.SeparateColorPlaneFlag) - { - HevcPlane plane = (HevcPlane)colorPlaneIndex; - this.DecodePcmPlane(ref reader, plane, x, y, size, size, this.sequenceParameterSet.PcmBitDepthLuma, regionId); - return; - } - - this.DecodePcmPlane(ref reader, HevcPlane.Y, x, y, size, size, this.sequenceParameterSet.PcmBitDepthLuma, regionId); - if (this.sequenceParameterSet.ChromaFormat == 0) - { - return; - } - - int subsamplingX = this.Picture.GetSubsamplingX(HevcPlane.Cb); - int subsamplingY = this.Picture.GetSubsamplingY(HevcPlane.Cb); - int chromaWidth = size >> subsamplingX; - int chromaHeight = size >> subsamplingY; - int chromaX = x >> subsamplingX; - int chromaY = y >> subsamplingY; - this.DecodePcmPlane( - ref reader, - HevcPlane.Cb, - chromaX, - chromaY, - chromaWidth, - chromaHeight, - this.sequenceParameterSet.PcmBitDepthChroma, - regionId); - - this.DecodePcmPlane( - ref reader, - HevcPlane.Cr, - chromaX, - chromaY, - chromaWidth, - chromaHeight, - this.sequenceParameterSet.PcmBitDepthChroma, - regionId); - } - - /// - /// Reads one rectangular PCM component plane directly into the reconstructed picture. - /// - /// The suspended entropy-substream reader. - /// The destination component plane. - /// The destination left coordinate. - /// The destination top coordinate. - /// The component rectangle width. - /// The component rectangle height. - /// The PCM sample precision. - /// The current independent-slice and tile prediction region. - private void DecodePcmPlane( - ref HevcCabacSyntaxReader reader, - HevcPlane plane, - int x, - int y, - int width, - int height, - int bitDepth, - int regionId) - { - // PCM samples can use fewer bits than the reconstructed component. H.265 places those bits at the - // most-significant end of the component range, so the raw code value must be restored before filtering. - int bitDepthShift = this.Picture.GetBitDepth(plane) - bitDepth; - for (int row = 0; row < height; row++) - { - Span destination = this.Picture.GetRowSpan(plane, y + row).Slice(x, width); - for (int column = 0; column < width; column++) - { - destination[column] = (ushort)(reader.ReadPcmSample(bitDepth) << bitDepthShift); - } - } - - this.reconstructionState.MarkReconstructed(plane, x, y, width, height, regionId); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.SampleAdaptiveOffset.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.SampleAdaptiveOffset.cs deleted file mode 100644 index 358614382..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.SampleAdaptiveOffset.cs +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Implements sample-adaptive-offset syntax decoding and merge resolution. -/// -internal sealed partial class HevcPictureDecoder -{ - /// - /// Applies the resolved sample-adaptive offsets to every component after deblocking has completed. - /// - /// The immutable deblocked picture used to classify every sample. - /// The picture tile mapping used to derive coding-tree-block boundaries. - private void ApplySampleAdaptiveOffset(HevcPictureBuffer source, in HevcTileLayout tileLayout) - { - int codingTreeBlockSize = 1 << this.sequenceParameterSet.CodingTreeBlockLog2; - int planeCount = this.sequenceParameterSet.ChromaFormat == 0 ? 1 : 3; - for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) - { - HevcPlane plane = (HevcPlane)planeIndex; - HevcPlane regionPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? plane : HevcPlane.Y; - int subsamplingX = this.Picture.GetSubsamplingX(plane); - int subsamplingY = this.Picture.GetSubsamplingY(plane); - int blockWidth = codingTreeBlockSize >> subsamplingX; - int blockHeight = codingTreeBlockSize >> subsamplingY; - int planeWidth = this.Picture.GetWidth(plane); - int planeHeight = this.Picture.GetHeight(plane); - int offsetScaleLog2 = plane == HevcPlane.Y - ? this.pictureParameterSet.SampleAdaptiveOffsetScaleLumaLog2 - : this.pictureParameterSet.SampleAdaptiveOffsetScaleChromaLog2; - - for (int codingTreeBlockY = 0; codingTreeBlockY < tileLayout.Height; codingTreeBlockY++) - { - for (int codingTreeBlockX = 0; codingTreeBlockX < tileLayout.Width; codingTreeBlockX++) - { - int rasterAddress = (codingTreeBlockY * tileLayout.Width) + codingTreeBlockX; - HevcSampleAdaptiveOffsetParameters parameters = this.sampleAdaptiveOffsetState.Get(rasterAddress, plane); - if (parameters.Type == HevcSampleAdaptiveOffsetType.Off) - { - continue; - } - - HevcLoopFilterBoundaryAvailability availability = this.sampleAdaptiveOffsetState.GetLoopFilterBoundaryAvailability( - rasterAddress, - regionPlane, - tileLayout.Width, - tileLayout.Height, - this.pictureParameterSet.LoopFilterAcrossTilesEnabled); - - int x = codingTreeBlockX * blockWidth; - int y = codingTreeBlockY * blockHeight; - int width = Math.Min(blockWidth, planeWidth - x); - int height = Math.Min(blockHeight, planeHeight - y); - - // Every classification reads the immutable post-deblocking picture. Later CTBs can therefore never - // observe offsets already written by an earlier CTB, including across permitted slice and tile boundaries. - HevcSampleAdaptiveOffsetFilter.ApplyBlock( - source, - this.Picture, - plane, - x, - y, - width, - height, - in parameters, - offsetScaleLog2, - availability.Left, - availability.Right, - availability.Above, - availability.Below, - availability.AboveLeft, - availability.AboveRight, - availability.BelowLeft, - availability.BelowRight); - } - } - } - } - - /// - /// Decodes and resolves the sample-adaptive-offset parameters for one coding-tree block. - /// - /// The active entropy-substream reader. - /// The independent slice governing component enable flags. - /// The coding-tree block's raster-scan address. - /// The horizontal coding-tree-block coordinate. - /// The vertical coding-tree-block coordinate. - /// The current independent-slice and tile prediction region. - private void DecodeSampleAdaptiveOffset( - ref HevcCabacSyntaxReader reader, - HevcSliceSegmentHeader independentSlice, - int rasterAddress, - int codingTreeBlockX, - int codingTreeBlockY, - int regionId) - { - HevcPlane regionPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? (HevcPlane)independentSlice.ColorPlaneId : HevcPlane.Y; - bool lumaEnabled = independentSlice.SampleAdaptiveOffsetLumaEnabled == true; - bool chromaEnabled = independentSlice.SampleAdaptiveOffsetChromaEnabled == true; - if (!lumaEnabled && !chromaEnabled) - { - this.sampleAdaptiveOffsetState.SetRegion(rasterAddress, regionPlane, regionId); - return; - } - - int codingTreeBlockWidth = HevcParameterSetSyntax.GetCodingTreeBlockCount( - this.sequenceParameterSet.Width, - this.sequenceParameterSet.CodingTreeBlockLog2); - - int leftAddress = rasterAddress - 1; - bool leftAvailable = codingTreeBlockX > 0 && this.sampleAdaptiveOffsetState.IsInRegion(leftAddress, regionPlane, regionId); - bool mergeLeft = leftAvailable && reader.ReadSampleAdaptiveOffsetMerge(); - int aboveAddress = rasterAddress - codingTreeBlockWidth; - bool aboveAvailable = codingTreeBlockY > 0 && this.sampleAdaptiveOffsetState.IsInRegion(aboveAddress, regionPlane, regionId); - bool mergeAbove = !mergeLeft && aboveAvailable && reader.ReadSampleAdaptiveOffsetMerge(); - if (mergeLeft || mergeAbove) - { - int sourceAddress = mergeLeft ? leftAddress : aboveAddress; - this.CopySampleAdaptiveOffsetParameters(sourceAddress, rasterAddress, lumaEnabled, chromaEnabled, independentSlice.ColorPlaneId); - this.sampleAdaptiveOffsetState.SetRegion(rasterAddress, regionPlane, regionId); - return; - } - - if (this.sequenceParameterSet.SeparateColorPlaneFlag) - { - HevcPlane plane = (HevcPlane)independentSlice.ColorPlaneId; - this.sampleAdaptiveOffsetState.Set(rasterAddress, plane, ReadSampleAdaptiveOffsetParameters(ref reader, this.Picture.GetBitDepth(plane), -1)); - } - else - { - if (lumaEnabled) - { - this.sampleAdaptiveOffsetState.Set( - rasterAddress, - HevcPlane.Y, - ReadSampleAdaptiveOffsetParameters(ref reader, this.sequenceParameterSet.BitDepthLuma, -1)); - } - - if (chromaEnabled) - { - HevcSampleAdaptiveOffsetParameters chromaBlue = ReadSampleAdaptiveOffsetParameters( - ref reader, - this.sequenceParameterSet.BitDepthChroma, - -1); - - this.sampleAdaptiveOffsetState.Set(rasterAddress, HevcPlane.Cb, chromaBlue); - this.sampleAdaptiveOffsetState.Set( - rasterAddress, - HevcPlane.Cr, - ReadSampleAdaptiveOffsetParameters(ref reader, this.sequenceParameterSet.BitDepthChroma, (int)chromaBlue.Type)); - } - } - - this.sampleAdaptiveOffsetState.SetRegion(rasterAddress, regionPlane, regionId); - } - - /// - /// Copies resolved merge-source parameters for the components enabled by the current slice. - /// - /// The merge-source coding-tree-block address. - /// The current coding-tree-block address. - /// Whether the current slice enables luma sample-adaptive offset. - /// Whether the current slice enables chroma sample-adaptive offset. - /// The selected separate-color-plane identifier. - private void CopySampleAdaptiveOffsetParameters( - int sourceAddress, - int destinationAddress, - bool lumaEnabled, - bool chromaEnabled, - byte colorPlaneId) - { - if (this.sequenceParameterSet.SeparateColorPlaneFlag) - { - HevcPlane plane = (HevcPlane)colorPlaneId; - this.sampleAdaptiveOffsetState.Set(destinationAddress, plane, this.sampleAdaptiveOffsetState.Get(sourceAddress, plane)); - return; - } - - if (lumaEnabled) - { - this.sampleAdaptiveOffsetState.Set(destinationAddress, HevcPlane.Y, this.sampleAdaptiveOffsetState.Get(sourceAddress, HevcPlane.Y)); - } - - if (chromaEnabled) - { - this.sampleAdaptiveOffsetState.Set(destinationAddress, HevcPlane.Cb, this.sampleAdaptiveOffsetState.Get(sourceAddress, HevcPlane.Cb)); - this.sampleAdaptiveOffsetState.Set(destinationAddress, HevcPlane.Cr, this.sampleAdaptiveOffsetState.Get(sourceAddress, HevcPlane.Cr)); - } - } - - /// - /// Decodes one component's new or disabled sample-adaptive-offset mode. - /// - /// The active entropy-substream reader. - /// The component sample precision. - /// The Cb type inherited by Cr, or negative one when the type is signaled. - /// The resolved component parameters. - private static HevcSampleAdaptiveOffsetParameters ReadSampleAdaptiveOffsetParameters( - ref HevcCabacSyntaxReader reader, - int bitDepth, - int inheritedType) - { - int type = inheritedType >= 0 - ? inheritedType == (int)HevcSampleAdaptiveOffsetType.Off ? 0 : inheritedType == (int)HevcSampleAdaptiveOffsetType.Band ? 1 : 2 - : reader.ReadSampleAdaptiveOffsetType(); - - if (type == 0) - { - return default; - } - - int maximumOffset = (1 << (Math.Min(bitDepth, 10) - 5)) - 1; - int offset0 = reader.ReadSampleAdaptiveOffsetAbsolute(maximumOffset); - int offset1 = reader.ReadSampleAdaptiveOffsetAbsolute(maximumOffset); - int offset2 = reader.ReadSampleAdaptiveOffsetAbsolute(maximumOffset); - int offset3 = reader.ReadSampleAdaptiveOffsetAbsolute(maximumOffset); - if (type == 1) - { - offset0 = ApplySampleAdaptiveOffsetSign(ref reader, offset0); - offset1 = ApplySampleAdaptiveOffsetSign(ref reader, offset1); - offset2 = ApplySampleAdaptiveOffsetSign(ref reader, offset2); - offset3 = ApplySampleAdaptiveOffsetSign(ref reader, offset3); - return new HevcSampleAdaptiveOffsetParameters( - HevcSampleAdaptiveOffsetType.Band, - reader.ReadSampleAdaptiveOffsetBandPosition(), - offset0, - offset1, - offset2, - offset3, - 0); - } - - HevcSampleAdaptiveOffsetType edgeType = inheritedType >= 0 - ? (HevcSampleAdaptiveOffsetType)inheritedType - : (HevcSampleAdaptiveOffsetType)((int)HevcSampleAdaptiveOffsetType.EdgeHorizontal + reader.ReadSampleAdaptiveOffsetEdgeClass()); - - return new HevcSampleAdaptiveOffsetParameters(edgeType, 0, offset0, offset1, 0, -offset2, -offset3); - } - - /// - /// Applies an explicitly coded sign to a nonzero band-offset magnitude. - /// - /// The active entropy-substream reader. - /// The decoded unsigned magnitude. - /// The signed magnitude. - private static int ApplySampleAdaptiveOffsetSign(ref HevcCabacSyntaxReader reader, int magnitude) - => magnitude != 0 && reader.ReadSampleAdaptiveOffsetSign() ? -magnitude : magnitude; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs deleted file mode 100644 index ffe8e3457..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Implements transform-tree syntax, coefficient reconstruction, and intra sample reconstruction. -/// -internal sealed partial class HevcPictureDecoder -{ - /// - /// Decodes and reconstructs one transform-tree node. - /// - /// The active entropy-substream reader. - /// The luma and component rectangles at this transform depth. - /// The transform depth relative to the coding-unit root. - /// The smallest luma transform permitted in the coding unit. - /// Whether the coding unit has four luma prediction partitions. - /// Whether the coding unit bypasses inverse quantization and transform. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - /// The blue-difference coded-block flags inherited from the parent. - /// The red-difference coded-block flags inherited from the parent. - private void DecodeTransformTree( - ref HevcCabacSyntaxReader reader, - in HevcTransformUnitGeometry geometry, - int transformDepth, - int minimumTransformLog2, - bool usesNxNPartitions, - bool transquantBypass, - int regionId, - int colorPlaneIndex, - HevcCodedBlockFlags parentChromaBlueFlags, - HevcCodedBlockFlags parentChromaRedFlags) - { - int log2Size = geometry.Log2LumaSize; - HevcTransformComponentGeometry primaryGeometry = geometry.Primary; - HevcTransformComponentGeometry chromaBlueGeometry = geometry.ChromaBlue; - HevcTransformComponentGeometry chromaRedGeometry = geometry.ChromaRed; - bool split; - if (usesNxNPartitions && transformDepth == 0) - { - split = true; - } - else if (log2Size > this.sequenceParameterSet.MaxTransformBlockLog2) - { - split = true; - } - else if (log2Size == this.sequenceParameterSet.MinTransformBlockLog2 || log2Size == minimumTransformLog2) - { - split = false; - } - else - { - split = reader.ReadTransformSubdivision(log2Size); - } - - HevcCodedBlockFlags chromaBlueFlags = parentChromaBlueFlags; - HevcCodedBlockFlags chromaRedFlags = parentChromaRedFlags; - if (geometry.HasCombinedChroma) - { - chromaBlueFlags = DecodeChromaCodedBlockFlags( - ref reader, - in chromaBlueGeometry, - transformDepth, - split, - parentChromaBlueFlags); - - chromaRedFlags = DecodeChromaCodedBlockFlags( - ref reader, - in chromaRedGeometry, - transformDepth, - split, - parentChromaRedFlags); - } - - if (split) - { - for (int child = 0; child < 4; child++) - { - HevcTransformUnitGeometry childGeometry = geometry.CreateChild(child); - this.DecodeTransformTree( - ref reader, - in childGeometry, - transformDepth + 1, - minimumTransformLog2, - usesNxNPartitions, - transquantBypass, - regionId, - colorPlaneIndex, - chromaBlueFlags, - chromaRedFlags); - } - - return; - } - - this.deblockingState.MarkBlock( - geometry.PrimaryPlane, - primaryGeometry.X, - primaryGeometry.Y, - primaryGeometry.Width, - primaryGeometry.Height); - - HevcCodedBlockFlags primaryFlags = new(reader.ReadTransformCodedBlockFlag(false, transformDepth == 0 ? 1 : 0)); - bool hasCodedResidual = primaryFlags.Any || chromaBlueFlags.Any || chromaRedFlags.Any; - if (hasCodedResidual && this.quantizationParameterDeltaPending) - { - this.ApplyQuantizationParameterDelta(reader.ReadDeltaQuantizationParameter()); - this.quantizationParameterDeltaPending = false; - } - - if ((chromaBlueFlags.Any || chromaRedFlags.Any) - && this.chromaQuantizationAdjustmentPending - && !transquantBypass) - { - this.currentChromaQuantizationAdjustment = reader.ReadChromaQuantizationAdjustment( - this.pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count); - - this.chromaQuantizationAdjustmentPending = false; - } - - HevcQuantizationParameters quantizationParameters = this.CreateQuantizationParameters(); - Span lumaResidual = this.integerScratch.Memory.Span.Slice(MaximumTransformSampleCount * 3, MaximumTransformSampleCount); - lumaResidual.Clear(); - this.DecodeComponentSections( - ref reader, - geometry.PrimaryPlane, - in primaryGeometry, - primaryFlags, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - true, - 0, - in primaryGeometry); - - if (!geometry.HasCombinedChroma) - { - return; - } - - int chromaMode = this.intraPredictionStates[colorPlaneIndex].GetChromaMode(geometry.Primary.X, geometry.Primary.Y); - int chromaBlueAlpha = 0; - bool canPredictAcrossComponents = this.pictureParameterSet.CrossComponentPredictionEnabled - && primaryFlags.Any - && chromaMode == 36 - && chromaBlueGeometry.Process - && chromaBlueGeometry.Width == chromaBlueGeometry.Height; - - if (canPredictAcrossComponents) - { - chromaBlueAlpha = reader.ReadCrossComponentPredictionScale(0); - } - - this.DecodeComponentSections( - ref reader, - HevcPlane.Cb, - in chromaBlueGeometry, - chromaBlueFlags, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - false, - chromaBlueAlpha, - in primaryGeometry); - - int chromaRedAlpha = 0; - if (canPredictAcrossComponents) - { - // The Cr scale follows the complete Cb residual syntax. Reading both scales together changes every - // subsequent CABAC decision whenever Cb carries coefficients. - chromaRedAlpha = reader.ReadCrossComponentPredictionScale(1); - } - - this.DecodeComponentSections( - ref reader, - HevcPlane.Cr, - in chromaRedGeometry, - chromaRedFlags, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - false, - chromaRedAlpha, - in primaryGeometry); - } - - /// - /// Decodes chroma coded-block flags at the highest transform level that owns the component rectangle. - /// - /// The active entropy-substream reader. - /// The current chroma component rectangle. - /// The luma transform depth. - /// Whether the current luma transform node subdivides. - /// The coded-block flags inherited from the parent transform node. - /// The flags governing the current component rectangle. - private static HevcCodedBlockFlags DecodeChromaCodedBlockFlags( - ref HevcCabacSyntaxReader reader, - in HevcTransformComponentGeometry geometry, - int transformDepth, - bool lumaSplit, - HevcCodedBlockFlags parentFlags) - { - if (!geometry.Process) - { - return parentFlags; - } - - bool shouldDecode = transformDepth == 0 || (geometry.ProcessesAllQuadrants && parentFlags.Any); - if (!shouldDecode) - { - return parentFlags; - } - - int context = transformDepth; - bool canQuadSplit = geometry.Width >= 8 && geometry.Height >= 8; - if (geometry.Width != geometry.Height && (!lumaSplit || !canQuadSplit)) - { - bool first = reader.ReadTransformCodedBlockFlag(true, context); - bool second = reader.ReadTransformCodedBlockFlag(true, context); - return new HevcCodedBlockFlags(first, second); - } - - return new HevcCodedBlockFlags(reader.ReadTransformCodedBlockFlag(true, context)); - } - - /// - /// Decodes one square component block or the two square sub-blocks of a rectangular 4:2:2 transform section. - /// - /// The active entropy-substream reader. - /// The reconstructed component plane. - /// The component rectangle. - /// The component coded-block flags. - /// Whether the coding unit bypasses inverse quantization and transform. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - /// The effective component quantization parameters. - /// The current luma residual retained for cross-component prediction. - /// Whether reconstructed residuals are copied to . - /// The signed inverse cross-component prediction scale. - /// The luma transform rectangle governing cross-component residual addressing. - private void DecodeComponentSections( - ref HevcCabacSyntaxReader reader, - HevcPlane plane, - in HevcTransformComponentGeometry geometry, - HevcCodedBlockFlags codedBlockFlags, - bool transquantBypass, - int regionId, - int colorPlaneIndex, - in HevcQuantizationParameters quantizationParameters, - Span lumaResidual, - bool retainResidual, - int crossComponentAlpha, - in HevcTransformComponentGeometry lumaGeometry) - { - if (!geometry.Process) - { - return; - } - - if (geometry.Width == geometry.Height) - { - this.DecodeComponentBlock( - ref reader, - plane, - geometry.X, - geometry.Y, - geometry.Width, - codedBlockFlags.First, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - retainResidual, - crossComponentAlpha, - this.GetLumaResidualOffset(plane, geometry.X, geometry.Y, in lumaGeometry), - lumaGeometry.Width); - - return; - } - - int size = Math.Min(geometry.Width, geometry.Height); - int secondX = geometry.Width > geometry.Height ? geometry.X + size : geometry.X; - int secondY = geometry.Height > geometry.Width ? geometry.Y + size : geometry.Y; - this.DecodeComponentBlock( - ref reader, - plane, - geometry.X, - geometry.Y, - size, - codedBlockFlags.First, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - retainResidual, - crossComponentAlpha, - this.GetLumaResidualOffset(plane, geometry.X, geometry.Y, in lumaGeometry), - lumaGeometry.Width); - - this.DecodeComponentBlock( - ref reader, - plane, - secondX, - secondY, - size, - codedBlockFlags.Second, - transquantBypass, - regionId, - colorPlaneIndex, - in quantizationParameters, - lumaResidual, - retainResidual, - crossComponentAlpha, - this.GetLumaResidualOffset(plane, secondX, secondY, in lumaGeometry), - lumaGeometry.Width); - } - - /// - /// Decodes, predicts, and reconstructs one square transform block. - /// - /// The active entropy-substream reader. - /// The reconstructed component plane. - /// The block left coordinate in component samples. - /// The block top coordinate in component samples. - /// The square transform-block side. - /// Whether coefficient syntax is present. - /// Whether the coding unit bypasses inverse quantization and transform. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - /// The effective component quantization parameters. - /// The current luma residual retained for cross-component prediction. - /// Whether reconstructed residuals are copied to . - /// The signed inverse cross-component prediction scale. - /// The first colocated sample in the retained luma residual. - /// The retained luma residual row stride. - private void DecodeComponentBlock( - ref HevcCabacSyntaxReader reader, - HevcPlane plane, - int x, - int y, - int size, - bool codedBlockFlag, - bool transquantBypass, - int regionId, - int colorPlaneIndex, - in HevcQuantizationParameters quantizationParameters, - Span lumaResidual, - bool retainResidual, - int crossComponentAlpha, - int lumaResidualOffset, - int lumaResidualStride) - { - int log2Size = BitOperations.Log2((uint)size); - int sampleCount = size * size; - Span integerScratch = this.integerScratch.Memory.Span; - Span quantized = integerScratch[..MaximumTransformSampleCount]; - Span dequantized = integerScratch.Slice(MaximumTransformSampleCount, MaximumTransformSampleCount); - Span residual = integerScratch.Slice(MaximumTransformSampleCount * 2, MaximumTransformSampleCount); - Span transformScratch = integerScratch.Slice(MaximumTransformSampleCount * 4, MaximumTransformSampleCount * 2); - Span prediction = this.PredictComponentBlock(plane, x, y, log2Size, regionId, colorPlaneIndex, transquantBypass); - residual[..sampleCount].Clear(); - bool useLumaSyntax = this.sequenceParameterSet.SeparateColorPlaneFlag; - HevcPlane codingPlane = useLumaSyntax ? HevcPlane.Y : plane; - int lumaX = x << this.Picture.GetSubsamplingX(plane); - int lumaY = y << this.Picture.GetSubsamplingY(plane); - int codingPredictionMode = plane == HevcPlane.Y || useLumaSyntax - ? this.intraPredictionStates[colorPlaneIndex].GetLumaMode(lumaX, lumaY) - : this.intraPredictionStates[colorPlaneIndex].GetEffectiveChromaMode(lumaX, lumaY); - - int predictionMode = codingPredictionMode; - if (plane != HevcPlane.Y && !useLumaSyntax && this.sequenceParameterSet.ChromaFormat == 2) - { - predictionMode = HevcIntraPredictionMode.RemapChroma422(predictionMode); - } - - bool transformSkip = codedBlockFlag - && !transquantBypass - && this.pictureParameterSet.TransformSkipEnabled - && log2Size <= this.pictureParameterSet.MaxTransformSkipBlockLog2 - && reader.ReadTransformSkip(codingPlane != HevcPlane.Y); - - HevcResidualDpcmMode residualDpcmMode = this.sequenceParameterSet.ImplicitResidualDpcmEnabled && (transformSkip || transquantBypass) - ? HevcResidualReconstructor.GetImplicitResidualDpcmMode(predictionMode, false) - : HevcResidualDpcmMode.None; - - if (codedBlockFlag) - { - HevcCoefficientCodingParameters codingParameters = HevcCoefficientCodingParameters.Create( - this.pictureParameterSet, - size, - size, - plane, - true, - codingPredictionMode, - transformSkip, - transquantBypass, - residualDpcmMode, - useLumaSyntax); - - this.coefficientDecoder.Decode(ref reader, quantized, in codingParameters); - - bool rotate = HevcResidualReconstructor.IsNonTransformedResidualRotated( - this.sequenceParameterSet.TransformSkipRotationEnabled, - true, - size); - - if (transquantBypass) - { - HevcResidualReconstructor.CopyBypassed(quantized[..sampleCount], residual, rotate); - } - else - { - int bitDepth = this.Picture.GetBitDepth(plane); - int maxTransformDynamicRange = this.sequenceParameterSet.GetMaxTransformDynamicRange(codingPlane); - int quantizationParameter = useLumaSyntax - ? quantizationParameters.Luma - : quantizationParameters.Get(plane); - - HevcInverseQuantizer.Dequantize( - quantized, - dequantized, - log2Size, - bitDepth, - maxTransformDynamicRange, - quantizationParameter, - this.sequenceParameterSet.ScalingListEnabled, - this.pictureParameterSet.ScalingList, - codingPlane, - true, - transformSkip, - this.sequenceParameterSet.ExtendedPrecisionProcessingEnabled); - - if (transformSkip) - { - HevcResidualReconstructor.ApplyTransformSkip( - dequantized, - residual, - size, - size, - bitDepth, - maxTransformDynamicRange, - log2Size, - this.sequenceParameterSet.ExtendedPrecisionProcessingEnabled, - rotate); - } - else - { - HevcInverseTransformer.Transform( - dequantized, - residual, - log2Size, - log2Size, - bitDepth, - maxTransformDynamicRange, - codingPlane == HevcPlane.Y && log2Size == 2, - transformScratch); - } - } - - HevcResidualReconstructor.ApplyResidualDpcm(residual, size, size, residualDpcmMode); - } - - if (crossComponentAlpha != 0) - { - for (int row = 0; row < size; row++) - { - HevcResidualReconstructor.ApplyCrossComponentPrediction( - lumaResidual.Slice(lumaResidualOffset + (row * lumaResidualStride), size), - residual.Slice(row * size, size), - size, - crossComponentAlpha, - this.sequenceParameterSet.BitDepthLuma - this.sequenceParameterSet.BitDepthChroma); - } - } - - if (retainResidual) - { - for (int row = 0; row < size; row++) - { - residual.Slice(row * size, size).CopyTo(lumaResidual.Slice(lumaResidualOffset + (row * lumaResidualStride), size)); - } - } - - HevcInverseTransformer.AddResidual( - residual, - prediction, - size, - size, - size, - this.Picture.GetBitDepth(plane)); - - this.CopyPredictionToPicture(prediction, plane, x, y, size); - this.reconstructionState.MarkReconstructed(plane, x, y, size, size, regionId); - } - - /// - /// Gets the packed luma-residual offset colocated with one component block. - /// - /// The component plane. - /// The component block left coordinate. - /// The component block top coordinate. - /// The governing luma transform rectangle. - /// The zero-based packed luma-residual offset. - private int GetLumaResidualOffset(HevcPlane plane, int x, int y, in HevcTransformComponentGeometry lumaGeometry) - { - int lumaX = x << this.Picture.GetSubsamplingX(plane); - int lumaY = y << this.Picture.GetSubsamplingY(plane); - return ((lumaY - lumaGeometry.Y) * lumaGeometry.Width) + lumaX - lumaGeometry.X; - } - - /// - /// Applies the signed coding-unit luma quantization delta with bit-depth-dependent modular wrapping. - /// - /// The decoded signed delta. - private void ApplyQuantizationParameterDelta(int delta) - { - int bitDepthOffset = 6 * (this.sequenceParameterSet.BitDepthLuma - 8); - int modulus = 52 + bitDepthOffset; - int value = this.currentQuantizationParameter + delta + bitDepthOffset; - value %= modulus; - if (value < 0) - { - value += modulus; - } - - this.currentQuantizationParameter = value - bitDepthOffset; - } - - /// - /// Creates the component quantization parameters selected by picture, slice, and coding-unit offsets. - /// - /// The effective luma, Cb, and Cr quantization parameters. - private HevcQuantizationParameters CreateQuantizationParameters() - { - int cbOffset = this.pictureParameterSet.ChromaCbQuantizationParameterOffset + this.currentSliceChromaBlueQuantizationOffset; - int crOffset = this.pictureParameterSet.ChromaCrQuantizationParameterOffset + this.currentSliceChromaRedQuantizationOffset; - if (this.currentChromaQuantizationAdjustment > 0) - { - int adjustmentIndex = this.currentChromaQuantizationAdjustment - 1; - cbOffset += this.pictureParameterSet.ChromaQuantizationParameterOffsetsCb[adjustmentIndex]; - crOffset += this.pictureParameterSet.ChromaQuantizationParameterOffsetsCr[adjustmentIndex]; - } - - return new HevcQuantizationParameters( - this.currentQuantizationParameter, - this.sequenceParameterSet.BitDepthLuma, - this.sequenceParameterSet.BitDepthChroma, - this.sequenceParameterSet.ChromaFormat, - cbOffset, - crOffset); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs deleted file mode 100644 index 7e2bd4bef..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Implements slice, coding-tree, and coding-unit traversal. -/// -internal sealed partial class HevcPictureDecoder -{ - /// - /// Decodes one ordered slice segment and returns the next tile-scan coding-tree-block address. - /// - /// The current independent or dependent slice segment. - /// The independent header governing inherited slice fields. - /// The one-based independent-slice index within the selected color plane. - /// The picture tile mapping. - /// The first coding-tree block in tile-scan order. - /// The governing independent slice's first coding-tree block in tile-scan order. - /// The tile-scan address immediately following the decoded segment. - private int DecodeSliceSegment( - HevcSliceSegmentHeader slice, - HevcSliceSegmentHeader independentSlice, - int independentSliceIndex, - in HevcTileLayout tileLayout, - int startAddressInTileScan, - int independentSliceStartAddressInTileScan) - { - int sliceQuantizationParameter = independentSlice.QuantizationParameter!.Value; - int colorPlaneIndex = this.sequenceParameterSet.SeparateColorPlaneFlag ? independentSlice.ColorPlaneId : 0; - this.lastCodedQuantizationParameter = sliceQuantizationParameter; - this.currentQuantizationParameter = sliceQuantizationParameter; - this.currentChromaQuantizationAdjustment = 0; - this.currentSliceChromaBlueQuantizationOffset = independentSlice.ChromaCbQuantizationParameterOffset; - this.currentSliceChromaRedQuantizationOffset = independentSlice.ChromaCrQuantizationParameterOffset; - this.quantizationParameterDeltaPending = this.pictureParameterSet.CodingUnitQuantizationParameterDeltaEnabled; - this.chromaQuantizationAdjustmentPending = independentSlice.ChromaQuantizationParameterOffsetListEnabled == true; - int substreamIndex = 0; - HevcCabacSyntaxReader reader = new(slice.GetEntropySubstream(substreamIndex).Span, sliceQuantizationParameter); - this.coefficientDecoder.ResetRiceAdaptation(); - - int contextOffset = colorPlaneIndex * HevcCabacContexts.ContextCount; - int riceOffset = colorPlaneIndex * 4; - int startRasterAddress = tileLayout.GetRasterAddress(startAddressInTileScan); - tileLayout.GetTilePosition( - startRasterAddress, - out int startTileIndex, - out int startColumnInTile, - out int startRowInTile, - out int startTileWidth, - out _); - - bool startsAtTileOrigin = startColumnInTile == 0 && startRowInTile == 0; - bool canInheritSliceSegmentContexts = !startsAtTileOrigin - && (startTileWidth >= 2 || !this.pictureParameterSet.EntropyCodingSynchronizationEnabled); - - // A dependent segment normally resumes the preceding CABAC state. Tile origins and one-CTB-wide WPP - // rows are initialization boundaries instead, matching the availability rules used by the reference decoder. - if (slice.DependentSliceSegment - && canInheritSliceSegmentContexts - && this.hasSliceSegmentContexts[colorPlaneIndex]) - { - reader.CopyContextsFrom(this.sliceSegmentContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); - this.coefficientDecoder.CopyRiceAdaptationFrom(this.sliceSegmentRiceAdaptation.AsSpan(riceOffset, 4)); - } - - if (!slice.DependentSliceSegment) - { - // An independent slice starts a new prediction region, so an upper-right CTB from the preceding - // independent slice cannot supply wavefront contexts to its first row. - this.hasWavefrontContexts[colorPlaneIndex] = false; - } - - bool startsAtWavefrontRow = this.pictureParameterSet.EntropyCodingSynchronizationEnabled - && startColumnInTile == 0 - && startRowInTile > 0; - - if (startsAtWavefrontRow - && startTileWidth > 1 - && this.hasWavefrontContexts[colorPlaneIndex] - && this.wavefrontContextTileIndices[colorPlaneIndex] == startTileIndex) - { - // A dependent segment can begin exactly at a wavefront row boundary. Its first substream still uses - // the upper-right state captured from the preceding row; no substream transition occurs inside this call. - reader.CopyContextsFrom(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); - this.coefficientDecoder.CopyRiceAdaptationFrom(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); - } - - int codingTreeBlockSize = 1 << this.sequenceParameterSet.CodingTreeBlockLog2; - int tileScanAddress = startAddressInTileScan; - bool firstCodingTreeBlock = true; - while (tileScanAddress < tileLayout.Width * tileLayout.Height) - { - int rasterAddress = tileLayout.GetRasterAddress(tileScanAddress); - tileLayout.GetTilePosition( - rasterAddress, - out int tileIndex, - out int columnInTile, - out int rowInTile, - out int tileWidth, - out int tileHeight); - - bool startsTile = columnInTile == 0 && rowInTile == 0; - bool startsWavefrontRow = this.pictureParameterSet.EntropyCodingSynchronizationEnabled && columnInTile == 0 && rowInTile > 0; - if (!firstCodingTreeBlock && (startsTile || startsWavefrontRow)) - { - if (!reader.ReadTerminate()) - { - throw new InvalidImageContentException("The HEVC entropy substream does not terminate at its tile or wavefront boundary."); - } - - reader.ValidateTerminationAlignment(); - substreamIndex++; - if (substreamIndex >= slice.EntropySubstreamCount) - { - throw new InvalidImageContentException("The HEVC slice segment has too few entropy entry points."); - } - - reader = new HevcCabacSyntaxReader(slice.GetEntropySubstream(substreamIndex).Span, sliceQuantizationParameter); - this.coefficientDecoder.ResetRiceAdaptation(); - this.lastCodedQuantizationParameter = sliceQuantizationParameter; - if (startsWavefrontRow - && tileWidth > 1 - && this.hasWavefrontContexts[colorPlaneIndex] - && this.wavefrontContextTileIndices[colorPlaneIndex] == tileIndex) - { - reader.CopyContextsFrom(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); - this.coefficientDecoder.CopyRiceAdaptationFrom(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); - } - } - - int ctbX = rasterAddress % tileLayout.Width; - int ctbY = rasterAddress / tileLayout.Width; - int x = ctbX * codingTreeBlockSize; - int y = ctbY * codingTreeBlockSize; - int regionId = ((independentSliceIndex - 1) * tileLayout.TileCount) + tileIndex + 1; - HevcPlane regionPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? (HevcPlane)colorPlaneIndex : HevcPlane.Y; - HevcLoopFilterRegion loopFilterRegion = new( - independentSliceStartAddressInTileScan, - tileIndex, - independentSlice.LoopFilterAcrossSlicesEnabled == true, - independentSlice.DeblockingFilterDisabled == true, - independentSlice.DeblockingFilterBetaOffsetDiv2, - independentSlice.DeblockingFilterTcOffsetDiv2); - - this.sampleAdaptiveOffsetState.SetLoopFilterRegion(rasterAddress, regionPlane, loopFilterRegion); - this.DecodeSampleAdaptiveOffset(ref reader, independentSlice, rasterAddress, ctbX, ctbY, regionId); - this.DecodeCodingTree( - ref reader, - x, - y, - this.sequenceParameterSet.CodingTreeBlockLog2, - 0, - regionId, - colorPlaneIndex); - - // HEVC places end_of_slice_segment_flag after the final coding unit of each complete CTB. Reading it - // inside the recursive leaf traversal consumes coefficient data whenever a CTB contains multiple CUs. - bool endOfSliceSegment = reader.ReadTerminate(); - - // Wavefront synchronization copies probability and persistent Rice state after the second CTB of each - // row. The next row starts with those contexts but a newly initialized arithmetic register. - if (this.pictureParameterSet.EntropyCodingSynchronizationEnabled && columnInTile == 1) - { - reader.CopyContextsTo(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); - this.coefficientDecoder.CopyRiceAdaptationTo(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); - this.hasWavefrontContexts[colorPlaneIndex] = true; - this.wavefrontContextTileIndices[colorPlaneIndex] = tileIndex; - } - - tileScanAddress++; - firstCodingTreeBlock = false; - if (endOfSliceSegment) - { - reader.ValidateTerminationAlignment(); - if (substreamIndex + 1 != slice.EntropySubstreamCount) - { - throw new InvalidImageContentException("The HEVC slice segment has unused entropy entry points."); - } - - reader.CopyContextsTo(this.sliceSegmentContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); - this.coefficientDecoder.CopyRiceAdaptationTo(this.sliceSegmentRiceAdaptation.AsSpan(riceOffset, 4)); - this.hasSliceSegmentContexts[colorPlaneIndex] = true; - return tileScanAddress; - } - - bool atTileEnd = columnInTile == tileWidth - 1 && rowInTile == tileHeight - 1; - bool atWavefrontRowEnd = this.pictureParameterSet.EntropyCodingSynchronizationEnabled && columnInTile == tileWidth - 1; - if (atTileEnd || atWavefrontRowEnd) - { - // A non-final tile or wavefront row has a second terminating bin after the coding-unit end flag. - // It is consumed when the following loop iteration opens the next bounded entropy substream. - continue; - } - } - - throw new InvalidImageContentException("The HEVC slice segment reaches the picture boundary without termination."); - } - - /// - /// Decodes one coding-tree node in depth-first Z order. - /// - /// The active entropy-substream reader. - /// The coding-node left luma coordinate. - /// The coding-node top luma coordinate. - /// The base-two logarithm of the coding-node side. - /// The coding-tree depth below the coding-tree-block root. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - private void DecodeCodingTree( - ref HevcCabacSyntaxReader reader, - int x, - int y, - int log2Size, - int depth, - int regionId, - int colorPlaneIndex) - { - int size = 1 << log2Size; - bool crossesPictureBoundary = x + size > this.sequenceParameterSet.Width || y + size > this.sequenceParameterSet.Height; - bool canSplit = log2Size > this.sequenceParameterSet.MinCodingBlockLog2; - HevcCodingTreeState codingTreeState = this.codingTreeStates[colorPlaneIndex]; - bool split = false; - if (canSplit) - { - if (crossesPictureBoundary) - { - split = true; - } - else - { - bool leftAvailable = this.reconstructionState.IsReconstructed((HevcPlane)colorPlaneIndex, x - 1, y, regionId); - bool aboveAvailable = this.reconstructionState.IsReconstructed((HevcPlane)colorPlaneIndex, x, y - 1, regionId); - int context = codingTreeState.GetSplitContext(x, y, depth, leftAvailable, aboveAvailable); - split = reader.ReadSplit(context); - } - } - - bool startsQuantizationGroup = depth == this.pictureParameterSet.QuantizationParameterDeltaDepth - || (!split && depth < this.pictureParameterSet.QuantizationParameterDeltaDepth); - if (startsQuantizationGroup && this.pictureParameterSet.CodingUnitQuantizationParameterDeltaEnabled) - { - // A leaf above the configured QG depth owns one complete quantization group. Waiting for the configured - // depth would carry the preceding group's coded-delta state into this coding unit and skip required syntax. - this.BeginQuantizationGroup(x, y, regionId, colorPlaneIndex); - } - - bool startsChromaQuantizationGroup = depth == this.pictureParameterSet.ChromaQuantizationParameterOffsetDepth - || (!split && depth < this.pictureParameterSet.ChromaQuantizationParameterOffsetDepth); - if (startsChromaQuantizationGroup && this.pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count != 0) - { - this.currentChromaQuantizationAdjustment = 0; - this.chromaQuantizationAdjustmentPending = true; - } - - if (split) - { - int childLog2Size = log2Size - 1; - int childSize = 1 << childLog2Size; - for (int child = 0; child < 4; child++) - { - int childX = x + ((child & 1) * childSize); - int childY = y + ((child >> 1) * childSize); - if (childX >= this.sequenceParameterSet.Width || childY >= this.sequenceParameterSet.Height) - { - continue; - } - - this.DecodeCodingTree( - ref reader, - childX, - childY, - childLog2Size, - depth + 1, - regionId, - colorPlaneIndex); - } - - return; - } - - this.DecodeCodingUnit(ref reader, x, y, log2Size, depth, regionId, colorPlaneIndex); - } - - /// - /// Decodes and reconstructs one intra-coded leaf coding unit. - /// - /// The active entropy-substream reader. - /// The coding-unit left luma coordinate. - /// The coding-unit top luma coordinate. - /// The base-two logarithm of the coding-unit side. - /// The coding-tree depth. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - private void DecodeCodingUnit( - ref HevcCabacSyntaxReader reader, - int x, - int y, - int log2Size, - int depth, - int regionId, - int colorPlaneIndex) - { - bool transquantBypass = this.pictureParameterSet.TransquantizationBypassEnabled && reader.ReadTransquantBypass(); - bool usesNxNPartitions = reader.ReadIntraNxNPartition(log2Size == this.sequenceParameterSet.MinCodingBlockLog2); - HevcPlane primaryPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? (HevcPlane)colorPlaneIndex : HevcPlane.Y; - bool pcm = this.sequenceParameterSet.PcmEnabled - && !usesNxNPartitions - && log2Size >= this.sequenceParameterSet.MinPcmCodingBlockLog2 - && log2Size <= this.sequenceParameterSet.MaxPcmCodingBlockLog2 - && reader.ReadPcmFlag(); - - if (pcm) - { - int size = 1 << log2Size; - this.deblockingState.MarkBlock(primaryPlane, x, y, size, size); - this.DecodePcmCodingUnit(ref reader, x, y, log2Size, regionId, colorPlaneIndex); - reader.RestartAfterPcm(); - } - else - { - HevcIntraPredictionState predictionState = this.intraPredictionStates[colorPlaneIndex]; - HevcPlane boundaryPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? (HevcPlane)colorPlaneIndex : HevcPlane.Y; - bool leftAvailable = this.reconstructionState.IsReconstructed(boundaryPlane, x - 1, y, regionId); - bool aboveAvailable = this.reconstructionState.IsReconstructed(boundaryPlane, x, y - 1, regionId); - predictionState.DecodeLumaModes(ref reader, x, y, log2Size, usesNxNPartitions, leftAvailable, aboveAvailable); - if (this.sequenceParameterSet.ChromaFormat != 0 && !this.sequenceParameterSet.SeparateColorPlaneFlag) - { - predictionState.DecodeChromaModes(ref reader, x, y, log2Size, usesNxNPartitions); - } - - int minimumTransformLog2 = GetMinimumTransformLog2Size(this.sequenceParameterSet, log2Size, usesNxNPartitions); - HevcTransformUnitGeometry geometry = HevcTransformUnitGeometry.CreateRoot( - x, - y, - log2Size, - this.sequenceParameterSet.ChromaFormat, - this.sequenceParameterSet.SeparateColorPlaneFlag, - colorPlaneIndex); - - this.DecodeTransformTree( - ref reader, - in geometry, - 0, - minimumTransformLog2, - usesNxNPartitions, - transquantBypass, - regionId, - colorPlaneIndex, - default, - default); - } - - this.codingTreeStates[colorPlaneIndex].SetCodingUnit( - x, - y, - log2Size, - depth, - this.currentQuantizationParameter, - transquantBypass, - pcm); - - this.lastCodedQuantizationParameter = this.currentQuantizationParameter; - } - - /// - /// Begins one luma quantization group using available spatial predictors. - /// - /// The quantization-group left luma coordinate. - /// The quantization-group top luma coordinate. - /// The current independent-slice and tile prediction region. - /// The selected separate-color plane, or zero for combined coding. - private void BeginQuantizationGroup(int x, int y, int regionId, int colorPlaneIndex) - { - HevcPlane plane = this.sequenceParameterSet.SeparateColorPlaneFlag ? (HevcPlane)colorPlaneIndex : HevcPlane.Y; - int codingTreeBlockMask = (1 << this.sequenceParameterSet.CodingTreeBlockLog2) - 1; - - // QP prediction neighbours are confined to the current CTB. This differs from intra sample availability, - // which may legitimately use reconstructed samples across the same left or upper CTB boundary. - bool leftAvailable = (x & codingTreeBlockMask) != 0 && this.reconstructionState.IsReconstructed(plane, x - 1, y, regionId); - bool aboveAvailable = (y & codingTreeBlockMask) != 0 && this.reconstructionState.IsReconstructed(plane, x, y - 1, regionId); - int fallback = this.lastCodedQuantizationParameter; - HevcCodingTreeState codingTreeState = this.codingTreeStates[colorPlaneIndex]; - int left = leftAvailable ? codingTreeState.GetQuantizationParameter(x - 1, y) : fallback; - int above = aboveAvailable ? codingTreeState.GetQuantizationParameter(x, y - 1) : fallback; - this.currentQuantizationParameter = (left + above + 1) >> 1; - this.quantizationParameterDeltaPending = true; - } - - /// - /// Derives the smallest luma transform permitted within one intra coding unit. - /// - /// The transform hierarchy limits. - /// The base-two logarithm of the coding-unit side. - /// Whether the coding unit has four luma prediction partitions. - /// The minimum luma transform side as a base-two logarithm. - private static int GetMinimumTransformLog2Size( - HevcSequenceParameterSet sequenceParameterSet, - int codingUnitLog2Size, - bool usesNxNPartitions) - { - int hierarchyReduction = sequenceParameterSet.MaxTransformHierarchyDepthIntra - 1 + (usesNxNPartitions ? 1 : 0); - int minimum = codingUnitLog2Size < sequenceParameterSet.MinTransformBlockLog2 + hierarchyReduction - ? sequenceParameterSet.MinTransformBlockLog2 - : codingUnitLog2Size - hierarchyReduction; - - return Math.Min(minimum, sequenceParameterSet.MaxTransformBlockLog2); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs deleted file mode 100644 index 8b4fd3859..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Owns the bounded state used to reconstruct one independently decodable HEVC still picture. -/// -internal sealed partial class HevcPictureDecoder : IDisposable -{ - /// - /// The maximum square transform-block sample count. - /// - private const int MaximumTransformSampleCount = 32 * 32; - - /// - /// The largest reference array used by a thirty-two-sample prediction block. - /// - private const int MaximumReferenceLength = (2 * 32) + 1; - - /// - /// The configuration providing picture-lifetime allocations. - /// - private readonly Configuration configuration; - - /// - /// The active picture parameters. - /// - private readonly HevcPictureParameterSet pictureParameterSet; - - /// - /// The active sequence parameters. - /// - private readonly HevcSequenceParameterSet sequenceParameterSet; - - /// - /// The decoded coding-unit state. - /// - private readonly HevcCodingTreeState[] codingTreeStates; - - /// - /// The decoded intra-prediction modes. - /// - private readonly HevcIntraPredictionState[] intraPredictionStates; - - /// - /// The completed prediction-block state used for reference availability. - /// - private readonly HevcReconstructionState reconstructionState; - - /// - /// The reusable coefficient entropy decoder. - /// - private readonly HevcCoefficientDecoder coefficientDecoder; - - /// - /// The resolved sample-adaptive-offset parameters for every coding-tree block. - /// - private readonly HevcSampleAdaptiveOffsetState sampleAdaptiveOffsetState; - - /// - /// The transform and prediction boundaries required by the deblocking stage. - /// - private readonly HevcDeblockingState deblockingState; - - /// - /// The integer coefficient, residual, and transform workspace. - /// - private readonly IMemoryOwner integerScratch; - - /// - /// The prediction, reference, and reference-substitution workspace. - /// - private readonly IMemoryOwner predictionScratch; - - /// - /// The ordered intra-reference availability workspace. - /// - private readonly IMemoryOwner availabilityScratch; - - /// - /// The per-color-plane adaptive contexts captured after the second coding-tree block of a wavefront row. - /// - private readonly HevcCabacContext[] wavefrontContexts = new HevcCabacContext[HevcCabacContexts.ContextCount * 3]; - - /// - /// The per-color-plane persistent Rice statistics captured with the wavefront probability contexts. - /// - private readonly int[] wavefrontRiceAdaptation = new int[12]; - - /// - /// Whether retained wavefront contexts are available for each color plane. - /// - private InlineArray4 hasWavefrontContexts; - - /// - /// The tile that owns each color plane's retained wavefront contexts. - /// - private InlineArray4 wavefrontContextTileIndices; - - /// - /// The adaptive contexts retained at the end of a dependent-slice prediction region. - /// - private readonly HevcCabacContext[] sliceSegmentContexts = new HevcCabacContext[HevcCabacContexts.ContextCount * 3]; - - /// - /// The persistent Rice statistics retained with dependent-slice probability contexts. - /// - private readonly int[] sliceSegmentRiceAdaptation = new int[12]; - - /// - /// Whether retained dependent-slice contexts are available. - /// - private InlineArray4 hasSliceSegmentContexts; - - /// - /// The luma quantization parameter most recently coded in the current prediction region. - /// - private int lastCodedQuantizationParameter; - - /// - /// The effective luma quantization parameter of the current quantization group. - /// - private int currentQuantizationParameter; - - /// - /// The one-based chroma quantization-offset-list selector of the current quantization group. - /// - private int currentChromaQuantizationAdjustment; - - /// - /// The Cb quantization-parameter offset signaled by the governing independent slice. - /// - private int currentSliceChromaBlueQuantizationOffset; - - /// - /// The Cr quantization-parameter offset signaled by the governing independent slice. - /// - private int currentSliceChromaRedQuantizationOffset; - - /// - /// Whether the current quantization group can still signal its luma delta. - /// - private bool quantizationParameterDeltaPending; - - /// - /// Whether the current quantization group can still signal its chroma adjustment. - /// - private bool chromaQuantizationAdjustmentPending; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing all decoder-owned memory. - /// The picture parameters governing the coded still image. - public HevcPictureDecoder(Configuration configuration, HevcPictureParameterSet pictureParameterSet) - { - this.configuration = configuration; - this.pictureParameterSet = pictureParameterSet; - this.sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - HevcPictureBuffer? picture = null; - HevcCodingTreeState[]? codingTreeStates = null; - HevcIntraPredictionState[]? intraPredictionStates = null; - HevcReconstructionState? reconstructionState = null; - HevcCoefficientDecoder? coefficientDecoder = null; - HevcSampleAdaptiveOffsetState? sampleAdaptiveOffsetState = null; - HevcDeblockingState? deblockingState = null; - IMemoryOwner? integerScratch = null; - IMemoryOwner? predictionScratch = null; - IMemoryOwner? availabilityScratch = null; - try - { - picture = new HevcPictureBuffer(configuration, this.sequenceParameterSet); - int codingTreeStateCount = this.sequenceParameterSet.SeparateColorPlaneFlag ? 3 : 1; - codingTreeStates = new HevcCodingTreeState[codingTreeStateCount]; - for (int index = 0; index < codingTreeStates.Length; index++) - { - codingTreeStates[index] = new HevcCodingTreeState(configuration, this.sequenceParameterSet); - } - - int intraPredictionStateCount = this.sequenceParameterSet.SeparateColorPlaneFlag ? 3 : 1; - intraPredictionStates = new HevcIntraPredictionState[intraPredictionStateCount]; - for (int index = 0; index < intraPredictionStates.Length; index++) - { - intraPredictionStates[index] = new HevcIntraPredictionState(configuration, this.sequenceParameterSet); - } - - reconstructionState = new HevcReconstructionState(configuration, this.sequenceParameterSet); - coefficientDecoder = new HevcCoefficientDecoder(configuration); - int codingTreeBlockCount = HevcParameterSetSyntax.GetCodingTreeBlockCount( - this.sequenceParameterSet.Width, - this.sequenceParameterSet.CodingTreeBlockLog2) - * HevcParameterSetSyntax.GetCodingTreeBlockCount( - this.sequenceParameterSet.Height, - this.sequenceParameterSet.CodingTreeBlockLog2); - - sampleAdaptiveOffsetState = new HevcSampleAdaptiveOffsetState(configuration, codingTreeBlockCount); - deblockingState = new HevcDeblockingState(configuration, this.sequenceParameterSet); - - // Six transform-sized integer regions retain quantized, dequantized, reconstructed, cross-component, and - // two-pass inverse-transform data without allocating in coding-unit or transform-unit loops. - integerScratch = configuration.MemoryAllocator.Allocate(MaximumTransformSampleCount * 6); - int maximumPredictionScratch = HevcIntraPredictor.GetScratchLength(5); - int maximumReferenceScratch = HevcIntraPredictor.GetReferenceScratchLength(5, 4); - predictionScratch = configuration.MemoryAllocator.Allocate( - MaximumTransformSampleCount + maximumPredictionScratch + maximumReferenceScratch + (MaximumReferenceLength * 4)); - - availabilityScratch = configuration.MemoryAllocator.Allocate((4 * 32 / 2) + 1); - - this.Picture = picture; - this.codingTreeStates = codingTreeStates; - this.intraPredictionStates = intraPredictionStates; - this.reconstructionState = reconstructionState; - this.coefficientDecoder = coefficientDecoder; - this.sampleAdaptiveOffsetState = sampleAdaptiveOffsetState; - this.deblockingState = deblockingState; - this.integerScratch = integerScratch; - this.predictionScratch = predictionScratch; - this.availabilityScratch = availabilityScratch; - } - catch - { - // No decoder ownership is published when construction fails. Unwind every completed child owner in reverse - // order because the caller cannot dispose an object whose constructor did not return. - availabilityScratch?.Dispose(); - predictionScratch?.Dispose(); - integerScratch?.Dispose(); - deblockingState?.Dispose(); - sampleAdaptiveOffsetState?.Dispose(); - coefficientDecoder?.Dispose(); - reconstructionState?.Dispose(); - if (intraPredictionStates is not null) - { - for (int index = intraPredictionStates.Length - 1; index >= 0; index--) - { - intraPredictionStates[index]?.Dispose(); - } - } - - if (codingTreeStates is not null) - { - for (int index = codingTreeStates.Length - 1; index >= 0; index--) - { - codingTreeStates[index]?.Dispose(); - } - } - - picture?.Dispose(); - throw; - } - } - - /// - /// Gets the native-precision reconstructed component planes. - /// - public HevcPictureBuffer Picture { get; } - - /// - /// Reconstructs every ordered slice segment in one independently decodable image item. - /// - /// The validated image-item NAL units and slice segments. - /// - /// A slice changes the coded picture parameters, overlaps an earlier segment, or does not terminate at a valid - /// coding-tree boundary. - /// - public void Decode(HevcImageItemBitstream bitstream) - { - HevcTileLayout tileLayout = new(this.pictureParameterSet); - int planeCount = this.sequenceParameterSet.SeparateColorPlaneFlag ? 3 : 1; - int[] nextCodingTreeBlockAddressesInTileScan = new int[planeCount]; - int[] independentSliceIndices = new int[planeCount]; - HevcSliceSegmentHeader?[] independentSlices = new HevcSliceSegmentHeader?[planeCount]; - for (int sliceIndex = 0; sliceIndex < bitstream.SliceSegments.Count; sliceIndex++) - { - HevcSliceSegmentHeader slice = bitstream.SliceSegments[sliceIndex]; - int colorPlane = this.sequenceParameterSet.SeparateColorPlaneFlag ? slice.ColorPlaneId : 0; - if (slice.PictureParameterSet.Id != this.pictureParameterSet.Id - || slice.PictureParameterSet.SequenceParameterSetId != this.pictureParameterSet.SequenceParameterSetId) - { - throw new InvalidImageContentException("The HEVC still picture changes parameter sets between slice segments."); - } - - if (!slice.DependentSliceSegment) - { - independentSlices[colorPlane] = slice; - independentSliceIndices[colorPlane]++; - } - - HevcSliceSegmentHeader? independentSlice = independentSlices[colorPlane]; - if (independentSlice is null) - { - throw new InvalidImageContentException("The HEVC still picture begins with a dependent slice segment."); - } - - int sliceStartAddressInTileScan = tileLayout.GetTileScanAddress(slice.SliceSegmentAddress); - if (sliceStartAddressInTileScan != nextCodingTreeBlockAddressesInTileScan[colorPlane]) - { - throw new InvalidImageContentException("The HEVC slice segments do not cover the coded picture in order."); - } - - nextCodingTreeBlockAddressesInTileScan[colorPlane] = this.DecodeSliceSegment( - slice, - independentSlice, - independentSliceIndices[colorPlane], - in tileLayout, - sliceStartAddressInTileScan, - tileLayout.GetTileScanAddress(independentSlice.SliceSegmentAddress)); - } - - int codingTreeBlockCount = HevcParameterSetSyntax.GetCodingTreeBlockCount( - this.sequenceParameterSet.Width, - this.sequenceParameterSet.CodingTreeBlockLog2) - * HevcParameterSetSyntax.GetCodingTreeBlockCount( - this.sequenceParameterSet.Height, - this.sequenceParameterSet.CodingTreeBlockLog2); - - foreach (int nextAddress in nextCodingTreeBlockAddressesInTileScan) - { - if (nextAddress != codingTreeBlockCount) - { - throw new InvalidImageContentException("The HEVC slice segments do not reconstruct the complete coded picture."); - } - } - - this.ApplyDeblockingFilter(in tileLayout); - if (this.sampleAdaptiveOffsetState.HasEnabledParameters) - { - // SAO classification always observes the complete post-deblocking picture, never samples already offset by an - // earlier CTB. One picture-lifetime snapshot provides that invariant without row allocations or filter-order coupling. - using HevcPictureBuffer sampleAdaptiveOffsetSource = new(this.configuration, this.sequenceParameterSet); - this.Picture.CopyTo(sampleAdaptiveOffsetSource); - this.ApplySampleAdaptiveOffset(sampleAdaptiveOffsetSource, in tileLayout); - } - } - - /// - /// Releases all current-picture state and reconstructed planes. - /// - public void Dispose() - { - this.availabilityScratch.Dispose(); - this.predictionScratch.Dispose(); - this.integerScratch.Dispose(); - this.deblockingState.Dispose(); - this.sampleAdaptiveOffsetState.Dispose(); - this.coefficientDecoder.Dispose(); - this.reconstructionState.Dispose(); - foreach (HevcIntraPredictionState state in this.intraPredictionStates) - { - state.Dispose(); - } - - foreach (HevcCodingTreeState state in this.codingTreeStates) - { - state.Dispose(); - } - - this.Picture.Dispose(); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureParameterSet.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureParameterSet.cs deleted file mode 100644 index 7b0f34f3b..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureParameterSet.cs +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the HEVC picture fields required to decode the independently coded picture in one still-image item. -/// -internal sealed class HevcPictureParameterSet -{ - /// - /// Initializes a new instance of the class. - /// - /// The decoded picture-parameter-set NAL unit. - /// The sequence parameter sets available to the coded image item. - /// - /// The picture parameter set is malformed, references an unavailable sequence parameter set, or declares - /// picture geometry outside that sequence parameter set. - /// - public HevcPictureParameterSet( - HevcNalUnit nalUnit, - IReadOnlyList sequenceParameterSets) - { - const byte pictureParameterSetNalUnitType = 34; - if (nalUnit.Header.NalUnitType != pictureParameterSetNalUnitType - || nalUnit.Header.LayerId != 0 - || nalUnit.Header.TemporalId != 0) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid NAL-unit header."); - } - - HevcBitReader reader = new(nalUnit.Rbsp.Span); - uint pictureParameterSetId = reader.ReadUnsignedExpGolomb(); - uint sequenceParameterSetId = reader.ReadUnsignedExpGolomb(); - if (pictureParameterSetId > 63 || sequenceParameterSetId > 15) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid identifier."); - } - - this.Id = (byte)pictureParameterSetId; - this.SequenceParameterSetId = (byte)sequenceParameterSetId; - - HevcSequenceParameterSet? sequenceParameterSet = null; - foreach (HevcSequenceParameterSet candidate in sequenceParameterSets) - { - if (candidate.Id == this.SequenceParameterSetId) - { - sequenceParameterSet = candidate; - break; - } - } - - if (sequenceParameterSet is null) - { - throw new InvalidImageContentException("The HEVC picture parameter set references an unavailable sequence parameter set."); - } - - this.SequenceParameterSet = sequenceParameterSet; - this.DependentSliceSegmentsEnabled = reader.ReadFlag(); - this.OutputFlagPresent = reader.ReadFlag(); - this.ExtraSliceHeaderBitCount = (int)reader.ReadBits(3); - this.SignDataHidingEnabled = reader.ReadFlag(); - this.CabacInitializationPresent = reader.ReadFlag(); - - uint defaultReferenceIndexCountList0MinusOne = reader.ReadUnsignedExpGolomb(); - uint defaultReferenceIndexCountList1MinusOne = reader.ReadUnsignedExpGolomb(); - if (defaultReferenceIndexCountList0MinusOne > 14 || defaultReferenceIndexCountList1MinusOne > 14) - { - throw new InvalidImageContentException("The HEVC picture parameter set declares too many default reference indices."); - } - - this.DefaultReferenceIndexCountList0 = (int)defaultReferenceIndexCountList0MinusOne + 1; - this.DefaultReferenceIndexCountList1 = (int)defaultReferenceIndexCountList1MinusOne + 1; - - this.InitialQuantizationParameterMinus26 = reader.ReadSignedExpGolomb(); - int minimumInitialQuantizationParameter = -26 - (6 * (sequenceParameterSet.BitDepthLuma - 8)); - if (this.InitialQuantizationParameterMinus26 < minimumInitialQuantizationParameter - || this.InitialQuantizationParameterMinus26 > 25) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid initial quantization parameter."); - } - - this.ConstrainedIntraPredictionEnabled = reader.ReadFlag(); - this.TransformSkipEnabled = reader.ReadFlag(); - this.CodingUnitQuantizationParameterDeltaEnabled = reader.ReadFlag(); - if (this.CodingUnitQuantizationParameterDeltaEnabled) - { - uint quantizationParameterDeltaDepth = reader.ReadUnsignedExpGolomb(); - int maximumDepth = sequenceParameterSet.CodingTreeBlockLog2 - sequenceParameterSet.MinCodingBlockLog2; - if (quantizationParameterDeltaDepth > maximumDepth) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid quantization-parameter delta depth."); - } - - this.QuantizationParameterDeltaDepth = (int)quantizationParameterDeltaDepth; - } - - this.ChromaCbQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - this.ChromaCrQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - this.SliceChromaQuantizationParameterOffsetsPresent = reader.ReadFlag(); - this.WeightedPredictionEnabled = reader.ReadFlag(); - this.WeightedBiPredictionEnabled = reader.ReadFlag(); - this.TransquantizationBypassEnabled = reader.ReadFlag(); - this.TilesEnabled = reader.ReadFlag(); - this.EntropyCodingSynchronizationEnabled = reader.ReadFlag(); - - int codingTreeBlockColumns = HevcParameterSetSyntax.GetCodingTreeBlockCount( - sequenceParameterSet.Width, - sequenceParameterSet.CodingTreeBlockLog2); - - int codingTreeBlockRows = HevcParameterSetSyntax.GetCodingTreeBlockCount( - sequenceParameterSet.Height, - sequenceParameterSet.CodingTreeBlockLog2); - - if (this.TilesEnabled) - { - uint tileColumnCountMinusOne = reader.ReadUnsignedExpGolomb(); - uint tileRowCountMinusOne = reader.ReadUnsignedExpGolomb(); - if (tileColumnCountMinusOne >= codingTreeBlockColumns - || tileRowCountMinusOne >= codingTreeBlockRows - || (tileColumnCountMinusOne == 0 && tileRowCountMinusOne == 0)) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid tile grid."); - } - - int tileColumnCount = (int)tileColumnCountMinusOne + 1; - int tileRowCount = (int)tileRowCountMinusOne + 1; - this.UniformTileSpacing = reader.ReadFlag(); - this.TileColumnWidths = ReadTileDimensions( - ref reader, - codingTreeBlockColumns, - tileColumnCount, - this.UniformTileSpacing); - - this.TileRowHeights = ReadTileDimensions( - ref reader, - codingTreeBlockRows, - tileRowCount, - this.UniformTileSpacing); - - this.LoopFilterAcrossTilesEnabled = reader.ReadFlag(); - } - else - { - // A picture without tile syntax is one tile spanning the coded CTB grid. Materializing that inferred - // layout lets slice addressing use the same bounded arrays for tiled and untiled image items. - this.UniformTileSpacing = true; - this.TileColumnWidths = [codingTreeBlockColumns]; - this.TileRowHeights = [codingTreeBlockRows]; - this.LoopFilterAcrossTilesEnabled = true; - } - - this.LoopFilterAcrossSlicesEnabled = reader.ReadFlag(); - this.DeblockingFilterControlPresent = reader.ReadFlag(); - if (this.DeblockingFilterControlPresent) - { - this.DeblockingFilterOverrideEnabled = reader.ReadFlag(); - this.DeblockingFilterDisabled = reader.ReadFlag(); - if (!this.DeblockingFilterDisabled) - { - this.DeblockingFilterBetaOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); - this.DeblockingFilterTcOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); - } - } - - this.ScalingListDataPresent = reader.ReadFlag(); - if (this.ScalingListDataPresent && !sequenceParameterSet.ScalingListEnabled) - { - throw new InvalidImageContentException("The HEVC picture parameter set declares scaling data disabled by its sequence parameter set."); - } - - this.ScalingList = this.ScalingListDataPresent - ? HevcScalingList.Parse(ref reader) - : sequenceParameterSet.ScalingList; - - this.ReferenceListModificationPresent = reader.ReadFlag(); - uint parallelMergeLevelMinusTwo = reader.ReadUnsignedExpGolomb(); - if (parallelMergeLevelMinusTwo > sequenceParameterSet.CodingTreeBlockLog2 - 2) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid parallel merge level."); - } - - this.ParallelMergeLevelLog2 = (int)parallelMergeLevelMinusTwo + 2; - this.SliceSegmentHeaderExtensionPresent = reader.ReadFlag(); - - this.MaxTransformSkipBlockLog2 = 2; - if (reader.ReadFlag()) - { - Span extensionFlags = stackalloc bool[8]; - for (int extensionFlag = 0; extensionFlag < extensionFlags.Length; extensionFlag++) - { - extensionFlags[extensionFlag] = reader.ReadFlag(); - } - - if (extensionFlags[1]) - { - throw new InvalidImageContentException("Layered HEVC picture extensions are not supported for still-image items."); - } - - if (extensionFlags[0]) - { - this.ReadRangeExtension(ref reader); - } - - bool unknownExtensionPresent = false; - for (int extensionFlag = 2; extensionFlag < extensionFlags.Length; extensionFlag++) - { - unknownExtensionPresent |= extensionFlags[extensionFlag]; - } - - if (unknownExtensionPresent) - { - while (reader.HasMoreRbspData()) - { - reader.ReadFlag(); - } - } - } - - reader.ReadRbspTrailingBits(); - } - - /// - /// Gets the picture-parameter-set identifier. - /// - public byte Id { get; } - - /// - /// Gets the referenced sequence-parameter-set identifier. - /// - public byte SequenceParameterSetId { get; } - - /// - /// Gets the sequence parameters governing this picture parameter set. - /// - public HevcSequenceParameterSet SequenceParameterSet { get; } - - /// - /// Gets a value indicating whether dependent slice segments can occur. - /// - public bool DependentSliceSegmentsEnabled { get; } - - /// - /// Gets a value indicating whether slice headers contain the picture-output flag. - /// - public bool OutputFlagPresent { get; } - - /// - /// Gets the number of reserved extra bits at the start of each independent slice header. - /// - public int ExtraSliceHeaderBitCount { get; } - - /// - /// Gets a value indicating whether transform-coefficient sign hiding is enabled. - /// - public bool SignDataHidingEnabled { get; } - - /// - /// Gets a value indicating whether slices can select an alternate CABAC initialization table. - /// - public bool CabacInitializationPresent { get; } - - /// - /// Gets the default active reference-index count for reference list zero. - /// - public int DefaultReferenceIndexCountList0 { get; } - - /// - /// Gets the default active reference-index count for reference list one. - /// - public int DefaultReferenceIndexCountList1 { get; } - - /// - /// Gets the picture quantization-parameter initializer relative to 26. - /// - public int InitialQuantizationParameterMinus26 { get; } - - /// - /// Gets a value indicating whether inter-coded neighbors are excluded from intra prediction. - /// - public bool ConstrainedIntraPredictionEnabled { get; } - - /// - /// Gets a value indicating whether residual transform skipping can be selected. - /// - public bool TransformSkipEnabled { get; } - - /// - /// Gets a value indicating whether coding units can change the quantization parameter. - /// - public bool CodingUnitQuantizationParameterDeltaEnabled { get; } - - /// - /// Gets the coding-tree depth at which quantization-parameter deltas are signaled. - /// - public int QuantizationParameterDeltaDepth { get; } - - /// - /// Gets the picture-level Cb quantization-parameter offset. - /// - public int ChromaCbQuantizationParameterOffset { get; } - - /// - /// Gets the picture-level Cr quantization-parameter offset. - /// - public int ChromaCrQuantizationParameterOffset { get; } - - /// - /// Gets a value indicating whether slices can add Cb and Cr quantization-parameter offsets. - /// - public bool SliceChromaQuantizationParameterOffsetsPresent { get; } - - /// - /// Gets a value indicating whether weighted prediction can be used by predictive slices. - /// - public bool WeightedPredictionEnabled { get; } - - /// - /// Gets a value indicating whether weighted prediction can be used by bidirectional slices. - /// - public bool WeightedBiPredictionEnabled { get; } - - /// - /// Gets a value indicating whether coding units can bypass transform and quantization. - /// - public bool TransquantizationBypassEnabled { get; } - - /// - /// Gets a value indicating whether the coded picture is partitioned into tiles. - /// - public bool TilesEnabled { get; } - - /// - /// Gets a value indicating whether wavefront entropy-coding synchronization is enabled. - /// - public bool EntropyCodingSynchronizationEnabled { get; } - - /// - /// Gets a value indicating whether the tile grid uses uniform proportional spacing. - /// - public bool UniformTileSpacing { get; } - - /// - /// Gets the tile-column widths in coding-tree blocks. - /// - public IReadOnlyList TileColumnWidths { get; } - - /// - /// Gets the tile-row heights in coding-tree blocks. - /// - public IReadOnlyList TileRowHeights { get; } - - /// - /// Gets a value indicating whether in-loop filtering crosses tile boundaries. - /// - public bool LoopFilterAcrossTilesEnabled { get; } - - /// - /// Gets a value indicating whether in-loop filtering crosses slice boundaries. - /// - public bool LoopFilterAcrossSlicesEnabled { get; } - - /// - /// Gets a value indicating whether picture or slice syntax controls deblocking. - /// - public bool DeblockingFilterControlPresent { get; } - - /// - /// Gets a value indicating whether slice headers can override picture-level deblocking. - /// - public bool DeblockingFilterOverrideEnabled { get; } - - /// - /// Gets a value indicating whether deblocking is disabled by default for the picture. - /// - public bool DeblockingFilterDisabled { get; } - - /// - /// Gets half the picture-level deblocking beta-threshold offset. - /// - public int DeblockingFilterBetaOffsetDiv2 { get; } - - /// - /// Gets half the picture-level deblocking clipping-threshold offset. - /// - public int DeblockingFilterTcOffsetDiv2 { get; } - - /// - /// Gets a value indicating whether this picture parameter set supplies scaling-list data. - /// - public bool ScalingListDataPresent { get; } - - /// - /// Gets the effective quantization scaling matrices for slices using this picture parameter set. - /// - public HevcScalingList ScalingList { get; } - - /// - /// Gets a value indicating whether slice headers can modify the initial reference-picture lists. - /// - public bool ReferenceListModificationPresent { get; } - - /// - /// Gets the base-two logarithm of the parallel merge-estimation region width and height. - /// - public int ParallelMergeLevelLog2 { get; } - - /// - /// Gets a value indicating whether slice-segment headers carry extension bytes. - /// - public bool SliceSegmentHeaderExtensionPresent { get; } - - /// - /// Gets the base-two logarithm of the maximum transform-skip block width and height. - /// - public int MaxTransformSkipBlockLog2 { get; private set; } - - /// - /// Gets a value indicating whether cross-component residual prediction is enabled. - /// - public bool CrossComponentPredictionEnabled { get; private set; } - - /// - /// Gets the coding-tree depth at which chroma quantization-offset indices are signaled. - /// - public int ChromaQuantizationParameterOffsetDepth { get; private set; } - - /// - /// Gets the Cb offsets in the selectable chroma quantization-parameter offset list. - /// - public IReadOnlyList ChromaQuantizationParameterOffsetsCb { get; private set; } = Array.Empty(); - - /// - /// Gets the Cr offsets in the selectable chroma quantization-parameter offset list. - /// - public IReadOnlyList ChromaQuantizationParameterOffsetsCr { get; private set; } = Array.Empty(); - - /// - /// Gets the base-two logarithm of the luma sample-adaptive-offset value scale. - /// - public int SampleAdaptiveOffsetScaleLumaLog2 { get; private set; } - - /// - /// Gets the base-two logarithm of the chroma sample-adaptive-offset value scale. - /// - public int SampleAdaptiveOffsetScaleChromaLog2 { get; private set; } - - /// - /// Reads the Range Extensions fields that change transform, chroma quantization, and SAO reconstruction. - /// - /// The picture-parameter-set raw byte sequence payload reader. - /// - /// A transform, coding-tree-depth, chroma offset, or sample-adaptive-offset scale is outside the governing - /// sequence-parameter-set bounds. - /// - private void ReadRangeExtension(ref HevcBitReader reader) - { - if (this.TransformSkipEnabled) - { - uint maxTransformSkipBlockLog2MinusTwo = reader.ReadUnsignedExpGolomb(); - if (maxTransformSkipBlockLog2MinusTwo > this.SequenceParameterSet.MaxTransformBlockLog2 - 2) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid transform-skip block size."); - } - - this.MaxTransformSkipBlockLog2 = (int)maxTransformSkipBlockLog2MinusTwo + 2; - } - - this.CrossComponentPredictionEnabled = reader.ReadFlag(); - if (reader.ReadFlag()) - { - uint chromaOffsetDepth = reader.ReadUnsignedExpGolomb(); - int maximumDepth = this.SequenceParameterSet.CodingTreeBlockLog2 - this.SequenceParameterSet.MinCodingBlockLog2; - if (chromaOffsetDepth > maximumDepth) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid chroma quantization-offset depth."); - } - - this.ChromaQuantizationParameterOffsetDepth = (int)chromaOffsetDepth; - - uint chromaOffsetCountMinusOne = reader.ReadUnsignedExpGolomb(); - if (chromaOffsetCountMinusOne > 5) - { - throw new InvalidImageContentException("The HEVC picture parameter set declares too many chroma quantization offsets."); - } - - int chromaOffsetCount = (int)chromaOffsetCountMinusOne + 1; - int[] cbOffsets = new int[chromaOffsetCount]; - int[] crOffsets = new int[chromaOffsetCount]; - for (int offset = 0; offset < chromaOffsetCount; offset++) - { - cbOffsets[offset] = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - crOffsets[offset] = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - } - - this.ChromaQuantizationParameterOffsetsCb = cbOffsets; - this.ChromaQuantizationParameterOffsetsCr = crOffsets; - } - - uint lumaScale = reader.ReadUnsignedExpGolomb(); - uint chromaScale = reader.ReadUnsignedExpGolomb(); - int maximumLumaScale = Math.Max(this.SequenceParameterSet.BitDepthLuma, 10) - 10; - int maximumChromaScale = Math.Max(this.SequenceParameterSet.BitDepthChroma, 10) - 10; - if (lumaScale > maximumLumaScale || chromaScale > maximumChromaScale) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid sample-adaptive-offset scale."); - } - - this.SampleAdaptiveOffsetScaleLumaLog2 = (int)lumaScale; - this.SampleAdaptiveOffsetScaleChromaLog2 = (int)chromaScale; - } - - /// - /// Reads or derives one axis of the tile grid. - /// - /// The picture-parameter-set raw byte sequence payload reader. - /// The complete picture dimension in coding-tree blocks. - /// The tile count on the same axis. - /// Whether the widths or heights use proportional uniform spacing. - /// Every tile width or height in coding-tree blocks, including the inferred final tile. - /// An explicit tile consumes the final block required by a later tile. - private static int[] ReadTileDimensions( - ref HevcBitReader reader, - int codingTreeBlockCount, - int tileCount, - bool uniformSpacing) - { - int[] dimensions = new int[tileCount]; - if (uniformSpacing) - { - for (int tile = 0; tile < tileCount; tile++) - { - // The normative floor-difference formula assigns every CTB exactly once even when the picture - // dimension is not divisible by the number of tiles. - dimensions[tile] = (((tile + 1) * codingTreeBlockCount) / tileCount) - - ((tile * codingTreeBlockCount) / tileCount); - } - - return dimensions; - } - - int consumed = 0; - for (int tile = 0; tile < tileCount - 1; tile++) - { - uint dimensionMinusOne = reader.ReadUnsignedExpGolomb(); - if (dimensionMinusOne >= codingTreeBlockCount - consumed - 1) - { - throw new InvalidImageContentException("The HEVC picture parameter set has an invalid explicit tile dimension."); - } - - dimensions[tile] = (int)dimensionMinusOne + 1; - consumed += dimensions[tile]; - } - - dimensions[^1] = codingTreeBlockCount - consumed; - return dimensions; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPlane.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPlane.cs deleted file mode 100644 index cdf81f1ea..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPlane.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies an HEVC luma or chroma reconstruction plane. -/// -internal enum HevcPlane -{ - /// - /// The luma or first separate-color plane. - /// - Y = 0, - - /// - /// The blue-difference chroma or second separate-color plane. - /// - Cb = 1, - - /// - /// The red-difference chroma or third separate-color plane. - /// - Cr = 2, -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs deleted file mode 100644 index 66d1e401e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the general HEVC profile, tier, constraint, and level description declared by a parameter set. -/// -internal sealed class HevcProfileTierLevel -{ - /// - /// Initializes a new instance of the class. - /// - /// The parameter-set raw byte sequence payload reader. - /// The highest declared temporal sublayer index. - /// - /// The profile-tier-level syntax is truncated or contains nonzero reserved bits. - /// - public HevcProfileTierLevel(ref HevcBitReader reader, int maxSubLayersMinusOne) - { - DebugGuard.MustBeBetweenOrEqualTo(maxSubLayersMinusOne, 0, 6, nameof(maxSubLayersMinusOne)); - - this.ProfileSpace = (byte)reader.ReadBits(2); - this.TierFlag = reader.ReadFlag(); - this.ProfileIdc = (byte)reader.ReadBits(5); - this.ProfileCompatibilityFlags = reader.ReadBits(32); - - // The configuration record carries these 48 bits verbatim. Preserve their exact ordering so the - // parameter set can be checked without reinterpreting profile-specific constraint layouts. - this.ConstraintIndicatorFlags = ((ulong)reader.ReadBits(16) << 32) | reader.ReadBits(32); - this.LevelIdc = (byte)reader.ReadBits(8); - - Span subLayerProfilePresent = stackalloc bool[6]; - Span subLayerLevelPresent = stackalloc bool[6]; - for (int subLayer = 0; subLayer < maxSubLayersMinusOne; subLayer++) - { - subLayerProfilePresent[subLayer] = reader.ReadFlag(); - subLayerLevelPresent[subLayer] = reader.ReadFlag(); - } - - if (maxSubLayersMinusOne > 0) - { - for (int subLayer = maxSubLayersMinusOne; subLayer < 8; subLayer++) - { - if (reader.ReadBits(2) != 0) - { - throw new InvalidImageContentException("The HEVC profile-tier-level syntax has nonzero reserved bits."); - } - } - } - - for (int subLayer = 0; subLayer < maxSubLayersMinusOne; subLayer++) - { - if (subLayerProfilePresent[subLayer]) - { - // A sublayer profile repeats the fixed 88-bit profile and constraint structure. It is consumed - // for alignment but not retained because one still-image item has no temporal playback model. - reader.ReadBits(2); - reader.ReadFlag(); - reader.ReadBits(5); - reader.ReadBits(32); - reader.ReadBits(16); - reader.ReadBits(32); - } - - if (subLayerLevelPresent[subLayer]) - { - reader.ReadBits(8); - } - } - } - - /// - /// Gets the namespace of the declared profile identifier. - /// - public byte ProfileSpace { get; } - - /// - /// Gets a value indicating whether the high tier is declared. - /// - public bool TierFlag { get; } - - /// - /// Gets the five-bit profile identifier. - /// - public byte ProfileIdc { get; } - - /// - /// Gets the profile-compatibility flags. - /// - public uint ProfileCompatibilityFlags { get; } - - /// - /// Gets the 48-bit profile-constraint flags. - /// - public ulong ConstraintIndicatorFlags { get; } - - /// - /// Gets the eight-bit level identifier. - /// - public byte LevelIdc { get; } - - /// - /// Determines whether this parameter-set description is compatible with an image item's codec-configuration - /// property. - /// - /// The associated HEVC codec configuration. - /// when the general profile, tier, compatibility, and level fields match. - public bool Matches(HevcCodecConfiguration configuration) - => this.ProfileSpace == configuration.GeneralProfileSpace - && this.TierFlag == configuration.GeneralTierFlag - && this.ProfileIdc == configuration.GeneralProfileIdc - && this.ProfileCompatibilityFlags == configuration.GeneralProfileCompatibilityFlags - && this.LevelIdc == configuration.GeneralLevelIdc; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs deleted file mode 100644 index 3f7a5e288..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the effective HEVC quantization parameters for one transform unit. -/// -internal readonly struct HevcQuantizationParameters -{ - /// - /// Initializes a new instance of the struct. - /// - /// The effective coding-unit luma quantization parameter before the luma bit-depth offset. - /// The reconstructed luma precision. - /// The reconstructed chroma precision. - /// The sequence chroma-format identifier. - /// The combined picture, slice, and coding-unit Cb quantization-parameter offset. - /// The combined picture, slice, and coding-unit Cr quantization-parameter offset. - public HevcQuantizationParameters( - int lumaQuantizationParameter, - int lumaBitDepth, - int chromaBitDepth, - byte chromaFormat, - int cbQuantizationParameterOffset, - int crQuantizationParameterOffset) - { - int lumaBitDepthOffset = 6 * (lumaBitDepth - 8); - int chromaBitDepthOffset = 6 * (chromaBitDepth - 8); - this.CbOffset = cbQuantizationParameterOffset; - this.CrOffset = crQuantizationParameterOffset; - this.Luma = lumaQuantizationParameter + lumaBitDepthOffset; - this.Cb = GetChromaQuantizationParameter(lumaQuantizationParameter, cbQuantizationParameterOffset, chromaBitDepthOffset, chromaFormat); - this.Cr = GetChromaQuantizationParameter(lumaQuantizationParameter, crQuantizationParameterOffset, chromaBitDepthOffset, chromaFormat); - } - - /// - /// Gets the effective nonnegative luma quantization parameter including its bit-depth offset. - /// - public int Luma { get; } - - /// - /// Gets the effective nonnegative blue-difference chroma quantization parameter including its bit-depth offset. - /// - public int Cb { get; } - - /// - /// Gets the effective nonnegative red-difference chroma quantization parameter including its bit-depth offset. - /// - public int Cr { get; } - - /// - /// Gets the combined picture, slice, and coding-unit Cb quantization-parameter offset. - /// - public int CbOffset { get; } - - /// - /// Gets the combined picture, slice, and coding-unit Cr quantization-parameter offset. - /// - public int CrOffset { get; } - - /// - /// Gets the H.265 Table 8-10 chroma quantization-parameter mapping for 4:2:0 pictures. - /// - private static ReadOnlySpan Chroma420QuantizationParameterMap => - [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 29, 30, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - ]; - - /// - /// Gets the effective quantization parameter for the selected reconstruction plane. - /// - /// The reconstruction plane. - /// The effective nonnegative quantization parameter including its bit-depth offset. - public int Get(HevcPlane plane) => plane switch - { - HevcPlane.Y => this.Luma, - HevcPlane.Cb => this.Cb, - _ => this.Cr, - }; - - /// - /// Derives an effective chroma quantization parameter from the luma value and combined component offset. - /// - /// The effective coding-unit luma quantization parameter before its bit-depth offset. - /// The combined picture, slice, and coding-unit component offset. - /// Six times the number of chroma bits above eight. - /// The sequence chroma-format identifier. - /// The effective nonnegative chroma quantization parameter including its bit-depth offset. - public static int GetChromaQuantizationParameter( - int lumaQuantizationParameter, - int componentOffset, - int chromaBitDepthOffset, - byte chromaFormat) - { - int unscaled = Math.Clamp(lumaQuantizationParameter + componentOffset, -chromaBitDepthOffset, 57); - if (unscaled < 0) - { - return unscaled + chromaBitDepthOffset; - } - - // H.265 section 8.6.1 maps nonnegative chroma QP before adding the bit-depth offset. The 4:2:0 table - // contains plateaus above QP 29, whereas 4:2:2 and 4:4:4 remain linear through 51 and then saturate. - int mapped = chromaFormat == 1 - ? Chroma420QuantizationParameterMap[unscaled] - : Math.Min(unscaled, 51); - - return mapped + chromaBitDepthOffset; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcReconstructionState.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcReconstructionState.cs deleted file mode 100644 index 4fa3fceec..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcReconstructionState.cs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Tracks reconstructed minimum prediction blocks for HEVC intra-reference availability. -/// -internal sealed class HevcReconstructionState : IDisposable -{ - /// - /// The base-two logarithm of the minimum luma prediction-block side. - /// - private const int MinPredictionBlockLog2 = 2; - - /// - /// The reconstruction-region identifiers for the three component planes. - /// - private readonly Buffer2D[] regions; - - /// - /// The horizontal chroma subsampling shift. - /// - private readonly int chromaSubsamplingX; - - /// - /// The vertical chroma subsampling shift. - /// - private readonly int chromaSubsamplingY; - - /// - /// The coded luma width used to reject padded right-edge units. - /// - private readonly int width; - - /// - /// The coded luma height used to reject padded bottom-edge units. - /// - private readonly int height; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing the image memory allocator. - /// The coded picture and chroma geometry. - public HevcReconstructionState(Configuration configuration, HevcSequenceParameterSet sequenceParameterSet) - { - this.width = sequenceParameterSet.Width; - this.height = sequenceParameterSet.Height; - int widthInUnits = DivideCeilingByPowerOfTwo(this.width, MinPredictionBlockLog2); - int heightInUnits = DivideCeilingByPowerOfTwo(this.height, MinPredictionBlockLog2); - this.chromaSubsamplingX = !sequenceParameterSet.SeparateColorPlaneFlag && sequenceParameterSet.ChromaFormat is 1 or 2 ? 1 : 0; - this.chromaSubsamplingY = !sequenceParameterSet.SeparateColorPlaneFlag && sequenceParameterSet.ChromaFormat == 1 ? 1 : 0; - - Buffer2D? lumaRegions = null; - Buffer2D? chromaBlueRegions = null; - Buffer2D? chromaRedRegions = null; - try - { - // Region identifiers gate every reconstructed-neighbor read. A stale pooled identifier can match the first - // region of a later picture, so these maps must begin at the reserved unavailable value zero. - lumaRegions = configuration.MemoryAllocator.Allocate2D(widthInUnits, heightInUnits, AllocationOptions.Clean); - chromaBlueRegions = configuration.MemoryAllocator.Allocate2D(widthInUnits, heightInUnits, AllocationOptions.Clean); - chromaRedRegions = configuration.MemoryAllocator.Allocate2D(widthInUnits, heightInUnits, AllocationOptions.Clean); - this.regions = [lumaRegions, chromaBlueRegions, chromaRedRegions]; - } - catch - { - chromaRedRegions?.Dispose(); - chromaBlueRegions?.Dispose(); - lumaRegions?.Dispose(); - throw; - } - } - - /// - /// Gets the horizontal availability-unit width for a component plane. - /// - /// The component plane. - /// The availability-unit width in component samples. - public int GetUnitWidth(HevcPlane plane) => 1 << (MinPredictionBlockLog2 - this.GetSubsamplingX(plane)); - - /// - /// Gets the vertical availability-unit height for a component plane. - /// - /// The component plane. - /// The availability-unit height in component samples. - public int GetUnitHeight(HevcPlane plane) => 1 << (MinPredictionBlockLog2 - this.GetSubsamplingY(plane)); - - /// - /// Marks a reconstructed component rectangle as available within one slice-and-tile prediction region. - /// - /// The reconstructed component plane. - /// The rectangle left coordinate in component samples. - /// The rectangle top coordinate in component samples. - /// The rectangle width in component samples. - /// The rectangle height in component samples. - /// The positive identifier shared by prediction blocks in the same slice segment and tile. - public void MarkReconstructed(HevcPlane plane, int x, int y, int width, int height, int regionId) - { - DebugGuard.MustBeGreaterThan(regionId, 0, nameof(regionId)); - int subsamplingX = this.GetSubsamplingX(plane); - int subsamplingY = this.GetSubsamplingY(plane); - int unitX = (x << subsamplingX) >> MinPredictionBlockLog2; - int unitY = (y << subsamplingY) >> MinPredictionBlockLog2; - int endX = DivideCeilingByPowerOfTwo((x + width) << subsamplingX, MinPredictionBlockLog2); - int endY = DivideCeilingByPowerOfTwo((y + height) << subsamplingY, MinPredictionBlockLog2); - Buffer2D map = this.regions[(int)plane]; - endX = Math.Min(endX, map.Width); - endY = Math.Min(endY, map.Height); - - // Chroma availability units map back to the same four-by-four luma grid used by HEVC neighbor derivation. - // Filling the complete rectangle makes later sub-TUs observe only samples whose reconstruction has finished. - for (int row = unitY; row < endY; row++) - { - map.DangerousGetRowSpan(row)[unitX..endX].Fill(regionId); - } - } - - /// - /// Builds the ordered availability flags consumed by HEVC reference-sample substitution. - /// - /// The component plane containing the prediction block. - /// The prediction-block left coordinate in component samples. - /// The prediction-block top coordinate in component samples. - /// The base-two logarithm of the square prediction-block side. - /// The current slice-and-tile prediction-region identifier. - /// - /// The destination ordered from the bottom-most below-left unit through top-left and then the above-right units. - /// - /// The number of flags written. - public int BuildReferenceAvailability(HevcPlane plane, int x, int y, int log2Size, int regionId, Span destination) - { - DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); - DebugGuard.MustBeGreaterThan(regionId, 0, nameof(regionId)); - int size = 1 << log2Size; - int unitWidth = this.GetUnitWidth(plane); - int unitHeight = this.GetUnitHeight(plane); - int leftUnitCount = (size * 2) / unitHeight; - int aboveUnitCount = (size * 2) / unitWidth; - int flagCount = leftUnitCount + aboveUnitCount + 1; - Span availability = destination[..flagCount]; - - for (int unit = 0; unit < leftUnitCount; unit++) - { - int unitY = y + ((leftUnitCount - unit - 1) * unitHeight); - availability[unit] = this.IsAvailable(plane, x - 1, unitY, regionId); - } - - availability[leftUnitCount] = this.IsAvailable(plane, x - 1, y - 1, regionId); - for (int unit = 0; unit < aboveUnitCount; unit++) - { - availability[leftUnitCount + unit + 1] = this.IsAvailable(plane, x + (unit * unitWidth), y - 1, regionId); - } - - return flagCount; - } - - /// - /// Gets whether one component sample has already been reconstructed in the selected prediction region. - /// - /// The component plane. - /// The component sample X coordinate. - /// The component sample Y coordinate. - /// The current slice-and-tile prediction-region identifier. - /// when the sample is available; otherwise, . - public bool IsReconstructed(HevcPlane plane, int x, int y, int regionId) => this.IsAvailable(plane, x, y, regionId); - - /// - /// Releases the owned reconstruction-region maps. - /// - public void Dispose() - { - foreach (Buffer2D map in this.regions) - { - map.Dispose(); - } - } - - /// - /// Gets whether a component sample belongs to an already reconstructed block in the selected prediction region. - /// - /// The component plane. - /// The component sample X coordinate. - /// The component sample Y coordinate. - /// The current slice-and-tile prediction-region identifier. - /// when the sample is available; otherwise, . - private bool IsAvailable(HevcPlane plane, int x, int y, int regionId) - { - if (x < 0 || y < 0) - { - return false; - } - - int subsamplingX = this.GetSubsamplingX(plane); - int subsamplingY = this.GetSubsamplingY(plane); - int planeWidth = DivideCeilingByPowerOfTwo(this.width, subsamplingX); - int planeHeight = DivideCeilingByPowerOfTwo(this.height, subsamplingY); - if (x >= planeWidth || y >= planeHeight) - { - return false; - } - - int unitX = (x << subsamplingX) >> MinPredictionBlockLog2; - int unitY = (y << subsamplingY) >> MinPredictionBlockLog2; - Buffer2D map = this.regions[(int)plane]; - return (uint)unitX < (uint)map.Width - && (uint)unitY < (uint)map.Height - && map.DangerousGetRowSpan(unitY)[unitX] == regionId; - } - - /// - /// Gets the horizontal chroma shift selected by a component plane. - /// - /// The component plane. - /// Zero for luma and full-resolution planes; otherwise, the chroma shift. - private int GetSubsamplingX(HevcPlane plane) => plane == HevcPlane.Y ? 0 : this.chromaSubsamplingX; - - /// - /// Gets the vertical chroma shift selected by a component plane. - /// - /// The component plane. - /// Zero for luma and full-resolution planes; otherwise, the chroma shift. - private int GetSubsamplingY(HevcPlane plane) => plane == HevcPlane.Y ? 0 : this.chromaSubsamplingY; - - /// - /// Divides a nonnegative sample count by a power of two with upward rounding. - /// - /// The sample count. - /// The base-two divisor logarithm. - /// The upward-rounded quotient. - private static int DivideCeilingByPowerOfTwo(int value, int shift) => (value + (1 << shift) - 1) >> shift; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualDpcmMode.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualDpcmMode.cs deleted file mode 100644 index 49cc61360..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualDpcmMode.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies the differential pulse-code modulation applied to an HEVC residual block. -/// -internal enum HevcResidualDpcmMode : byte -{ - /// - /// No residual differential pulse-code modulation is applied. - /// - None = 0, - - /// - /// Residual differences accumulate from left to right within each row. - /// - Horizontal = 1, - - /// - /// Residual differences accumulate from top to bottom within each column. - /// - Vertical = 2, -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.LeftShiftTransformSkipOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.LeftShiftTransformSkipOperator.cs deleted file mode 100644 index f714b5a6e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.LeftShiftTransformSkipOperator.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcResidualReconstructor -{ - /// - /// Applies the exact left shift used by high-bit-depth transform-skip reconstruction. - /// - private readonly struct LeftShiftTransformSkipOperator : ITransformSkipOperator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 values, int shift) => values << shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 values, int shift) => values << shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 values, int shift) => values << shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(int value, int shift) => value << shift; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.Operator.cs deleted file mode 100644 index 5604eba5b..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.Operator.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcResidualReconstructor -{ - /// - /// Defines a closed transform-skip normalization operator for every SIMD width and the scalar tail. - /// - private interface ITransformSkipOperator - { - /// - /// Normalizes sixteen transform-skipped coefficients. - /// - /// The dequantized coefficients. - /// The nonnegative shift magnitude. - /// The reconstructed residuals. - static abstract Vector512 Invoke(Vector512 values, int shift); - - /// - /// Normalizes eight transform-skipped coefficients. - /// - /// The dequantized coefficients. - /// The nonnegative shift magnitude. - /// The reconstructed residuals. - static abstract Vector256 Invoke(Vector256 values, int shift); - - /// - /// Normalizes four transform-skipped coefficients. - /// - /// The dequantized coefficients. - /// The nonnegative shift magnitude. - /// The reconstructed residuals. - static abstract Vector128 Invoke(Vector128 values, int shift); - - /// - /// Normalizes one transform-skipped coefficient. - /// - /// The dequantized coefficient. - /// The nonnegative shift magnitude. - /// The reconstructed residual. - static abstract int Invoke(int value, int shift); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.RightShiftTransformSkipOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.RightShiftTransformSkipOperator.cs deleted file mode 100644 index 92d32e44e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.RightShiftTransformSkipOperator.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcResidualReconstructor -{ - /// - /// Applies the rounded right shift used by ordinary transform-skip reconstruction. - /// - private readonly struct RightShiftTransformSkipOperator : ITransformSkipOperator - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 values, int shift) - => shift == 0 ? values : (values + Vector512.Create(1 << (shift - 1))) >> shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 values, int shift) - => shift == 0 ? values : (values + Vector256.Create(1 << (shift - 1))) >> shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 values, int shift) - => shift == 0 ? values : (values + Vector128.Create(1 << (shift - 1))) >> shift; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(int value, int shift) => shift == 0 ? value : (value + (1 << (shift - 1))) >> shift; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs deleted file mode 100644 index 1c7710540..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Reconstructs HEVC transform-skipped, bypassed, and differential residual blocks. -/// -/// -/// Consecutive residual samples are widened to signed 32-bit lanes for normalization and prediction addition. Closed -/// static operators encode the selected transform-skip shift so the JIT specializes left-shift, rounded-right-shift, -/// and identity cases outside the row loops. Saturation to the residual range and clipping to sample depth occur at the -/// same stage in every vector width and in the scalar tail. -/// -internal static partial class HevcResidualReconstructor -{ - /// - /// The minimum residual sample represented by the decoder reconstruction pipeline. - /// - private const int ResidualMinimum = short.MinValue; - - /// - /// The maximum residual sample represented by the decoder reconstruction pipeline. - /// - private const int ResidualMaximum = short.MaxValue; - - /// - /// Copies one transquant-bypass coefficient block into residual sample order. - /// - /// The decoded coefficients in raster order. - /// The destination residual block in packed raster order. - /// Whether the complete coefficient order is reversed. - public static void CopyBypassed(ReadOnlySpan coefficients, Span residual, bool rotate) - { - Span destination = residual[..coefficients.Length]; - if (!rotate) - { - coefficients.CopyTo(destination); - return; - } - - CopyReversed(coefficients, destination); - } - - /// - /// Reconstructs one transform-skipped residual block from dequantized coefficients. - /// - /// The dequantized coefficients in raster order. - /// The destination residual block in packed raster order. - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The base-two logarithm of the equivalent square transform size. - /// Whether transform-skip precision is extended by the sequence. - /// Whether the complete coefficient order is reversed. - public static void ApplyTransformSkip( - ReadOnlySpan coefficients, - Span residual, - int width, - int height, - int bitDepth, - int maxTransformDynamicRange, - int equivalentLog2TransformSize, - bool extendedPrecisionProcessingEnabled, - bool rotate) - { - int transformShift = maxTransformDynamicRange - bitDepth - equivalentLog2TransformSize; - if (extendedPrecisionProcessingEnabled) - { - transformShift = Math.Max(0, transformShift); - } - - int coefficientCount = width * height; - if (transformShift >= 0) - { - ApplyTransformSkip(coefficients[..coefficientCount], residual[..coefficientCount], transformShift, rotate); - } - else - { - ApplyTransformSkip(coefficients[..coefficientCount], residual[..coefficientCount], -transformShift, rotate); - } - } - - /// - /// Gets whether a non-transformed residual block uses the HEVC Range Extensions coefficient rotation. - /// - /// Whether the sequence enables transform-skip rotation. - /// Whether the transform unit belongs to an intra-predicted coding unit. - /// The transform-block width. - /// when the complete coefficient order is reversed; otherwise, . - public static bool IsNonTransformedResidualRotated(bool transformSkipRotationEnabled, bool isIntraPredicted, int width) - => transformSkipRotationEnabled && isIntraPredicted && width == 4; - - /// - /// Gets the implicit residual differential mode selected by an intra-prediction direction. - /// - /// The resolved luma or chroma intra-prediction mode. - /// Whether the 4:2:2 chroma intra-angle remapping applies. - /// The residual differential mode selected by the prediction direction. - public static HevcResidualDpcmMode GetImplicitResidualDpcmMode(int intraPredictionMode, bool remapChroma422) - { - int predictionMode = remapChroma422 ? HevcIntraPredictionMode.RemapChroma422(intraPredictionMode) : intraPredictionMode; - return predictionMode switch - { - HevcIntraPredictionMode.Horizontal => HevcResidualDpcmMode.Horizontal, - HevcIntraPredictionMode.Vertical => HevcResidualDpcmMode.Vertical, - _ => HevcResidualDpcmMode.None, - }; - } - - /// - /// Applies inverse residual differential pulse-code modulation to one packed residual block. - /// - /// The residual block in packed raster order. - /// The residual-block width. - /// The residual-block height. - /// The differential accumulation direction. - public static void ApplyResidualDpcm(Span residual, int width, int height, HevcResidualDpcmMode mode) - { - if (mode == HevcResidualDpcmMode.Vertical) - { - ApplyVerticalResidualDpcm(residual, width, height); - } - else if (mode == HevcResidualDpcmMode.Horizontal) - { - ApplyHorizontalResidualDpcm(residual, width, height); - } - } - - /// - /// Adds the scaled luma residual to one chroma residual block for inverse cross-component prediction. - /// - /// The packed luma residual samples colocated with the chroma block. - /// The packed chroma residual block updated in place. - /// The number of residual samples in each block. - /// The signed cross-component scale from minus eight through eight. - /// The luma bit depth minus the chroma bit depth. - public static void ApplyCrossComponentPrediction( - ReadOnlySpan lumaResidual, - Span chromaResidual, - int sampleCount, - int alpha, - int bitDepthDifference) - { - ref int lumaBase = ref MemoryMarshal.GetReference(lumaResidual); - ref int chromaBase = ref MemoryMarshal.GetReference(chromaResidual); - int index = 0; - - // The scale denominator is eight. Adjusting luma precision first preserves the normative arithmetic shift - // for negative residuals before the signed alpha multiplication is applied independently to every lane. - if (Vector512.IsHardwareAccelerated) - { - Vector512 alphaVector = Vector512.Create(alpha); - Vector512 minimum = Vector512.Create(ResidualMinimum); - Vector512 maximum = Vector512.Create(ResidualMaximum); - for (; index <= sampleCount - Vector512.Count; index += Vector512.Count) - { - Vector512 luma = AdjustBitDepth(Vector512.LoadUnsafe(ref lumaBase, (nuint)index), bitDepthDifference); - Vector512 chroma = Vector512.LoadUnsafe(ref chromaBase, (nuint)index); - Vector512.Clamp(chroma + ((luma * alphaVector) >> 3), minimum, maximum).StoreUnsafe(ref chromaBase, (nuint)index); - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 alphaVector = Vector256.Create(alpha); - Vector256 minimum = Vector256.Create(ResidualMinimum); - Vector256 maximum = Vector256.Create(ResidualMaximum); - for (; index <= sampleCount - Vector256.Count; index += Vector256.Count) - { - Vector256 luma = AdjustBitDepth(Vector256.LoadUnsafe(ref lumaBase, (nuint)index), bitDepthDifference); - Vector256 chroma = Vector256.LoadUnsafe(ref chromaBase, (nuint)index); - Vector256.Clamp(chroma + ((luma * alphaVector) >> 3), minimum, maximum).StoreUnsafe(ref chromaBase, (nuint)index); - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 alphaVector = Vector128.Create(alpha); - Vector128 minimum = Vector128.Create(ResidualMinimum); - Vector128 maximum = Vector128.Create(ResidualMaximum); - for (; index <= sampleCount - Vector128.Count; index += Vector128.Count) - { - Vector128 luma = AdjustBitDepth(Vector128.LoadUnsafe(ref lumaBase, (nuint)index), bitDepthDifference); - Vector128 chroma = Vector128.LoadUnsafe(ref chromaBase, (nuint)index); - Vector128.Clamp(chroma + ((luma * alphaVector) >> 3), minimum, maximum).StoreUnsafe(ref chromaBase, (nuint)index); - } - } - - for (; index < sampleCount; index++) - { - int luma = AdjustBitDepth(Unsafe.Add(ref lumaBase, index), bitDepthDifference); - int chroma = Unsafe.Add(ref chromaBase, index) + ((alpha * luma) >> 3); - Unsafe.Add(ref chromaBase, index) = Math.Clamp(chroma, ResidualMinimum, ResidualMaximum); - } - } - - /// - /// Adjusts sixteen luma residuals to chroma precision. - /// - /// The luma residuals. - /// The luma bit depth minus the chroma bit depth. - /// The precision-adjusted residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 AdjustBitDepth(Vector512 values, int difference) - => difference >= 0 ? values >> difference : values << -difference; - - /// - /// Adjusts eight luma residuals to chroma precision. - /// - /// The luma residuals. - /// The luma bit depth minus the chroma bit depth. - /// The precision-adjusted residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 AdjustBitDepth(Vector256 values, int difference) - => difference >= 0 ? values >> difference : values << -difference; - - /// - /// Adjusts four luma residuals to chroma precision. - /// - /// The luma residuals. - /// The luma bit depth minus the chroma bit depth. - /// The precision-adjusted residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 AdjustBitDepth(Vector128 values, int difference) - => difference >= 0 ? values >> difference : values << -difference; - - /// - /// Adjusts one luma residual to chroma precision. - /// - /// The luma residual. - /// The luma bit depth minus the chroma bit depth. - /// The precision-adjusted residual. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int AdjustBitDepth(int value, int difference) => difference >= 0 ? value >> difference : value << -difference; - - /// - /// Applies one transform-skip normalization operator to a complete coefficient block. - /// - /// The signed shift operator selected before entering the hot loop. - /// The dequantized coefficients in raster order. - /// The destination residual block in packed raster order. - /// The nonnegative shift magnitude. - /// Whether the complete coefficient order is reversed. - private static void ApplyTransformSkip(ReadOnlySpan coefficients, Span residual, int shift, bool rotate) - where TOperator : struct, ITransformSkipOperator - { - ref int sourceBase = ref MemoryMarshal.GetReference(coefficients); - ref int destinationBase = ref MemoryMarshal.GetReference(residual); - int count = coefficients.Length; - int index = 0; - - // Rotation reverses the complete raster sequence, not the lanes of independently loaded forward chunks. Each - // load therefore starts at the mirrored chunk and shuffles its lanes before the common destination traversal. - if (Vector512.IsHardwareAccelerated) - { - for (; index <= count - Vector512.Count; index += Vector512.Count) - { - Vector512 values = Load512(ref sourceBase, count, index, rotate); - TOperator.Invoke(values, shift).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector256.IsHardwareAccelerated) - { - for (; index <= count - Vector256.Count; index += Vector256.Count) - { - Vector256 values = Load256(ref sourceBase, count, index, rotate); - TOperator.Invoke(values, shift).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector128.IsHardwareAccelerated) - { - for (; index <= count - Vector128.Count; index += Vector128.Count) - { - Vector128 values = Load128(ref sourceBase, count, index, rotate); - TOperator.Invoke(values, shift).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - for (; index < count; index++) - { - int sourceIndex = rotate ? count - 1 - index : index; - Unsafe.Add(ref destinationBase, index) = TOperator.Invoke(Unsafe.Add(ref sourceBase, sourceIndex), shift); - } - } - - /// - /// Copies one coefficient block while reversing its complete raster order. - /// - /// The source coefficient block. - /// The destination residual block. - private static void CopyReversed(ReadOnlySpan source, Span destination) - { - ref int sourceBase = ref MemoryMarshal.GetReference(source); - ref int destinationBase = ref MemoryMarshal.GetReference(destination); - int count = source.Length; - int index = 0; - - // The descending source loads and ascending destination stores never overlap because callers provide distinct - // coefficient and residual spans. The shared index permits a scalar tail for non-vector-sized blocks. - if (Vector512.IsHardwareAccelerated) - { - for (; index <= count - Vector512.Count; index += Vector512.Count) - { - Load512(ref sourceBase, count, index, true).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector256.IsHardwareAccelerated) - { - for (; index <= count - Vector256.Count; index += Vector256.Count) - { - Load256(ref sourceBase, count, index, true).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector128.IsHardwareAccelerated) - { - for (; index <= count - Vector128.Count; index += Vector128.Count) - { - Load128(ref sourceBase, count, index, true).StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - for (; index < count; index++) - { - Unsafe.Add(ref destinationBase, index) = Unsafe.Add(ref sourceBase, count - 1 - index); - } - } - - /// - /// Loads and optionally reverses sixteen source coefficients. - /// - /// The first source coefficient. - /// The complete coefficient count. - /// The destination coefficient index. - /// Whether the complete coefficient order is reversed. - /// The source coefficients in destination order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 Load512(ref int source, int count, int index, bool rotate) - { - if (!rotate) - { - return Vector512.LoadUnsafe(ref source, (nuint)index); - } - - Vector512 values = Vector512.LoadUnsafe(ref source, (nuint)(count - index - Vector512.Count)); - return Vector512.Shuffle(values, Vector512.Create(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); - } - - /// - /// Loads and optionally reverses eight source coefficients. - /// - /// The first source coefficient. - /// The complete coefficient count. - /// The destination coefficient index. - /// Whether the complete coefficient order is reversed. - /// The source coefficients in destination order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 Load256(ref int source, int count, int index, bool rotate) - { - if (!rotate) - { - return Vector256.LoadUnsafe(ref source, (nuint)index); - } - - Vector256 values = Vector256.LoadUnsafe(ref source, (nuint)(count - index - Vector256.Count)); - return Vector256.Shuffle(values, Vector256.Create(7, 6, 5, 4, 3, 2, 1, 0)); - } - - /// - /// Loads and optionally reverses four source coefficients. - /// - /// The first source coefficient. - /// The complete coefficient count. - /// The destination coefficient index. - /// Whether the complete coefficient order is reversed. - /// The source coefficients in destination order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 Load128(ref int source, int count, int index, bool rotate) - { - if (!rotate) - { - return Vector128.LoadUnsafe(ref source, (nuint)index); - } - - Vector128 values = Vector128.LoadUnsafe(ref source, (nuint)(count - index - Vector128.Count)); - return Vector128.Shuffle(values, Vector128.Create(3, 2, 1, 0)); - } - - /// - /// Accumulates residual differences from top to bottom while processing independent columns in SIMD lanes. - /// - /// The residual block in packed raster order. - /// The residual-block width. - /// The residual-block height. - private static void ApplyVerticalResidualDpcm(Span residual, int width, int height) - { - ref int residualBase = ref MemoryMarshal.GetReference(residual); - int x = 0; - - // Lanes are independent columns. Carrying the reconstructed row above in the accumulator removes the need for - // a horizontal shuffle while preserving the top-to-bottom dependency of residual DPCM. - if (Vector512.IsHardwareAccelerated) - { - Vector512 minimum = Vector512.Create(ResidualMinimum); - Vector512 maximum = Vector512.Create(ResidualMaximum); - for (; x <= width - Vector512.Count; x += Vector512.Count) - { - Vector512 accumulator = Vector512.LoadUnsafe(ref residualBase, (nuint)x); - for (int y = 1; y < height; y++) - { - int index = (y * width) + x; - accumulator += Vector512.LoadUnsafe(ref residualBase, (nuint)index); - Vector512.Clamp(accumulator, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)index); - } - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 minimum = Vector256.Create(ResidualMinimum); - Vector256 maximum = Vector256.Create(ResidualMaximum); - for (; x <= width - Vector256.Count; x += Vector256.Count) - { - Vector256 accumulator = Vector256.LoadUnsafe(ref residualBase, (nuint)x); - for (int y = 1; y < height; y++) - { - int index = (y * width) + x; - accumulator += Vector256.LoadUnsafe(ref residualBase, (nuint)index); - Vector256.Clamp(accumulator, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)index); - } - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 minimum = Vector128.Create(ResidualMinimum); - Vector128 maximum = Vector128.Create(ResidualMaximum); - for (; x <= width - Vector128.Count; x += Vector128.Count) - { - Vector128 accumulator = Vector128.LoadUnsafe(ref residualBase, (nuint)x); - for (int y = 1; y < height; y++) - { - int index = (y * width) + x; - accumulator += Vector128.LoadUnsafe(ref residualBase, (nuint)index); - Vector128.Clamp(accumulator, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)index); - } - } - } - - for (; x < width; x++) - { - int accumulator = Unsafe.Add(ref residualBase, x); - for (int y = 1; y < height; y++) - { - int index = (y * width) + x; - accumulator += Unsafe.Add(ref residualBase, index); - Unsafe.Add(ref residualBase, index) = Math.Clamp(accumulator, ResidualMinimum, ResidualMaximum); - } - } - } - - /// - /// Accumulates residual differences from left to right using an inclusive SIMD prefix sum for each row. - /// - /// The residual block in packed raster order. - /// The residual-block width. - /// The residual-block height. - private static void ApplyHorizontalResidualDpcm(Span residual, int width, int height) - { - ref int residualBase = ref MemoryMarshal.GetReference(residual); - for (int y = 0; y < height; y++) - { - int rowOffset = y * width; - int x = 0; - int accumulator = 0; - - // PrefixSum resolves dependencies inside a vector. The final lane then seeds the next vector width or the - // scalar tail, so changing SIMD width cannot change the left-to-right accumulation order. - if (Vector512.IsHardwareAccelerated) - { - Vector512 minimum = Vector512.Create(ResidualMinimum); - Vector512 maximum = Vector512.Create(ResidualMaximum); - for (; x <= width - Vector512.Count; x += Vector512.Count) - { - Vector512 values = Vector512.LoadUnsafe(ref residualBase, (nuint)(rowOffset + x)); - values = PrefixSum(values) + Vector512.Create(accumulator); - accumulator = values.GetElement(Vector512.Count - 1); - Vector512.Clamp(values, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)(rowOffset + x)); - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 minimum = Vector256.Create(ResidualMinimum); - Vector256 maximum = Vector256.Create(ResidualMaximum); - for (; x <= width - Vector256.Count; x += Vector256.Count) - { - Vector256 values = Vector256.LoadUnsafe(ref residualBase, (nuint)(rowOffset + x)); - values = PrefixSum(values) + Vector256.Create(accumulator); - accumulator = values.GetElement(Vector256.Count - 1); - Vector256.Clamp(values, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)(rowOffset + x)); - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 minimum = Vector128.Create(ResidualMinimum); - Vector128 maximum = Vector128.Create(ResidualMaximum); - for (; x <= width - Vector128.Count; x += Vector128.Count) - { - Vector128 values = Vector128.LoadUnsafe(ref residualBase, (nuint)(rowOffset + x)); - values = PrefixSum(values) + Vector128.Create(accumulator); - accumulator = values.GetElement(Vector128.Count - 1); - Vector128.Clamp(values, minimum, maximum).StoreUnsafe(ref residualBase, (nuint)(rowOffset + x)); - } - } - - for (; x < width; x++) - { - int index = rowOffset + x; - accumulator += Unsafe.Add(ref residualBase, index); - Unsafe.Add(ref residualBase, index) = x == 0 ? accumulator : Math.Clamp(accumulator, ResidualMinimum, ResidualMaximum); - } - } - } - - /// - /// Computes an inclusive prefix sum across sixteen signed lanes. - /// - /// The residual differences. - /// The accumulated residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 PrefixSum(Vector512 values) - { - // Out-of-range shuffle indices create zero lanes. Distances 1, 2, 4, and 8 form an inclusive Hillis-Steele - // scan without carrying values backward across the start of the vector. - values += Vector512.Shuffle(values, Vector512.Create(16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)); - values += Vector512.Shuffle(values, Vector512.Create(16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)); - values += Vector512.Shuffle(values, Vector512.Create(16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); - return values + Vector512.Shuffle(values, Vector512.Create(16, 16, 16, 16, 16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7)); - } - - /// - /// Computes an inclusive prefix sum across eight signed lanes. - /// - /// The residual differences. - /// The accumulated residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 PrefixSum(Vector256 values) - { - // Out-of-range index eight supplies the zero lanes needed at each doubling step. - values += Vector256.Shuffle(values, Vector256.Create(8, 0, 1, 2, 3, 4, 5, 6)); - values += Vector256.Shuffle(values, Vector256.Create(8, 8, 0, 1, 2, 3, 4, 5)); - return values + Vector256.Shuffle(values, Vector256.Create(8, 8, 8, 8, 0, 1, 2, 3)); - } - - /// - /// Computes an inclusive prefix sum across four signed lanes. - /// - /// The residual differences. - /// The accumulated residuals. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 PrefixSum(Vector128 values) - { - // Out-of-range index four supplies the zero lanes needed at distances one and two. - values += Vector128.Shuffle(values, Vector128.Create(4, 0, 1, 2)); - return values + Vector128.Shuffle(values, Vector128.Create(4, 4, 0, 1)); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.BandOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.BandOperator.cs deleted file mode 100644 index 99b40cdd2..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.BandOperator.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcSampleAdaptiveOffsetFilter -{ - /// - /// Classifies samples by one of thirty-two most-significant-value bands. - /// - private readonly struct BandOperator : ISampleClassifier - { - /// - public static bool UsesNeighbors => false; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Classify( - Vector512 current, - Vector512 neighbor0, - Vector512 neighbor1, - in KernelParameters kernel) - => (Vector512.ShiftRightArithmetic(current, kernel.BandShift) - Vector512.Create(kernel.BandPosition)) & Vector512.Create((short)31); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Classify( - Vector256 current, - Vector256 neighbor0, - Vector256 neighbor1, - in KernelParameters kernel) - => (Vector256.ShiftRightArithmetic(current, kernel.BandShift) - Vector256.Create(kernel.BandPosition)) & Vector256.Create((short)31); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Classify( - Vector128 current, - Vector128 neighbor0, - Vector128 neighbor1, - in KernelParameters kernel) - => (Vector128.ShiftRightArithmetic(current, kernel.BandShift) - Vector128.Create(kernel.BandPosition)) & Vector128.Create((short)31); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Classify(short current, short neighbor0, short neighbor1, in KernelParameters kernel) - => ((current >> kernel.BandShift) - kernel.BandPosition) & 31; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.EdgeOperator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.EdgeOperator.cs deleted file mode 100644 index 0b07b37c8..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.EdgeOperator.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcSampleAdaptiveOffsetFilter -{ - /// - /// Classifies samples by the sum of their signs relative to two directional neighbors. - /// - private readonly struct EdgeOperator : ISampleClassifier - { - /// - public static bool UsesNeighbors => true; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Classify( - Vector512 current, - Vector512 neighbor0, - Vector512 neighbor1, - in KernelParameters kernel) - { - // Each comparison pair produces -1, 0, or 1. Adding two maps the normative edge classes onto the - // contiguous zero-through-four offset-table indices used by the selection kernel. - Vector512 one = Vector512.Create((short)1); - Vector512 sign0 = (Vector512.GreaterThan(current, neighbor0) & one) - (Vector512.LessThan(current, neighbor0) & one); - Vector512 sign1 = (Vector512.GreaterThan(current, neighbor1) & one) - (Vector512.LessThan(current, neighbor1) & one); - return sign0 + sign1 + Vector512.Create((short)2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Classify( - Vector256 current, - Vector256 neighbor0, - Vector256 neighbor1, - in KernelParameters kernel) - { - Vector256 one = Vector256.Create((short)1); - Vector256 sign0 = (Vector256.GreaterThan(current, neighbor0) & one) - (Vector256.LessThan(current, neighbor0) & one); - Vector256 sign1 = (Vector256.GreaterThan(current, neighbor1) & one) - (Vector256.LessThan(current, neighbor1) & one); - return sign0 + sign1 + Vector256.Create((short)2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Classify( - Vector128 current, - Vector128 neighbor0, - Vector128 neighbor1, - in KernelParameters kernel) - { - Vector128 one = Vector128.Create((short)1); - Vector128 sign0 = (Vector128.GreaterThan(current, neighbor0) & one) - (Vector128.LessThan(current, neighbor0) & one); - Vector128 sign1 = (Vector128.GreaterThan(current, neighbor1) & one) - (Vector128.LessThan(current, neighbor1) & one); - return sign0 + sign1 + Vector128.Create((short)2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Classify(short current, short neighbor0, short neighbor1, in KernelParameters kernel) - => Math.Sign(current - neighbor0) + Math.Sign(current - neighbor1) + 2; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.Operator.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.Operator.cs deleted file mode 100644 index af2adab55..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.Operator.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -internal static partial class HevcSampleAdaptiveOffsetFilter -{ - /// - /// Defines the sample classifier shared by the SIMD row traversal and scalar tail. - /// - private interface ISampleClassifier - { - /// - /// Gets a value indicating whether classification reads the two neighboring sample rows. - /// - public static abstract bool UsesNeighbors { get; } - - /// - /// Classifies thirty-two current samples against their two classifier inputs. - /// - /// The current sample lanes. - /// The first neighboring sample lanes. - /// The second neighboring sample lanes. - /// The scaled offset and band-class state. - /// The zero-based offset-table indices. - public static abstract Vector512 Classify( - Vector512 current, - Vector512 neighbor0, - Vector512 neighbor1, - in KernelParameters kernel); - - /// - /// Classifies sixteen current samples against their two classifier inputs. - /// - /// The current sample lanes. - /// The first neighboring sample lanes. - /// The second neighboring sample lanes. - /// The scaled offset and band-class state. - /// The zero-based offset-table indices. - public static abstract Vector256 Classify( - Vector256 current, - Vector256 neighbor0, - Vector256 neighbor1, - in KernelParameters kernel); - - /// - /// Classifies eight current samples against their two classifier inputs. - /// - /// The current sample lanes. - /// The first neighboring sample lanes. - /// The second neighboring sample lanes. - /// The scaled offset and band-class state. - /// The zero-based offset-table indices. - public static abstract Vector128 Classify( - Vector128 current, - Vector128 neighbor0, - Vector128 neighbor1, - in KernelParameters kernel); - - /// - /// Classifies one current sample against its two classifier inputs. - /// - /// The current sample. - /// The first neighboring sample. - /// The second neighboring sample. - /// The scaled offset and band-class state. - /// The zero-based offset-table index. - public static abstract int Classify(short current, short neighbor0, short neighbor1, in KernelParameters kernel); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs deleted file mode 100644 index e6bba520f..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Applies HEVC sample-adaptive offsets to reconstructed component blocks. -/// -/// -/// Each SIMD lane classifies one reconstructed sample. Band-offset operators derive the class directly from the current -/// value, while edge-offset operators compare aligned lanes from the two neighboring coordinates. The resulting class -/// indices select one of the signaled offsets, after which addition and bit-depth clipping remain lane-wise. A scalar -/// continuation handles only incomplete vectors at picture edges. -/// -internal static partial class HevcSampleAdaptiveOffsetFilter -{ - /// - /// Applies one resolved sample-adaptive-offset mode to a component coding-tree block. - /// - /// The immutable pre-SAO picture used for every classification. - /// The picture receiving filtered samples. - /// The component plane. - /// The block's left coordinate in component samples. - /// The block's top coordinate in component samples. - /// The block width in component samples. - /// The block height in component samples. - /// The resolved coded offsets and classifier. - /// The component offset scale from the picture range-extension parameters. - /// Whether classification may read the block immediately to the left. - /// Whether classification may read the block immediately to the right. - /// Whether classification may read the block immediately above. - /// Whether classification may read the block immediately below. - /// Whether classification may read the upper-left diagonal block. - /// Whether classification may read the upper-right diagonal block. - /// Whether classification may read the lower-left diagonal block. - /// Whether classification may read the lower-right diagonal block. - public static void ApplyBlock( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - in HevcSampleAdaptiveOffsetParameters parameters, - int offsetScaleLog2, - bool leftAvailable, - bool rightAvailable, - bool aboveAvailable, - bool belowAvailable, - bool aboveLeftAvailable, - bool aboveRightAvailable, - bool belowLeftAvailable, - bool belowRightAvailable) - { - if (parameters.Type == HevcSampleAdaptiveOffsetType.Off) - { - return; - } - - KernelParameters kernel = new(parameters, source.GetBitDepth(plane), offsetScaleLog2); - switch (parameters.Type) - { - case HevcSampleAdaptiveOffsetType.Band: - ApplyBand(source, destination, plane, x, y, width, height, in kernel); - break; - case HevcSampleAdaptiveOffsetType.EdgeHorizontal: - ApplyHorizontalEdges(source, destination, plane, x, y, width, height, leftAvailable, rightAvailable, in kernel); - break; - case HevcSampleAdaptiveOffsetType.EdgeVertical: - ApplyVerticalEdges(source, destination, plane, x, y, width, height, aboveAvailable, belowAvailable, in kernel); - break; - case HevcSampleAdaptiveOffsetType.EdgeDescending: - ApplyDescendingEdges( - source, - destination, - plane, - x, - y, - width, - height, - leftAvailable, - rightAvailable, - aboveAvailable, - belowAvailable, - aboveLeftAvailable, - belowRightAvailable, - in kernel); - break; - case HevcSampleAdaptiveOffsetType.EdgeAscending: - ApplyAscendingEdges( - source, - destination, - plane, - x, - y, - width, - height, - leftAvailable, - rightAvailable, - aboveAvailable, - belowAvailable, - aboveRightAvailable, - belowLeftAvailable, - in kernel); - break; - } - } - - /// - /// Applies band offsets to every sample in a component block. - /// - /// The immutable pre-SAO picture. - /// The destination picture. - /// The component plane. - /// The block's left coordinate. - /// The block's top coordinate. - /// The block width. - /// The block height. - /// The scaled offset and band-class state. - private static void ApplyBand( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - in KernelParameters kernel) - { - for (int row = y; row < y + height; row++) - { - ReadOnlySpan sourceRow = source.GetRowSpan(plane, row).Slice(x, width); - Span destinationRow = destination.GetRowSpan(plane, row).Slice(x, width); - - // Band classification depends only on the current sample. The closed classifier's UsesNeighbors value removes - // the two neighbor loads when this generic traversal is specialized for BandOperator. - ApplyRow(sourceRow, sourceRow, sourceRow, destinationRow, in kernel); - } - } - - /// - /// Applies horizontal edge offsets within the available left and right boundaries. - /// - /// The immutable pre-SAO picture. - /// The destination picture. - /// The component plane. - /// The block's left coordinate. - /// The block's top coordinate. - /// The block width. - /// The block height. - /// Whether the left neighboring block is available. - /// Whether the right neighboring block is available. - /// The scaled offset state. - private static void ApplyHorizontalEdges( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - bool leftAvailable, - bool rightAvailable, - in KernelParameters kernel) - { - int start = x + (leftAvailable ? 0 : 1); - int end = x + width - (rightAvailable ? 0 : 1); - int count = end - start; - if (count <= 0) - { - return; - } - - for (int row = y; row < y + height; row++) - { - ReadOnlySpan sourceRow = source.GetRowSpan(plane, row); - ApplyRow( - sourceRow.Slice(start, count), - sourceRow.Slice(start - 1, count), - sourceRow.Slice(start + 1, count), - destination.GetRowSpan(plane, row).Slice(start, count), - in kernel); - } - } - - /// - /// Applies vertical edge offsets within the available upper and lower boundaries. - /// - /// The immutable pre-SAO picture. - /// The destination picture. - /// The component plane. - /// The block's left coordinate. - /// The block's top coordinate. - /// The block width. - /// The block height. - /// Whether the upper neighboring block is available. - /// Whether the lower neighboring block is available. - /// The scaled offset state. - private static void ApplyVerticalEdges( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - bool aboveAvailable, - bool belowAvailable, - in KernelParameters kernel) - { - int start = y + (aboveAvailable ? 0 : 1); - int end = y + height - (belowAvailable ? 0 : 1); - for (int row = start; row < end; row++) - { - ApplyRow( - source.GetRowSpan(plane, row).Slice(x, width), - source.GetRowSpan(plane, row - 1).Slice(x, width), - source.GetRowSpan(plane, row + 1).Slice(x, width), - destination.GetRowSpan(plane, row).Slice(x, width), - in kernel); - } - } - - /// - /// Applies descending-diagonal edge offsets within the eight resolved block boundaries. - /// - /// The immutable pre-SAO picture. - /// The destination picture. - /// The component plane. - /// The block's left coordinate. - /// The block's top coordinate. - /// The block width. - /// The block height. - /// Whether the left neighboring block is available. - /// Whether the right neighboring block is available. - /// Whether the upper neighboring block is available. - /// Whether the lower neighboring block is available. - /// Whether the upper-left neighboring block is available. - /// Whether the lower-right neighboring block is available. - /// The scaled offset state. - private static void ApplyDescendingEdges( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - bool leftAvailable, - bool rightAvailable, - bool aboveAvailable, - bool belowAvailable, - bool aboveLeftAvailable, - bool belowRightAvailable, - in KernelParameters kernel) - { - int commonStart = x + (leftAvailable ? 0 : 1); - int commonEnd = x + width - (rightAvailable ? 0 : 1); - int lastRow = y + height - 1; - for (int row = y; row <= lastRow; row++) - { - int start = commonStart; - int end = commonEnd; - if (row == y) - { - start = aboveLeftAvailable ? x : x + 1; - end = aboveAvailable ? commonEnd : x + 1; - } - - if (row == lastRow) - { - start = Math.Max(start, belowAvailable ? commonStart : x + width - 1); - end = Math.Min(end, belowRightAvailable ? x + width : x + width - 1); - } - - int count = end - start; - if (count <= 0) - { - continue; - } - - ApplyRow( - source.GetRowSpan(plane, row).Slice(start, count), - source.GetRowSpan(plane, row - 1).Slice(start - 1, count), - source.GetRowSpan(plane, row + 1).Slice(start + 1, count), - destination.GetRowSpan(plane, row).Slice(start, count), - in kernel); - } - } - - /// - /// Applies ascending-diagonal edge offsets within the eight resolved block boundaries. - /// - /// The immutable pre-SAO picture. - /// The destination picture. - /// The component plane. - /// The block's left coordinate. - /// The block's top coordinate. - /// The block width. - /// The block height. - /// Whether the left neighboring block is available. - /// Whether the right neighboring block is available. - /// Whether the upper neighboring block is available. - /// Whether the lower neighboring block is available. - /// Whether the upper-right neighboring block is available. - /// Whether the lower-left neighboring block is available. - /// The scaled offset state. - private static void ApplyAscendingEdges( - HevcPictureBuffer source, - HevcPictureBuffer destination, - HevcPlane plane, - int x, - int y, - int width, - int height, - bool leftAvailable, - bool rightAvailable, - bool aboveAvailable, - bool belowAvailable, - bool aboveRightAvailable, - bool belowLeftAvailable, - in KernelParameters kernel) - { - int commonStart = x + (leftAvailable ? 0 : 1); - int commonEnd = x + width - (rightAvailable ? 0 : 1); - int lastRow = y + height - 1; - for (int row = y; row <= lastRow; row++) - { - int start = commonStart; - int end = commonEnd; - if (row == y) - { - start = aboveAvailable ? commonStart : x + width - 1; - end = aboveRightAvailable ? x + width : x + width - 1; - } - - if (row == lastRow) - { - start = Math.Max(start, belowLeftAvailable ? x : x + 1); - end = Math.Min(end, belowAvailable ? commonEnd : x + 1); - } - - int count = end - start; - if (count <= 0) - { - continue; - } - - ApplyRow( - source.GetRowSpan(plane, row).Slice(start, count), - source.GetRowSpan(plane, row - 1).Slice(start + 1, count), - source.GetRowSpan(plane, row + 1).Slice(start - 1, count), - destination.GetRowSpan(plane, row).Slice(start, count), - in kernel); - } - } - - /// - /// Applies one closed classifier to a contiguous row range using every accelerated SIMD width before the scalar tail. - /// - /// The band or edge classifier selected before entering the row. - /// The current source samples. - /// The first classifier input samples. - /// The second classifier input samples. - /// The destination samples. - /// The scaled offset and clamp state. - private static void ApplyRow( - ReadOnlySpan current, - ReadOnlySpan neighbor0, - ReadOnlySpan neighbor1, - Span destination, - in KernelParameters kernel) - where TClassifier : struct, ISampleClassifier - { - ref ushort currentBase = ref MemoryMarshal.GetReference(current); - ref ushort neighbor0Base = ref MemoryMarshal.GetReference(neighbor0); - ref ushort neighbor1Base = ref MemoryMarshal.GetReference(neighbor1); - ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); - int index = 0; - - // HEVC's exposed 8/10/12-bit profiles keep every sample and scaled offset inside Int16. Signed lanes therefore - // provide comparisons, addition, and saturation without the two widening stages an Int32 implementation needs. - if (Vector512.IsHardwareAccelerated) - { - Vector512 minimum = Vector512.Zero; - Vector512 maximum = Vector512.Create(kernel.Maximum); - for (; index <= current.Length - Vector512.Count; index += Vector512.Count) - { - Vector512 value = Vector512.LoadUnsafe(ref currentBase, (nuint)index).AsInt16(); - Vector512 first = TClassifier.UsesNeighbors ? Vector512.LoadUnsafe(ref neighbor0Base, (nuint)index).AsInt16() : default; - Vector512 second = TClassifier.UsesNeighbors ? Vector512.LoadUnsafe(ref neighbor1Base, (nuint)index).AsInt16() : default; - Vector512 classes = TClassifier.Classify(value, first, second, in kernel); - Vector512 filtered = Vector512.Clamp(value + SelectOffset(classes, in kernel), minimum, maximum); - filtered.AsUInt16().StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector256.IsHardwareAccelerated) - { - Vector256 minimum = Vector256.Zero; - Vector256 maximum = Vector256.Create(kernel.Maximum); - for (; index <= current.Length - Vector256.Count; index += Vector256.Count) - { - Vector256 value = Vector256.LoadUnsafe(ref currentBase, (nuint)index).AsInt16(); - Vector256 first = TClassifier.UsesNeighbors ? Vector256.LoadUnsafe(ref neighbor0Base, (nuint)index).AsInt16() : default; - Vector256 second = TClassifier.UsesNeighbors ? Vector256.LoadUnsafe(ref neighbor1Base, (nuint)index).AsInt16() : default; - Vector256 classes = TClassifier.Classify(value, first, second, in kernel); - Vector256 filtered = Vector256.Clamp(value + SelectOffset(classes, in kernel), minimum, maximum); - filtered.AsUInt16().StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - if (Vector128.IsHardwareAccelerated) - { - Vector128 minimum = Vector128.Zero; - Vector128 maximum = Vector128.Create(kernel.Maximum); - for (; index <= current.Length - Vector128.Count; index += Vector128.Count) - { - Vector128 value = Vector128.LoadUnsafe(ref currentBase, (nuint)index).AsInt16(); - Vector128 first = TClassifier.UsesNeighbors ? Vector128.LoadUnsafe(ref neighbor0Base, (nuint)index).AsInt16() : default; - Vector128 second = TClassifier.UsesNeighbors ? Vector128.LoadUnsafe(ref neighbor1Base, (nuint)index).AsInt16() : default; - Vector128 classes = TClassifier.Classify(value, first, second, in kernel); - Vector128 filtered = Vector128.Clamp(value + SelectOffset(classes, in kernel), minimum, maximum); - filtered.AsUInt16().StoreUnsafe(ref destinationBase, (nuint)index); - } - } - - for (; index < current.Length; index++) - { - short currentValue = (short)Unsafe.Add(ref currentBase, index); - short first = TClassifier.UsesNeighbors ? (short)Unsafe.Add(ref neighbor0Base, index) : default; - short second = TClassifier.UsesNeighbors ? (short)Unsafe.Add(ref neighbor1Base, index) : default; - int offsetIndex = TClassifier.Classify(currentValue, first, second, in kernel); - - int filtered = Unsafe.Add(ref currentBase, index) + SelectOffset(offsetIndex, in kernel); - Unsafe.Add(ref destinationBase, index) = (ushort)Math.Clamp(filtered, 0, kernel.Maximum); - } - } - - /// - /// Selects one of five signed offsets for thirty-two classifier indices. - /// - /// The zero-based classifier indices. - /// The five scaled offsets. - /// The selected signed offset in every lane. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 SelectOffset(Vector512 classes, in KernelParameters kernel) - { - // The table contains only five values and there is no portable 16-bit gather. A comparison chain keeps every - // class lane in registers and leaves unrecognized classes at the required zero offset. - Vector512 selected = Vector512.Zero; - selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)0)), Vector512.Create(kernel.Offset0), selected); - selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)1)), Vector512.Create(kernel.Offset1), selected); - selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)2)), Vector512.Create(kernel.Offset2), selected); - selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)3)), Vector512.Create(kernel.Offset3), selected); - return Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)4)), Vector512.Create(kernel.Offset4), selected); - } - - /// - /// Selects one of five signed offsets for sixteen classifier indices. - /// - /// The zero-based classifier indices. - /// The five scaled offsets. - /// The selected signed offset in every lane. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 SelectOffset(Vector256 classes, in KernelParameters kernel) - { - Vector256 selected = Vector256.Zero; - selected = Vector256.ConditionalSelect(Vector256.Equals(classes, Vector256.Create((short)0)), Vector256.Create(kernel.Offset0), selected); - selected = Vector256.ConditionalSelect(Vector256.Equals(classes, Vector256.Create((short)1)), Vector256.Create(kernel.Offset1), selected); - selected = Vector256.ConditionalSelect(Vector256.Equals(classes, Vector256.Create((short)2)), Vector256.Create(kernel.Offset2), selected); - selected = Vector256.ConditionalSelect(Vector256.Equals(classes, Vector256.Create((short)3)), Vector256.Create(kernel.Offset3), selected); - return Vector256.ConditionalSelect(Vector256.Equals(classes, Vector256.Create((short)4)), Vector256.Create(kernel.Offset4), selected); - } - - /// - /// Selects one of five signed offsets for eight classifier indices. - /// - /// The zero-based classifier indices. - /// The five scaled offsets. - /// The selected signed offset in every lane. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 SelectOffset(Vector128 classes, in KernelParameters kernel) - { - Vector128 selected = Vector128.Zero; - selected = Vector128.ConditionalSelect(Vector128.Equals(classes, Vector128.Create((short)0)), Vector128.Create(kernel.Offset0), selected); - selected = Vector128.ConditionalSelect(Vector128.Equals(classes, Vector128.Create((short)1)), Vector128.Create(kernel.Offset1), selected); - selected = Vector128.ConditionalSelect(Vector128.Equals(classes, Vector128.Create((short)2)), Vector128.Create(kernel.Offset2), selected); - selected = Vector128.ConditionalSelect(Vector128.Equals(classes, Vector128.Create((short)3)), Vector128.Create(kernel.Offset3), selected); - return Vector128.ConditionalSelect(Vector128.Equals(classes, Vector128.Create((short)4)), Vector128.Create(kernel.Offset4), selected); - } - - /// - /// Selects one of five signed offsets for one classifier index. - /// - /// The zero-based classifier index. - /// The five scaled offsets. - /// The selected signed offset, or zero for an unmodified class. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int SelectOffset(int classification, in KernelParameters kernel) - => classification switch - { - 0 => kernel.Offset0, - 1 => kernel.Offset1, - 2 => kernel.Offset2, - 3 => kernel.Offset3, - 4 => kernel.Offset4, - _ => 0, - }; - - /// - /// Contains one block's scaled offsets and invariant classification values. - /// - private readonly struct KernelParameters - { - /// - /// Initializes a new instance of the struct. - /// - /// The decoded signed offsets. - /// The component sample precision. - /// The component offset scale. - public KernelParameters(in HevcSampleAdaptiveOffsetParameters parameters, int bitDepth, int offsetScaleLog2) - { - // Range Extensions scales each coded offset once before filtering. Hoisting the shifts here keeps the - // classification loops to comparisons, table selection, one addition, and saturation. - this.Offset0 = (short)(parameters.Offset0 << offsetScaleLog2); - this.Offset1 = (short)(parameters.Offset1 << offsetScaleLog2); - this.Offset2 = (short)(parameters.Offset2 << offsetScaleLog2); - this.Offset3 = (short)(parameters.Offset3 << offsetScaleLog2); - this.Offset4 = (short)(parameters.Offset4 << offsetScaleLog2); - this.BandPosition = (short)parameters.BandPosition; - this.BandShift = bitDepth - 5; - this.Maximum = (short)((1 << bitDepth) - 1); - } - - /// - /// Gets the first scaled class offset. - /// - public short Offset0 { get; } - - /// - /// Gets the second scaled class offset. - /// - public short Offset1 { get; } - - /// - /// Gets the third scaled class offset. - /// - public short Offset2 { get; } - - /// - /// Gets the fourth scaled class offset. - /// - public short Offset3 { get; } - - /// - /// Gets the fifth scaled class offset. - /// - public short Offset4 { get; } - - /// - /// Gets the first active band class. - /// - public short BandPosition { get; } - - /// - /// Gets the number of low sample bits discarded to form one of thirty-two band classes. - /// - public int BandShift { get; } - - /// - /// Gets the largest component sample value. - /// - public short Maximum { get; } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetParameters.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetParameters.cs deleted file mode 100644 index b3dd836a1..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetParameters.cs +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies the HEVC sample-adaptive-offset classifier selected for one component coding-tree block. -/// -internal enum HevcSampleAdaptiveOffsetType : byte -{ - /// - /// No sample-adaptive offset is applied. - /// - Off, - - /// - /// Samples are classified by their most-significant sample-value band. - /// - Band, - - /// - /// Samples are classified by horizontal neighboring samples. - /// - EdgeHorizontal, - - /// - /// Samples are classified by vertical neighboring samples. - /// - EdgeVertical, - - /// - /// Samples are classified by neighbors on the descending diagonal. - /// - EdgeDescending, - - /// - /// Samples are classified by neighbors on the ascending diagonal. - /// - EdgeAscending, -} - -/// -/// Contains the resolved HEVC sample-adaptive offsets for one component coding-tree block. -/// -internal readonly struct HevcSampleAdaptiveOffsetParameters -{ - /// - /// Initializes a new instance of the struct. - /// - /// The sample classifier. - /// The first of four consecutive band classes. - /// The first band or full-valley offset. - /// The second band or half-valley offset. - /// The third band or plain-edge offset. - /// The fourth band or half-peak offset. - /// The full-peak offset. - public HevcSampleAdaptiveOffsetParameters( - HevcSampleAdaptiveOffsetType type, - int bandPosition, - int offset0, - int offset1, - int offset2, - int offset3, - int offset4) - { - this.Type = type; - this.BandPosition = bandPosition; - this.Offset0 = offset0; - this.Offset1 = offset1; - this.Offset2 = offset2; - this.Offset3 = offset3; - this.Offset4 = offset4; - } - - /// - /// Gets the sample classifier. - /// - public HevcSampleAdaptiveOffsetType Type { get; } - - /// - /// Gets the first of four consecutive band classes. - /// - public int BandPosition { get; } - - /// - /// Gets the first band or full-valley offset. - /// - public int Offset0 { get; } - - /// - /// Gets the second band or half-valley offset. - /// - public int Offset1 { get; } - - /// - /// Gets the third band or plain-edge offset. - /// - public int Offset2 { get; } - - /// - /// Gets the fourth band or half-peak offset. - /// - public int Offset3 { get; } - - /// - /// Gets the full-peak offset. - /// - public int Offset4 { get; } -} - -/// -/// Identifies the slice and tile governing in-loop filtering for one coding-tree block. -/// -internal readonly struct HevcLoopFilterRegion -{ - /// - /// Initializes a new instance of the struct. - /// - /// The first coding-tree block of the independent slice in tile-scan order. - /// The zero-based tile index. - /// Whether the governing slice permits filtering across its slice boundary. - /// Whether the governing slice disables deblocking. - /// Half the slice beta-threshold offset. - /// Half the slice clipping-threshold offset. - public HevcLoopFilterRegion( - int sliceStartAddressInTileScan, - int tileIndex, - bool loopFilterAcrossSlicesEnabled, - bool deblockingFilterDisabled, - int deblockingFilterBetaOffsetDiv2, - int deblockingFilterTcOffsetDiv2) - { - this.SliceStartAddressInTileScan = sliceStartAddressInTileScan; - this.TileIndex = tileIndex; - this.LoopFilterAcrossSlicesEnabled = loopFilterAcrossSlicesEnabled; - this.DeblockingFilterDisabled = deblockingFilterDisabled; - this.DeblockingFilterBetaOffsetDiv2 = deblockingFilterBetaOffsetDiv2; - this.DeblockingFilterTcOffsetDiv2 = deblockingFilterTcOffsetDiv2; - } - - /// - /// Gets the first coding-tree block of the independent slice in tile-scan order. - /// - public int SliceStartAddressInTileScan { get; } - - /// - /// Gets the zero-based tile index. - /// - public int TileIndex { get; } - - /// - /// Gets a value indicating whether the governing slice permits filtering across its slice boundary. - /// - public bool LoopFilterAcrossSlicesEnabled { get; } - - /// - /// Gets a value indicating whether the governing slice disables deblocking. - /// - public bool DeblockingFilterDisabled { get; } - - /// - /// Gets half the governing slice's beta-threshold offset. - /// - public int DeblockingFilterBetaOffsetDiv2 { get; } - - /// - /// Gets half the governing slice's clipping-threshold offset. - /// - public int DeblockingFilterTcOffsetDiv2 { get; } -} - -/// -/// Contains the eight coding-tree-block neighbor availability values used by HEVC in-loop filters. -/// -internal readonly struct HevcLoopFilterBoundaryAvailability -{ - /// - /// Initializes a new instance of the struct. - /// - /// Whether the left block is available. - /// Whether the right block is available. - /// Whether the block above is available. - /// Whether the block below is available. - /// Whether the upper-left block is available. - /// Whether the upper-right block is available. - /// Whether the lower-left block is available. - /// Whether the lower-right block is available. - public HevcLoopFilterBoundaryAvailability( - bool left, - bool right, - bool above, - bool below, - bool aboveLeft, - bool aboveRight, - bool belowLeft, - bool belowRight) - { - this.Left = left; - this.Right = right; - this.Above = above; - this.Below = below; - this.AboveLeft = aboveLeft; - this.AboveRight = aboveRight; - this.BelowLeft = belowLeft; - this.BelowRight = belowRight; - } - - /// - /// Gets a value indicating whether the left block is available. - /// - public bool Left { get; } - - /// - /// Gets a value indicating whether the right block is available. - /// - public bool Right { get; } - - /// - /// Gets a value indicating whether the block above is available. - /// - public bool Above { get; } - - /// - /// Gets a value indicating whether the block below is available. - /// - public bool Below { get; } - - /// - /// Gets a value indicating whether the upper-left block is available. - /// - public bool AboveLeft { get; } - - /// - /// Gets a value indicating whether the upper-right block is available. - /// - public bool AboveRight { get; } - - /// - /// Gets a value indicating whether the lower-left block is available. - /// - public bool BelowLeft { get; } - - /// - /// Gets a value indicating whether the lower-right block is available. - /// - public bool BelowRight { get; } -} - -/// -/// Owns resolved sample-adaptive-offset parameters and prediction and filter region identifiers for one picture. -/// -internal sealed class HevcSampleAdaptiveOffsetState : IDisposable -{ - /// - /// Whether any decoded component block enables sample-adaptive offset. - /// - private bool hasEnabledParameters; - - /// - /// The three component records for every raster-ordered coding-tree block. - /// - private readonly IMemoryOwner parameters; - - /// - /// The independent-slice and tile prediction region of every coding-tree block. - /// - private readonly IMemoryOwner regions; - - /// - /// The independent-slice and tile filter region of every coding-tree block and color plane. - /// - private readonly IMemoryOwner loopFilterRegions; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration providing pooled picture state. - /// The raster-ordered coding-tree-block count. - public HevcSampleAdaptiveOffsetState(Configuration configuration, int codingTreeBlockCount) - { - IMemoryOwner? parameters = null; - IMemoryOwner? regions = null; - IMemoryOwner? loopFilterRegions = null; - try - { - parameters = configuration.MemoryAllocator.Allocate(codingTreeBlockCount * 3); - - // Slice headers can disable SAO independently for luma and chroma. Initialize every component record to Off - // so an enabled component never causes untouched records from pooled memory to enter the picture-level pass. - parameters.Memory.Span.Clear(); - regions = configuration.MemoryAllocator.Allocate(codingTreeBlockCount * 3); - loopFilterRegions = configuration.MemoryAllocator.Allocate(codingTreeBlockCount * 3); - - this.parameters = parameters; - this.regions = regions; - this.loopFilterRegions = loopFilterRegions; - } - catch - { - loopFilterRegions?.Dispose(); - regions?.Dispose(); - parameters?.Dispose(); - throw; - } - } - - /// - /// Gets a value indicating whether any component block enables sample-adaptive offset. - /// - public bool HasEnabledParameters => this.hasEnabledParameters; - - /// - /// Gets the resolved component parameters for one coding-tree block. - /// - /// The raster-scan coding-tree-block address. - /// The component plane. - /// The resolved sample-adaptive-offset parameters. - public HevcSampleAdaptiveOffsetParameters Get(int rasterAddress, HevcPlane plane) - => this.parameters.Memory.Span[(rasterAddress * 3) + (int)plane]; - - /// - /// Stores resolved component parameters for one coding-tree block. - /// - /// The raster-scan coding-tree-block address. - /// The component plane. - /// The resolved sample-adaptive-offset parameters. - public void Set(int rasterAddress, HevcPlane plane, HevcSampleAdaptiveOffsetParameters value) - { - this.parameters.Memory.Span[(rasterAddress * 3) + (int)plane] = value; - this.hasEnabledParameters |= value.Type != HevcSampleAdaptiveOffsetType.Off; - } - - /// - /// Gets whether one coding-tree block belongs to the selected prediction region. - /// - /// The raster-scan coding-tree-block address. - /// The independently coded color plane, or luma for combined-plane coding. - /// The current independent-slice and tile prediction-region identifier. - /// when the block belongs to the region; otherwise, . - public bool IsInRegion(int rasterAddress, HevcPlane plane, int regionId) - => this.regions.Memory.Span[(rasterAddress * 3) + (int)plane] == regionId; - - /// - /// Records the prediction region after one coding-tree block's parameters are decoded. - /// - /// The raster-scan coding-tree-block address. - /// The independently coded color plane, or luma for combined-plane coding. - /// The positive prediction-region identifier. - public void SetRegion(int rasterAddress, HevcPlane plane, int regionId) - => this.regions.Memory.Span[(rasterAddress * 3) + (int)plane] = regionId; - - /// - /// Records the in-loop filter region for one coding-tree block. - /// - /// The raster-scan coding-tree-block address. - /// The independently coded color plane, or luma for combined-plane coding. - /// The governing independent-slice and tile state. - public void SetLoopFilterRegion(int rasterAddress, HevcPlane plane, HevcLoopFilterRegion value) - => this.loopFilterRegions.Memory.Span[(rasterAddress * 3) + (int)plane] = value; - - /// - /// Derives the picture, slice, and tile boundary availability used by the in-loop filters for one coding-tree block. - /// - /// The raster-scan coding-tree-block address. - /// The independently coded color plane, or luma for combined-plane coding. - /// The picture width in coding-tree blocks. - /// The picture height in coding-tree blocks. - /// Whether the picture permits filtering across tile boundaries. - /// The availability of all eight neighboring coding-tree blocks. - public HevcLoopFilterBoundaryAvailability GetLoopFilterBoundaryAvailability( - int rasterAddress, - HevcPlane plane, - int pictureWidth, - int pictureHeight, - bool loopFilterAcrossTilesEnabled) - { - int x = rasterAddress % pictureWidth; - int y = rasterAddress / pictureWidth; - HevcLoopFilterRegion current = this.GetLoopFilterRegion(rasterAddress, plane); - - // H.265 assigns left, above, and upper-left boundaries to the current slice, while right, below, and - // lower-right boundaries belong to the neighboring slice. This asymmetry makes filtering independent of CTB order. - bool left = x > 0 - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress - 1, plane), true, loopFilterAcrossTilesEnabled); - bool right = x + 1 < pictureWidth - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress + 1, plane), false, loopFilterAcrossTilesEnabled); - bool above = y > 0 - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress - pictureWidth, plane), true, loopFilterAcrossTilesEnabled); - bool below = y + 1 < pictureHeight - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress + pictureWidth, plane), false, loopFilterAcrossTilesEnabled); - bool aboveLeft = x > 0 && y > 0 - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress - pictureWidth - 1, plane), true, loopFilterAcrossTilesEnabled); - bool belowRight = x + 1 < pictureWidth && y + 1 < pictureHeight - && IsLoopFilterNeighborAvailable(current, this.GetLoopFilterRegion(rasterAddress + pictureWidth + 1, plane), false, loopFilterAcrossTilesEnabled); - - // The crossed diagonals do not have a fixed owner in raster order. The later independent slice owns the - // boundary flag, which is identified by its greater tile-scan start address. - bool aboveRight = x + 1 < pictureWidth && y > 0 - && IsLoopFilterDiagonalAvailable(current, this.GetLoopFilterRegion(rasterAddress - pictureWidth + 1, plane), loopFilterAcrossTilesEnabled); - bool belowLeft = x > 0 && y + 1 < pictureHeight - && IsLoopFilterDiagonalAvailable(current, this.GetLoopFilterRegion(rasterAddress + pictureWidth - 1, plane), loopFilterAcrossTilesEnabled); - - return new HevcLoopFilterBoundaryAvailability(left, right, above, below, aboveLeft, aboveRight, belowLeft, belowRight); - } - - /// - /// Gets the retained in-loop filter region for one coding-tree block. - /// - /// The raster-scan coding-tree-block address. - /// The independently coded color plane, or luma for combined-plane coding. - /// The retained slice and tile state. - public HevcLoopFilterRegion GetLoopFilterRegion(int rasterAddress, HevcPlane plane) - => this.loopFilterRegions.Memory.Span[(rasterAddress * 3) + (int)plane]; - - /// - /// Determines availability across a boundary with a direction-selected slice owner. - /// - /// The current block's region. - /// The neighboring block's region. - /// Whether the current slice controls a boundary between different slices. - /// Whether tile boundaries permit filtering. - /// when both slice and tile rules permit filtering. - private static bool IsLoopFilterNeighborAvailable( - HevcLoopFilterRegion current, - HevcLoopFilterRegion neighbor, - bool currentOwnsSliceBoundary, - bool loopFilterAcrossTilesEnabled) - { - bool sameSlice = current.SliceStartAddressInTileScan == neighbor.SliceStartAddressInTileScan; - bool sliceAvailable = sameSlice - || (currentOwnsSliceBoundary ? current.LoopFilterAcrossSlicesEnabled : neighbor.LoopFilterAcrossSlicesEnabled); - - return sliceAvailable && (loopFilterAcrossTilesEnabled || current.TileIndex == neighbor.TileIndex); - } - - /// - /// Determines availability across a crossed-diagonal boundary using the later slice as its owner. - /// - /// The current block's region. - /// The diagonally neighboring block's region. - /// Whether tile boundaries permit filtering. - /// when both slice and tile rules permit filtering. - private static bool IsLoopFilterDiagonalAvailable( - HevcLoopFilterRegion current, - HevcLoopFilterRegion neighbor, - bool loopFilterAcrossTilesEnabled) - { - bool currentOwnsSliceBoundary = current.SliceStartAddressInTileScan > neighbor.SliceStartAddressInTileScan; - return IsLoopFilterNeighborAvailable(current, neighbor, currentOwnsSliceBoundary, loopFilterAcrossTilesEnabled); - } - - /// - /// Releases the pooled sample-adaptive-offset picture state. - /// - public void Dispose() - { - this.loopFilterRegions.Dispose(); - this.regions.Dispose(); - this.parameters.Dispose(); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcScalingList.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcScalingList.cs deleted file mode 100644 index c3971d836..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcScalingList.cs +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the HEVC quantization scaling matrices and their large-transform DC coefficients. -/// -internal sealed class HevcScalingList -{ - /// - /// The number of matrix identifiers defined for each transform-size category. - /// - private const int MatrixCount = 6; - - /// - /// The number of decoded scaling coefficients stored for every transform-size category. - /// - private const int CompactCoefficientCount = (16 * MatrixCount) + (64 * MatrixCount * 3); - - /// - /// The number of separately coded DC coefficients. - /// - private const int DcCoefficientCount = 4 * MatrixCount; - - /// - /// The first separately coded DC coefficient in the contiguous coefficient store. - /// - private const int DcCoefficientOffset = CompactCoefficientCount; - - /// - /// The first transform-sized matrix in the contiguous coefficient store. - /// - private const int ExpandedCoefficientOffset = DcCoefficientOffset + DcCoefficientCount; - - /// - /// The number of transform-sized coefficients stored across every size and matrix identifier. - /// - private const int ExpandedCoefficientCount = MatrixCount * ((4 * 4) + (8 * 8) + (16 * 16) + (32 * 32)); - - /// - /// The compact syntax matrices, separately coded DC values, and transform-sized matrices. - /// - private readonly byte[] coefficients = new byte[ExpandedCoefficientOffset + ExpandedCoefficientCount]; - - /// - /// Initializes a new instance of the class with the normative default matrices. - /// - public HevcScalingList() - { - for (int sizeId = 0; sizeId < 4; sizeId++) - { - for (int matrixId = 0; matrixId < MatrixCount; matrixId++) - { - ReadOnlySpan source = sizeId == 0 - ? Default4x4 - : matrixId < 3 ? DefaultIntra8x8 : DefaultInter8x8; - - source.CopyTo(this.GetWritableMatrix(sizeId, matrixId)); - this.coefficients[GetDcCoefficientOffset(sizeId, matrixId)] = 16; - } - } - - this.ExpandMatrices(); - } - - /// - /// Gets the flat default matrix used by four-by-four transforms. - /// - private static ReadOnlySpan Default4x4 => - [ - 16, 16, 16, 16, - 16, 16, 16, 16, - 16, 16, 16, 16, - 16, 16, 16, 16, - ]; - - /// - /// Gets the default intra-predicted matrix used by transforms of eight-by-eight and larger. - /// - private static ReadOnlySpan DefaultIntra8x8 => - [ - 16, 16, 16, 16, 17, 18, 21, 24, - 16, 16, 16, 16, 17, 19, 22, 25, - 16, 16, 17, 18, 20, 22, 25, 29, - 16, 16, 18, 21, 24, 27, 31, 36, - 17, 17, 20, 24, 30, 35, 41, 47, - 18, 19, 22, 27, 35, 44, 54, 65, - 21, 22, 25, 31, 41, 54, 70, 88, - 24, 25, 29, 36, 47, 65, 88, 115, - ]; - - /// - /// Gets the default inter-predicted matrix used by transforms of eight-by-eight and larger. - /// - private static ReadOnlySpan DefaultInter8x8 => - [ - 16, 16, 16, 16, 17, 18, 20, 24, - 16, 16, 16, 17, 18, 20, 24, 25, - 16, 16, 17, 18, 20, 24, 25, 28, - 16, 17, 18, 20, 24, 25, 28, 33, - 17, 18, 20, 24, 25, 28, 33, 41, - 18, 20, 24, 25, 28, 33, 41, 54, - 20, 24, 25, 28, 33, 41, 54, 71, - 24, 25, 28, 33, 41, 54, 71, 91, - ]; - - /// - /// Gets the diagonal coefficient order for four-by-four matrices. - /// - private static ReadOnlySpan DiagonalScan4x4 => - [ - 0, 4, 1, 8, 5, 2, 12, 9, 6, 3, 13, 10, 7, 14, 11, 15 - ]; - - /// - /// Gets the diagonal coefficient order for matrices of eight-by-eight and larger. - /// - private static ReadOnlySpan DiagonalScan8x8 => - [ - 0, 8, 1, 16, 9, 2, 24, 17, 10, 3, 32, 25, 18, 11, 4, 40, - 33, 26, 19, 12, 5, 48, 41, 34, 27, 20, 13, 6, 56, 49, 42, 35, - 28, 21, 14, 7, 57, 50, 43, 36, 29, 22, 15, 58, 51, 44, 37, 30, - 23, 59, 52, 45, 38, 31, 60, 53, 46, 39, 61, 54, 47, 62, 55, 63 - ]; - - /// - /// Reads a complete scaling-list-data structure. - /// - /// The parameter-set raw byte sequence payload reader. - /// The decoded scaling matrices. - /// A prediction reference is outside its permitted matrix set. - public static HevcScalingList Parse(ref HevcBitReader reader) - { - HevcScalingList scalingList = new(); - for (int sizeId = 0; sizeId < 4; sizeId++) - { - int matrixStep = sizeId == 3 ? 3 : 1; - for (int matrixId = 0; matrixId < MatrixCount; matrixId += matrixStep) - { - bool predictionMode = reader.ReadFlag(); - if (!predictionMode) - { - uint matrixIdDelta = reader.ReadUnsignedExpGolomb(); - if (sizeId == 3) - { - if (matrixIdDelta > matrixId / 3) - { - throw new InvalidImageContentException("The HEVC scaling list references an unavailable matrix."); - } - - matrixIdDelta *= 3; - } - - if (matrixIdDelta > matrixId) - { - throw new InvalidImageContentException("The HEVC scaling list references an unavailable matrix."); - } - - int referenceMatrixId = matrixId - (int)matrixIdDelta; - if (referenceMatrixId != matrixId) - { - // Span copying uses ImageSharp's runtime-optimized memory path and preserves one scalar - // behavior model for these small, infrequently parsed coefficient tables. - scalingList.GetMatrix(sizeId, referenceMatrixId).CopyTo(scalingList.GetWritableMatrix(sizeId, matrixId)); - byte dcCoefficient = scalingList.coefficients[GetDcCoefficientOffset(sizeId, referenceMatrixId)]; - - scalingList.coefficients[GetDcCoefficientOffset(sizeId, matrixId)] = dcCoefficient; - } - - continue; - } - - int nextCoefficient = 8; - if (sizeId > 1) - { - nextCoefficient = (int)(((long)reader.ReadSignedExpGolomb() + 8) & 255); - scalingList.coefficients[GetDcCoefficientOffset(sizeId, matrixId)] = (byte)(nextCoefficient & 255); - } - - ReadOnlySpan scan = sizeId == 0 ? DiagonalScan4x4 : DiagonalScan8x8; - Span matrix = scalingList.GetWritableMatrix(sizeId, matrixId); - for (int coefficient = 0; coefficient < matrix.Length; coefficient++) - { - nextCoefficient = (int)(((long)nextCoefficient + reader.ReadSignedExpGolomb()) & 255); - matrix[scan[coefficient]] = (byte)nextCoefficient; - } - } - - if (sizeId == 3) - { - // HEVC signals only luma matrices at 32x32. Chroma uses the corresponding 16x16 matrices. - for (int matrixId = 0; matrixId < MatrixCount; matrixId++) - { - if (matrixId is 0 or 3) - { - continue; - } - - scalingList.GetMatrix(sizeId - 1, matrixId).CopyTo(scalingList.GetWritableMatrix(sizeId, matrixId)); - scalingList.coefficients[GetDcCoefficientOffset(sizeId, matrixId)] = scalingList.coefficients[GetDcCoefficientOffset(sizeId - 1, matrixId)]; - } - } - } - - scalingList.ExpandMatrices(); - return scalingList; - } - - /// - /// Gets a decoded scaling matrix. - /// - /// The transform-size category from zero for 4x4 through three for 32x32. - /// The prediction and color-component matrix identifier. - /// The 16 or 64 decoded scaling coefficients in raster order. - public ReadOnlySpan GetMatrix(int sizeId, int matrixId) - { - DebugGuard.MustBeBetweenOrEqualTo(sizeId, 0, 3, nameof(sizeId)); - DebugGuard.MustBeBetweenOrEqualTo(matrixId, 0, MatrixCount - 1, nameof(matrixId)); - int coefficientCount = GetCompactMatrixLength(sizeId); - return this.coefficients.AsSpan(GetCompactMatrixOffset(sizeId, matrixId), coefficientCount); - } - - /// - /// Gets a scaling matrix expanded to its transform dimensions. - /// - /// The transform-size category from zero for 4x4 through three for 32x32. - /// The prediction and color-component matrix identifier. - /// The transform-sized scaling coefficients in raster order. - public ReadOnlySpan GetExpandedMatrix(int sizeId, int matrixId) - { - DebugGuard.MustBeBetweenOrEqualTo(sizeId, 0, 3, nameof(sizeId)); - DebugGuard.MustBeBetweenOrEqualTo(matrixId, 0, MatrixCount - 1, nameof(matrixId)); - int coefficientCount = GetExpandedMatrixLength(sizeId); - return this.coefficients.AsSpan(GetExpandedMatrixOffset(sizeId, matrixId), coefficientCount); - } - - /// - /// Gets the DC scaling coefficient for a large transform matrix. - /// - /// The transform-size category. - /// The prediction and color-component matrix identifier. - /// The decoded DC coefficient. - public byte GetDcCoefficient(int sizeId, int matrixId) - { - DebugGuard.MustBeBetweenOrEqualTo(sizeId, 0, 3, nameof(sizeId)); - DebugGuard.MustBeBetweenOrEqualTo(matrixId, 0, MatrixCount - 1, nameof(matrixId)); - return this.coefficients[GetDcCoefficientOffset(sizeId, matrixId)]; - } - - /// - /// Expands every syntax matrix once so inverse quantization can consume consecutive weights without coordinate division. - /// - private void ExpandMatrices() - { - for (int sizeId = 0; sizeId < 4; sizeId++) - { - int size = 1 << (sizeId + 2); - int ratio = Math.Max(1, size >> 3); - int sourceSide = Math.Min(size, 8); - for (int matrixId = 0; matrixId < MatrixCount; matrixId++) - { - ReadOnlySpan source = this.GetMatrix(sizeId, matrixId); - Span destination = this.coefficients.AsSpan(GetExpandedMatrixOffset(sizeId, matrixId), size * size); - for (int y = 0; y < size; y++) - { - int sourceRowOffset = (y / ratio) * sourceSide; - int destinationRowOffset = y * size; - for (int x = 0; x < size; x++) - { - destination[destinationRowOffset + x] = source[sourceRowOffset + (x / ratio)]; - } - } - - if (sizeId > 1) - { - // Sixteen- and thirty-two-point matrices code their DC weight separately from the 8x8 body. - destination[0] = this.coefficients[GetDcCoefficientOffset(sizeId, matrixId)]; - } - } - } - } - - /// - /// Gets a writable compact syntax matrix. - /// - /// The transform-size category. - /// The matrix identifier. - /// The writable compact matrix. - private Span GetWritableMatrix(int sizeId, int matrixId) - => this.coefficients.AsSpan(GetCompactMatrixOffset(sizeId, matrixId), GetCompactMatrixLength(sizeId)); - - /// - /// Gets the number of coefficients coded for one syntax matrix. - /// - /// The transform-size category. - /// The compact coefficient count. - private static int GetCompactMatrixLength(int sizeId) => sizeId == 0 ? 16 : 64; - - /// - /// Gets the number of coefficients in one transform-sized matrix. - /// - /// The transform-size category. - /// The expanded coefficient count. - private static int GetExpandedMatrixLength(int sizeId) => 1 << ((sizeId + 2) * 2); - - /// - /// Gets the compact-matrix offset for a size and matrix identifier. - /// - /// The transform-size category. - /// The matrix identifier. - /// The compact-matrix offset. - private static int GetCompactMatrixOffset(int sizeId, int matrixId) - { - int sizeOffset = sizeId switch - { - 0 => 0, - 1 => 16 * MatrixCount, - 2 => (16 * MatrixCount) + (64 * MatrixCount), - _ => (16 * MatrixCount) + (64 * MatrixCount * 2), - }; - - return sizeOffset + (matrixId * GetCompactMatrixLength(sizeId)); - } - - /// - /// Gets the expanded-matrix offset for a size and matrix identifier. - /// - /// The transform-size category. - /// The matrix identifier. - /// The expanded-matrix offset. - private static int GetExpandedMatrixOffset(int sizeId, int matrixId) - { - int sizeOffset = sizeId switch - { - 0 => 0, - 1 => 16 * MatrixCount, - 2 => (16 * MatrixCount) + (64 * MatrixCount), - _ => (16 * MatrixCount) + (64 * MatrixCount) + (256 * MatrixCount), - }; - - return ExpandedCoefficientOffset + sizeOffset + (matrixId * GetExpandedMatrixLength(sizeId)); - } - - /// - /// Gets the separately coded DC-coefficient offset for a size and matrix identifier. - /// - /// The transform-size category. - /// The matrix identifier. - /// The DC-coefficient offset. - private static int GetDcCoefficientOffset(int sizeId, int matrixId) => DcCoefficientOffset + (sizeId * MatrixCount) + matrixId; -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSequenceParameterSet.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSequenceParameterSet.cs deleted file mode 100644 index 1f8e5585d..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSequenceParameterSet.cs +++ /dev/null @@ -1,573 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the HEVC sequence fields required to reconstruct one independently coded still image. -/// -internal sealed class HevcSequenceParameterSet -{ - /// - /// Initializes a new instance of the class. - /// - /// The decoded sequence-parameter-set NAL unit. - /// The sequence parameter set is malformed or outside the still-image profile. - public HevcSequenceParameterSet(HevcNalUnit nalUnit) - { - const byte sequenceParameterSetNalUnitType = 33; - if (nalUnit.Header.NalUnitType != sequenceParameterSetNalUnitType - || nalUnit.Header.LayerId != 0 - || nalUnit.Header.TemporalId != 0) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid NAL-unit header."); - } - - HevcBitReader reader = new(nalUnit.Rbsp.Span); - this.VideoParameterSetId = (byte)reader.ReadBits(4); - int maxSubLayersMinusOne = (int)reader.ReadBits(3); - if (maxSubLayersMinusOne > 6) - { - throw new InvalidImageContentException("The HEVC sequence parameter set declares too many temporal sublayers."); - } - - this.MaxSubLayers = maxSubLayersMinusOne + 1; - this.TemporalIdNestingFlag = reader.ReadFlag(); - if (maxSubLayersMinusOne == 0 && !this.TemporalIdNestingFlag) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has invalid temporal nesting."); - } - - this.ProfileTierLevel = new HevcProfileTierLevel(ref reader, maxSubLayersMinusOne); - uint sequenceParameterSetId = reader.ReadUnsignedExpGolomb(); - if (sequenceParameterSetId > 15) - { - throw new InvalidImageContentException("The HEVC sequence parameter set identifier is invalid."); - } - - this.Id = (byte)sequenceParameterSetId; - uint chromaFormat = reader.ReadUnsignedExpGolomb(); - if (chromaFormat > 3) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid chroma format."); - } - - this.ChromaFormat = (byte)chromaFormat; - this.SeparateColorPlaneFlag = this.ChromaFormat == 3 && reader.ReadFlag(); - - uint width = reader.ReadUnsignedExpGolomb(); - uint height = reader.ReadUnsignedExpGolomb(); - if (width is 0 or > int.MaxValue || height is 0 or > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has invalid coded dimensions."); - } - - this.Width = (int)width; - this.Height = (int)height; - if (reader.ReadFlag()) - { - int cropUnitWidth = HevcParameterSetSyntax.GetCropUnitWidth(this.ChromaFormat, this.SeparateColorPlaneFlag); - int cropUnitHeight = HevcParameterSetSyntax.GetCropUnitHeight(this.ChromaFormat, this.SeparateColorPlaneFlag); - this.ConformanceWindowLeftOffset = ReadScaledOffset(ref reader, cropUnitWidth); - this.ConformanceWindowRightOffset = ReadScaledOffset(ref reader, cropUnitWidth); - this.ConformanceWindowTopOffset = ReadScaledOffset(ref reader, cropUnitHeight); - this.ConformanceWindowBottomOffset = ReadScaledOffset(ref reader, cropUnitHeight); - } - - if ((long)this.ConformanceWindowLeftOffset + this.ConformanceWindowRightOffset >= this.Width - || (long)this.ConformanceWindowTopOffset + this.ConformanceWindowBottomOffset >= this.Height) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid conformance window."); - } - - this.DisplayWidth = this.Width - this.ConformanceWindowLeftOffset - this.ConformanceWindowRightOffset; - this.DisplayHeight = this.Height - this.ConformanceWindowTopOffset - this.ConformanceWindowBottomOffset; - - this.BitDepthLuma = ReadBitDepth(ref reader); - this.BitDepthChroma = ReadBitDepth(ref reader); - uint log2MaxPictureOrderCountLsbMinusFour = reader.ReadUnsignedExpGolomb(); - if (log2MaxPictureOrderCountLsbMinusFour > 12) - { - throw new InvalidImageContentException("The HEVC picture-order-count width is invalid."); - } - - this.PictureOrderCountLsbBits = (int)log2MaxPictureOrderCountLsbMinusFour + 4; - - bool subLayerOrderingInfoPresent = reader.ReadFlag(); - int firstOrderingSubLayer = subLayerOrderingInfoPresent ? 0 : maxSubLayersMinusOne; - for (int subLayer = firstOrderingSubLayer; subLayer <= maxSubLayersMinusOne; subLayer++) - { - uint maxDecodedPictureBufferingMinusOne = reader.ReadUnsignedExpGolomb(); - uint maxNumReorderPictures = reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - if (maxNumReorderPictures > maxDecodedPictureBufferingMinusOne) - { - throw new InvalidImageContentException("The HEVC sequence parameter set has invalid sublayer ordering limits."); - } - } - - uint minCodingBlockLog2MinusThree = reader.ReadUnsignedExpGolomb(); - if (minCodingBlockLog2MinusThree > 3) - { - throw new InvalidImageContentException("The HEVC minimum coding-block size is invalid."); - } - - this.MinCodingBlockLog2 = (int)minCodingBlockLog2MinusThree + 3; - uint codingBlockSizeDifference = reader.ReadUnsignedExpGolomb(); - if (codingBlockSizeDifference > 6 - this.MinCodingBlockLog2) - { - throw new InvalidImageContentException("The HEVC coding-tree-block size is invalid."); - } - - this.CodingTreeBlockLog2 = this.MinCodingBlockLog2 + (int)codingBlockSizeDifference; - - uint minTransformBlockLog2MinusTwo = reader.ReadUnsignedExpGolomb(); - if (minTransformBlockLog2MinusTwo > this.MinCodingBlockLog2 - 3) - { - throw new InvalidImageContentException("The HEVC minimum transform-block size is invalid."); - } - - this.MinTransformBlockLog2 = (int)minTransformBlockLog2MinusTwo + 2; - uint transformBlockSizeDifference = reader.ReadUnsignedExpGolomb(); - int maximumTransformBlockLog2 = Math.Min(5, this.CodingTreeBlockLog2); - if (transformBlockSizeDifference > maximumTransformBlockLog2 - this.MinTransformBlockLog2) - { - throw new InvalidImageContentException("The HEVC maximum transform-block size is invalid."); - } - - this.MaxTransformBlockLog2 = this.MinTransformBlockLog2 + (int)transformBlockSizeDifference; - uint maxTransformHierarchyDepthInter = reader.ReadUnsignedExpGolomb(); - uint maxTransformHierarchyDepthIntra = reader.ReadUnsignedExpGolomb(); - uint maxHierarchyDepth = (uint)(this.CodingTreeBlockLog2 - this.MinTransformBlockLog2); - if (maxTransformHierarchyDepthInter > maxHierarchyDepth || maxTransformHierarchyDepthIntra > maxHierarchyDepth) - { - throw new InvalidImageContentException("The HEVC transform hierarchy depth is invalid."); - } - - this.MaxTransformHierarchyDepthInter = (int)maxTransformHierarchyDepthInter + 1; - this.MaxTransformHierarchyDepthIntra = (int)maxTransformHierarchyDepthIntra + 1; - - this.ScalingListEnabled = reader.ReadFlag(); - this.ScalingList = new HevcScalingList(); - if (this.ScalingListEnabled && reader.ReadFlag()) - { - this.ScalingList = HevcScalingList.Parse(ref reader); - } - - this.AsymmetricMotionPartitionsEnabled = reader.ReadFlag(); - this.SampleAdaptiveOffsetEnabled = reader.ReadFlag(); - this.PcmEnabled = reader.ReadFlag(); - if (this.PcmEnabled) - { - this.PcmBitDepthLuma = (int)reader.ReadBits(4) + 1; - this.PcmBitDepthChroma = (int)reader.ReadBits(4) + 1; - if (this.PcmBitDepthLuma > this.BitDepthLuma || this.PcmBitDepthChroma > this.BitDepthChroma) - { - throw new InvalidImageContentException("The HEVC PCM bit depth exceeds the coded sample precision."); - } - - uint minPcmCodingBlockLog2MinusThree = reader.ReadUnsignedExpGolomb(); - this.MinPcmCodingBlockLog2 = (int)minPcmCodingBlockLog2MinusThree + 3; - int maximumPcmCodingBlockLog2 = Math.Min(this.CodingTreeBlockLog2, 5); - if (this.MinPcmCodingBlockLog2 < Math.Min(this.MinCodingBlockLog2, 5) - || this.MinPcmCodingBlockLog2 > maximumPcmCodingBlockLog2) - { - throw new InvalidImageContentException("The HEVC minimum PCM coding-block size is invalid."); - } - - uint pcmCodingBlockSizeDifference = reader.ReadUnsignedExpGolomb(); - if (pcmCodingBlockSizeDifference > maximumPcmCodingBlockLog2 - this.MinPcmCodingBlockLog2) - { - throw new InvalidImageContentException("The HEVC maximum PCM coding-block size is invalid."); - } - - this.MaxPcmCodingBlockLog2 = this.MinPcmCodingBlockLog2 + (int)pcmCodingBlockSizeDifference; - this.PcmLoopFilterDisabled = reader.ReadFlag(); - } - - uint shortTermReferencePictureSetCount = reader.ReadUnsignedExpGolomb(); - if (shortTermReferencePictureSetCount > 64) - { - throw new InvalidImageContentException("The HEVC sequence parameter set declares too many short-term reference-picture sets."); - } - - List shortTermReferencePictureSets = new((int)shortTermReferencePictureSetCount); - for (int referenceSet = 0; referenceSet < shortTermReferencePictureSetCount; referenceSet++) - { - shortTermReferencePictureSets.Add( - HevcShortTermReferencePictureSet.Parse(ref reader, shortTermReferencePictureSets, referenceSet)); - } - - this.ShortTermReferencePictureSets = shortTermReferencePictureSets; - - if (reader.ReadFlag()) - { - uint longTermReferencePictureCount = reader.ReadUnsignedExpGolomb(); - if (longTermReferencePictureCount > 32) - { - throw new InvalidImageContentException("The HEVC sequence parameter set declares too many long-term reference pictures."); - } - - uint[] pictureOrderCounts = new uint[longTermReferencePictureCount]; - bool[] usedByCurrentPicture = new bool[longTermReferencePictureCount]; - for (int reference = 0; reference < pictureOrderCounts.Length; reference++) - { - pictureOrderCounts[reference] = reader.ReadBits(this.PictureOrderCountLsbBits); - usedByCurrentPicture[reference] = reader.ReadFlag(); - } - - this.LongTermReferencePictureOrderCounts = pictureOrderCounts; - this.LongTermReferencePicturesUsedByCurrent = usedByCurrentPicture; - } - else - { - this.LongTermReferencePictureOrderCounts = Array.Empty(); - this.LongTermReferencePicturesUsedByCurrent = Array.Empty(); - } - - this.TemporalMotionVectorPredictionEnabled = reader.ReadFlag(); - this.StrongIntraSmoothingEnabled = reader.ReadFlag(); - if (reader.ReadFlag()) - { - this.VideoUsabilityInformation = new HevcVideoUsabilityInformation( - ref reader, - this.ChromaFormat, - this.SeparateColorPlaneFlag, - maxSubLayersMinusOne); - } - - if (reader.ReadFlag()) - { - Span extensionFlags = stackalloc bool[8]; - for (int extensionFlag = 0; extensionFlag < extensionFlags.Length; extensionFlag++) - { - extensionFlags[extensionFlag] = reader.ReadFlag(); - } - - if (extensionFlags[1]) - { - throw new InvalidImageContentException("Layered HEVC sequence extensions are not supported for still-image items."); - } - - if (extensionFlags[0]) - { - this.TransformSkipRotationEnabled = reader.ReadFlag(); - this.TransformSkipContextEnabled = reader.ReadFlag(); - this.ImplicitResidualDpcmEnabled = reader.ReadFlag(); - this.ExplicitResidualDpcmEnabled = reader.ReadFlag(); - this.ExtendedPrecisionProcessingEnabled = reader.ReadFlag(); - this.IntraSmoothingDisabled = reader.ReadFlag(); - this.HighPrecisionOffsetsEnabled = reader.ReadFlag(); - this.PersistentRiceAdaptationEnabled = reader.ReadFlag(); - this.CabacBypassAlignmentEnabled = reader.ReadFlag(); - } - - bool unknownExtensionPresent = false; - for (int extensionFlag = 2; extensionFlag < extensionFlags.Length; extensionFlag++) - { - unknownExtensionPresent |= extensionFlags[extensionFlag]; - } - - if (unknownExtensionPresent) - { - while (reader.HasMoreRbspData()) - { - reader.ReadFlag(); - } - } - } - - reader.ReadRbspTrailingBits(); - } - - /// - /// Gets the referenced video-parameter-set identifier. - /// - public byte VideoParameterSetId { get; } - - /// - /// Gets the sequence-parameter-set identifier. - /// - public byte Id { get; } - - /// - /// Gets the declared number of temporal sublayers. - /// - public int MaxSubLayers { get; } - - /// - /// Gets a value indicating whether temporal identifiers are nested. - /// - public bool TemporalIdNestingFlag { get; } - - /// - /// Gets the general profile, tier, constraint, and level description. - /// - public HevcProfileTierLevel ProfileTierLevel { get; } - - /// - /// Gets the coded chroma format, from monochrome through YUV 4:4:4. - /// - public byte ChromaFormat { get; } - - /// - /// Gets a value indicating whether 4:4:4 components are coded as separate color planes. - /// - public bool SeparateColorPlaneFlag { get; } - - /// - /// Gets the coded luma width before conformance cropping. - /// - public int Width { get; } - - /// - /// Gets the coded luma height before conformance cropping. - /// - public int Height { get; } - - /// - /// Gets the displayed width after conformance cropping. - /// - public int DisplayWidth { get; } - - /// - /// Gets the displayed height after conformance cropping. - /// - public int DisplayHeight { get; } - - /// - /// Gets the conformance-window left offset in luma samples. - /// - public int ConformanceWindowLeftOffset { get; } - - /// - /// Gets the conformance-window right offset in luma samples. - /// - public int ConformanceWindowRightOffset { get; } - - /// - /// Gets the conformance-window top offset in luma samples. - /// - public int ConformanceWindowTopOffset { get; } - - /// - /// Gets the conformance-window bottom offset in luma samples. - /// - public int ConformanceWindowBottomOffset { get; } - - /// - /// Gets the luma sample precision in bits. - /// - public int BitDepthLuma { get; } - - /// - /// Gets the chroma sample precision in bits. - /// - public int BitDepthChroma { get; } - - /// - /// Gets the coded picture-order-count least-significant-bit width. - /// - public int PictureOrderCountLsbBits { get; } - - /// - /// Gets the base-two logarithm of the minimum luma coding-block size. - /// - public int MinCodingBlockLog2 { get; } - - /// - /// Gets the base-two logarithm of the coding-tree-block size. - /// - public int CodingTreeBlockLog2 { get; } - - /// - /// Gets the base-two logarithm of the minimum luma transform-block size. - /// - public int MinTransformBlockLog2 { get; } - - /// - /// Gets the base-two logarithm of the maximum luma transform-block size. - /// - public int MaxTransformBlockLog2 { get; } - - /// - /// Gets the maximum inter-predicted transform hierarchy depth. - /// - public int MaxTransformHierarchyDepthInter { get; } - - /// - /// Gets the maximum intra-predicted transform hierarchy depth. - /// - public int MaxTransformHierarchyDepthIntra { get; } - - /// - /// Gets a value indicating whether scaling lists affect inverse quantization. - /// - public bool ScalingListEnabled { get; } - - /// - /// Gets the effective quantization scaling matrices. - /// - public HevcScalingList ScalingList { get; } - - /// - /// Gets a value indicating whether asymmetric motion partitions are enabled. - /// - public bool AsymmetricMotionPartitionsEnabled { get; } - - /// - /// Gets a value indicating whether sample-adaptive offset filtering is enabled. - /// - public bool SampleAdaptiveOffsetEnabled { get; } - - /// - /// Gets a value indicating whether pulse-code-modulated coding blocks are enabled. - /// - public bool PcmEnabled { get; } - - /// - /// Gets the PCM luma sample precision in bits. - /// - public int PcmBitDepthLuma { get; } - - /// - /// Gets the PCM chroma sample precision in bits. - /// - public int PcmBitDepthChroma { get; } - - /// - /// Gets the base-two logarithm of the minimum PCM coding-block size. - /// - public int MinPcmCodingBlockLog2 { get; } - - /// - /// Gets the base-two logarithm of the maximum PCM coding-block size. - /// - public int MaxPcmCodingBlockLog2 { get; } - - /// - /// Gets a value indicating whether in-loop filtering is disabled for PCM blocks. - /// - public bool PcmLoopFilterDisabled { get; } - - /// - /// Gets the SPS short-term reference-picture sets. - /// - public IReadOnlyList ShortTermReferencePictureSets { get; } - - /// - /// Gets the long-term reference picture-order-count values. - /// - public IReadOnlyList LongTermReferencePictureOrderCounts { get; } - - /// - /// Gets the long-term reference-picture current-usage flags. - /// - public IReadOnlyList LongTermReferencePicturesUsedByCurrent { get; } - - /// - /// Gets a value indicating whether temporal motion-vector prediction is enabled. - /// - public bool TemporalMotionVectorPredictionEnabled { get; } - - /// - /// Gets a value indicating whether strong intra smoothing is enabled. - /// - public bool StrongIntraSmoothingEnabled { get; } - - /// - /// Gets the optional still-image VUI presentation description. - /// - public HevcVideoUsabilityInformation? VideoUsabilityInformation { get; } - - /// - /// Gets a value indicating whether transform-skip coefficient rotation is enabled. - /// - public bool TransformSkipRotationEnabled { get; } - - /// - /// Gets a value indicating whether transform-skip-specific entropy contexts are enabled. - /// - public bool TransformSkipContextEnabled { get; } - - /// - /// Gets a value indicating whether implicit residual DPCM is enabled. - /// - public bool ImplicitResidualDpcmEnabled { get; } - - /// - /// Gets a value indicating whether explicit residual DPCM is enabled. - /// - public bool ExplicitResidualDpcmEnabled { get; } - - /// - /// Gets a value indicating whether extended-precision processing is enabled. - /// - public bool ExtendedPrecisionProcessingEnabled { get; } - - /// - /// Gets a value indicating whether intra smoothing is disabled. - /// - public bool IntraSmoothingDisabled { get; } - - /// - /// Gets a value indicating whether high-precision prediction offsets are enabled. - /// - public bool HighPrecisionOffsetsEnabled { get; } - - /// - /// Gets a value indicating whether persistent Rice adaptation is enabled. - /// - public bool PersistentRiceAdaptationEnabled { get; } - - /// - /// Gets a value indicating whether CABAC bypass alignment is enabled. - /// - public bool CabacBypassAlignmentEnabled { get; } - - /// - /// Gets the base-two logarithm of the transform dynamic range for the specified reconstructed plane. - /// - /// The reconstructed plane. - /// The transform dynamic range excluding its sign bit. - public int GetMaxTransformDynamicRange(HevcPlane plane) - { - int bitDepth = plane == HevcPlane.Y ? this.BitDepthLuma : this.BitDepthChroma; - return this.ExtendedPrecisionProcessingEnabled ? Math.Max(15, bitDepth + 6) : 15; - } - - /// - /// Reads a conformance-window offset and converts it to luma-sample units. - /// - /// The sequence-parameter-set raw byte sequence payload reader. - /// The chroma-dependent luma-sample unit. - /// The scaled offset. - /// The scaled offset exceeds the supported image dimension range. - private static int ReadScaledOffset(ref HevcBitReader reader, int unit) - { - uint offset = reader.ReadUnsignedExpGolomb(); - if (offset > int.MaxValue / unit) - { - throw new InvalidImageContentException("The HEVC conformance-window offset is too large."); - } - - return (int)offset * unit; - } - - /// - /// Reads and validates a coded HEVC sample precision. - /// - /// The sequence-parameter-set raw byte sequence payload reader. - /// The sample precision in bits. - /// The declared precision exceeds 16 bits. - private static int ReadBitDepth(ref HevcBitReader reader) - { - uint bitDepthMinusEight = reader.ReadUnsignedExpGolomb(); - if (bitDepthMinusEight > 8) - { - throw new InvalidImageContentException("The HEVC sample bit depth is invalid."); - } - - return (int)bitDepthMinusEight + 8; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcShortTermReferencePictureSet.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcShortTermReferencePictureSet.cs deleted file mode 100644 index 93d53befe..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcShortTermReferencePictureSet.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the bounded picture-order differences declared by one HEVC short-term reference-picture set. -/// -internal sealed class HevcShortTermReferencePictureSet -{ - /// - /// Stores the bounded signed picture-order differences in HEVC reference order. - /// - private InlineArray16 deltaPictureOrders; - - /// - /// Stores the bounded current-picture usage flags corresponding to . - /// - private InlineArray16 usedByCurrentPicture; - - /// - /// Initializes a new instance of the class. - /// - private HevcShortTermReferencePictureSet() - { - } - - /// - /// Gets the number of pictures declared by the reference-picture set. - /// - public int Count { get; private set; } - - /// - /// Gets a signed picture-order difference in HEVC reference order. - /// - /// The zero-based reference-picture index. - /// The signed picture-order difference. - public int GetDeltaPictureOrder(int index) => this.deltaPictureOrders[index]; - - /// - /// Gets whether a reference picture is used by the current picture. - /// - /// The zero-based reference-picture index. - /// when the reference is used by the current picture. - public bool IsUsedByCurrentPicture(int index) => this.usedByCurrentPicture[index]; - - /// - /// Reads one SPS short-term reference-picture set. - /// - /// The sequence-parameter-set raw byte sequence payload reader. - /// The previously decoded sets available for inter-set prediction. - /// The zero-based index of the set being decoded. - /// The decoded reference-picture set. - /// The set exceeds the HEVC decoded-picture-buffer bound. - public static HevcShortTermReferencePictureSet Parse( - ref HevcBitReader reader, - IReadOnlyList previousSets, - int index) - { - HevcShortTermReferencePictureSet result = new(); - Span deltaPictureOrders = result.deltaPictureOrders; - Span usedByCurrentPicture = result.usedByCurrentPicture; - int pictureCount = 0; - bool interSetPrediction = index > 0 && reader.ReadFlag(); - if (interSetPrediction) - { - HevcShortTermReferencePictureSet referenceSet = previousSets[index - 1]; - bool deltaPictureOrderSign = reader.ReadFlag(); - uint absoluteDeltaPictureOrderMinusOne = reader.ReadUnsignedExpGolomb(); - if (absoluteDeltaPictureOrderMinusOne >= int.MaxValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - int deltaReferencePictureSet = (deltaPictureOrderSign ? -1 : 1) - * ((int)absoluteDeltaPictureOrderMinusOne + 1); - - for (int referenceIndex = 0; referenceIndex <= referenceSet.Count; referenceIndex++) - { - bool used = reader.ReadFlag(); - bool useDelta = used || reader.ReadFlag(); - if (!useDelta) - { - continue; - } - - if (pictureCount == deltaPictureOrders.Length) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture set is too large."); - } - - int referenceDelta = referenceIndex < referenceSet.Count - ? referenceSet.GetDeltaPictureOrder(referenceIndex) - : 0; - - long deltaPictureOrder = (long)deltaReferencePictureSet + referenceDelta; - if (deltaPictureOrder is < int.MinValue or > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - deltaPictureOrders[pictureCount] = (int)deltaPictureOrder; - usedByCurrentPicture[pictureCount] = used; - pictureCount++; - } - - // HEVC orders negative differences nearest-first, followed by positive differences nearest-first. - for (int outer = 1; outer < pictureCount; outer++) - { - int delta = deltaPictureOrders[outer]; - bool used = usedByCurrentPicture[outer]; - int inner = outer - 1; - while (inner >= 0 && delta < deltaPictureOrders[inner]) - { - deltaPictureOrders[inner + 1] = deltaPictureOrders[inner]; - usedByCurrentPicture[inner + 1] = usedByCurrentPicture[inner]; - inner--; - } - - deltaPictureOrders[inner + 1] = delta; - usedByCurrentPicture[inner + 1] = used; - } - - int negativeCount = 0; - while (negativeCount < pictureCount && deltaPictureOrders[negativeCount] < 0) - { - negativeCount++; - } - - deltaPictureOrders[..negativeCount].Reverse(); - usedByCurrentPicture[..negativeCount].Reverse(); - } - else - { - uint negativePictureCount = reader.ReadUnsignedExpGolomb(); - uint positivePictureCount = reader.ReadUnsignedExpGolomb(); - if (negativePictureCount > 16 || positivePictureCount > 16 - negativePictureCount) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture set is too large."); - } - - int previousDelta = 0; - for (uint negativeIndex = 0; negativeIndex < negativePictureCount; negativeIndex++) - { - uint deltaMinusOne = reader.ReadUnsignedExpGolomb(); - if (deltaMinusOne >= int.MaxValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - long deltaPictureOrder = (long)previousDelta - deltaMinusOne - 1; - if (deltaPictureOrder < int.MinValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - previousDelta = (int)deltaPictureOrder; - deltaPictureOrders[pictureCount] = previousDelta; - usedByCurrentPicture[pictureCount] = reader.ReadFlag(); - pictureCount++; - } - - previousDelta = 0; - for (uint positiveIndex = 0; positiveIndex < positivePictureCount; positiveIndex++) - { - uint deltaMinusOne = reader.ReadUnsignedExpGolomb(); - if (deltaMinusOne >= int.MaxValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - long deltaPictureOrder = (long)previousDelta + deltaMinusOne + 1; - if (deltaPictureOrder > int.MaxValue) - { - throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); - } - - previousDelta = (int)deltaPictureOrder; - deltaPictureOrders[pictureCount] = previousDelta; - usedByCurrentPicture[pictureCount] = reader.ReadFlag(); - pictureCount++; - } - } - - result.Count = pictureCount; - return result; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs deleted file mode 100644 index de4956705..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the decoded header and entropy-coded payload of one HEVC still-picture slice segment. -/// -internal sealed class HevcSliceSegmentHeader -{ - /// - /// The decoded-byte lengths preceding each tile or wavefront entropy entry point. - /// - private int[] entryPointOffsets = []; - - /// - /// Initializes a new instance of the class. - /// - /// The item-local instantaneous-decoder-refresh NAL unit. - /// The picture parameter sets available to the coded image item. - /// - /// The NAL unit is not a base-layer IDR slice, references unavailable parameters, or contains malformed - /// still-picture slice-header syntax. - /// - public HevcSliceSegmentHeader( - HevcNalUnit nalUnit, - IReadOnlyList pictureParameterSets) - { - if (!nalUnit.Header.IsInstantaneousDecoderRefresh - || nalUnit.Header.LayerId != 0 - || nalUnit.Header.TemporalId != 0) - { - throw new InvalidImageContentException("The HEVC image item contains a non-IDR or layered coded slice."); - } - - this.NalUnit = nalUnit; - HevcBitReader reader = new(nalUnit.Rbsp.Span); - this.FirstSliceSegmentInPicture = reader.ReadFlag(); - - // An IDR item has no earlier picture whose output can affect the returned still image. Consume the required - // random-access flag without retaining sequence-output state in the image decoder. - reader.ReadFlag(); - - uint pictureParameterSetId = reader.ReadUnsignedExpGolomb(); - if (pictureParameterSetId > 63) - { - throw new InvalidImageContentException("The HEVC slice segment has an invalid picture-parameter-set identifier."); - } - - HevcPictureParameterSet? pictureParameterSet = null; - foreach (HevcPictureParameterSet candidate in pictureParameterSets) - { - if (candidate.Id == pictureParameterSetId) - { - pictureParameterSet = candidate; - break; - } - } - - if (pictureParameterSet is null) - { - throw new InvalidImageContentException("The HEVC slice segment references an unavailable picture parameter set."); - } - - this.PictureParameterSet = pictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - if (pictureParameterSet.DependentSliceSegmentsEnabled && !this.FirstSliceSegmentInPicture) - { - this.DependentSliceSegment = reader.ReadFlag(); - } - - int codingTreeBlockColumns = HevcParameterSetSyntax.GetCodingTreeBlockCount( - sequenceParameterSet.Width, - sequenceParameterSet.CodingTreeBlockLog2); - - int codingTreeBlockRows = HevcParameterSetSyntax.GetCodingTreeBlockCount( - sequenceParameterSet.Height, - sequenceParameterSet.CodingTreeBlockLog2); - - int codingTreeBlockCount = codingTreeBlockColumns * codingTreeBlockRows; - if (!this.FirstSliceSegmentInPicture) - { - int addressBitCount = HevcParameterSetSyntax.GetCeilingLog2(codingTreeBlockCount); - uint address = reader.ReadBits(addressBitCount); - if (address >= codingTreeBlockCount) - { - throw new InvalidImageContentException("The HEVC slice segment address is outside the coded picture."); - } - - this.SliceSegmentAddress = (int)address; - } - - if (!this.DependentSliceSegment) - { - this.ReadIndependentHeader(ref reader); - } - - this.ReadEntryPoints(ref reader, codingTreeBlockCount); - if (pictureParameterSet.SliceSegmentHeaderExtensionPresent) - { - uint extensionLength = reader.ReadUnsignedExpGolomb(); - if (extensionLength > int.MaxValue || extensionLength > reader.BitsRemaining / 8) - { - throw new InvalidImageContentException("The HEVC slice-segment header extension is truncated."); - } - - for (int byteIndex = 0; byteIndex < extensionLength; byteIndex++) - { - reader.ReadBits(8); - } - } - - reader.ReadByteAlignment(); - this.HeaderLength = reader.BitPosition / 8; - this.SliceData = nalUnit.Rbsp[this.HeaderLength..]; - if (this.SliceData.IsEmpty) - { - throw new InvalidImageContentException("The HEVC slice segment contains no entropy-coded data."); - } - - int encodedHeaderLength = GetEncodedPayloadOffset( - this.HeaderLength, - nalUnit.EmulationPreventionBytePositions.Span); - - int availableEncodedData = nalUnit.EncodedPayloadLength - encodedHeaderLength; - int cumulativeEntryPointOffset = 0; - int previousDecodedBoundary = this.HeaderLength; - int[] entryPointOffsets = this.entryPointOffsets; - for (int index = 0; index < entryPointOffsets.Length; index++) - { - int entryPointOffset = entryPointOffsets[index]; - if (cumulativeEntryPointOffset > availableEncodedData - entryPointOffset) - { - throw new InvalidImageContentException("The HEVC slice entry point extends beyond its NAL unit."); - } - - cumulativeEntryPointOffset += entryPointOffset; - int encodedBoundary = encodedHeaderLength + cumulativeEntryPointOffset; - int decodedBoundary = GetDecodedPayloadOffset( - encodedBoundary, - nalUnit.EmulationPreventionBytePositions.Span); - - // entry_point_offset_minus1 counts encoded NAL bytes. The entropy decoder consumes the de-escaped RBSP, - // so each retained substream length must exclude prevention bytes from its own encoded interval. - entryPointOffsets[index] = decodedBoundary - previousDecodedBoundary; - previousDecodedBoundary = decodedBoundary; - } - } - - /// - /// Gets the complete decoded NAL unit containing this slice segment. - /// - public HevcNalUnit NalUnit { get; } - - /// - /// Gets a value indicating whether this is the first slice segment of the coded picture. - /// - public bool FirstSliceSegmentInPicture { get; } - - /// - /// Gets a value indicating whether this segment inherits syntax from an earlier independent slice. - /// - public bool DependentSliceSegment { get; } - - /// - /// Gets the picture parameters selected by this slice segment. - /// - public HevcPictureParameterSet PictureParameterSet { get; } - - /// - /// Gets the raster-scan address of the first coding-tree block in this slice segment. - /// - public int SliceSegmentAddress { get; } - - /// - /// Gets the independent slice prediction type, or for a dependent segment. - /// - public HevcSliceType? SliceType { get; private set; } - - /// - /// Gets the selected color-plane identifier for separate-plane 4:4:4 coding. - /// - public byte ColorPlaneId { get; private set; } - - /// - /// Gets a value indicating whether luma sample-adaptive offset filtering is enabled. - /// - public bool? SampleAdaptiveOffsetLumaEnabled { get; private set; } - - /// - /// Gets a value indicating whether chroma sample-adaptive offset filtering is enabled. - /// - public bool? SampleAdaptiveOffsetChromaEnabled { get; private set; } - - /// - /// Gets the effective luma quantization parameter, or for a dependent segment. - /// - public int? QuantizationParameter { get; private set; } - - /// - /// Gets the slice-level Cb quantization-parameter offset. - /// - public int ChromaCbQuantizationParameterOffset { get; private set; } - - /// - /// Gets the slice-level Cr quantization-parameter offset. - /// - public int ChromaCrQuantizationParameterOffset { get; private set; } - - /// - /// Gets a value indicating whether coding units can select the PPS chroma-offset list. - /// - public bool? ChromaQuantizationParameterOffsetListEnabled { get; private set; } - - /// - /// Gets a value indicating whether deblocking is disabled for this independent slice. - /// - public bool? DeblockingFilterDisabled { get; private set; } - - /// - /// Gets half the effective deblocking beta-threshold offset. - /// - public int DeblockingFilterBetaOffsetDiv2 { get; private set; } - - /// - /// Gets half the effective deblocking clipping-threshold offset. - /// - public int DeblockingFilterTcOffsetDiv2 { get; private set; } - - /// - /// Gets a value indicating whether in-loop filtering crosses slice boundaries. - /// - public bool? LoopFilterAcrossSlicesEnabled { get; private set; } - - /// - /// Gets the decoded-byte lengths that separate tile or wavefront entropy substreams after the first substream. - /// - public IReadOnlyList EntryPointOffsets => this.entryPointOffsets; - - /// - /// Gets the number of independently initialized tile or wavefront entropy substreams in this slice segment. - /// - public int EntropySubstreamCount => this.entryPointOffsets.Length + 1; - - /// - /// Gets the slice-header length in decoded raw-byte-sequence payload bytes. - /// - public int HeaderLength { get; } - - /// - /// Gets the entropy-coded slice data following byte alignment. - /// - public ReadOnlyMemory SliceData { get; } - - /// - /// Gets one bounded entropy substream in slice coding order. - /// - /// The zero-based entropy-substream index. - /// The decoded raw-byte-sequence payload bytes belonging to the selected substream. - public ReadOnlyMemory GetEntropySubstream(int index) - { - DebugGuard.MustBeBetweenOrEqualTo(index, 0, this.entryPointOffsets.Length, nameof(index)); - int offset = 0; - for (int precedingIndex = 0; precedingIndex < index; precedingIndex++) - { - offset += this.entryPointOffsets[precedingIndex]; - } - - int length = index < this.entryPointOffsets.Length - ? this.entryPointOffsets[index] - : this.SliceData.Length - offset; - - return this.SliceData.Slice(offset, length); - } - - /// - /// Reads fields carried only by an independent slice-segment header. - /// - /// The slice-segment raw byte sequence payload reader. - /// - /// The slice is not intra-coded or its quantization and filter fields are outside the governing parameter bounds. - /// - private void ReadIndependentHeader(ref HevcBitReader reader) - { - HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - for (int extraBit = 0; extraBit < pictureParameterSet.ExtraSliceHeaderBitCount; extraBit++) - { - reader.ReadFlag(); - } - - uint sliceType = reader.ReadUnsignedExpGolomb(); - if (sliceType != (uint)HevcSliceType.Intra) - { - throw new InvalidImageContentException("An independently coded HEVC image item must contain intra IDR slices."); - } - - this.SliceType = HevcSliceType.Intra; - if (pictureParameterSet.OutputFlagPresent && !reader.ReadFlag()) - { - throw new InvalidImageContentException("The HEVC image-item slice is marked as unavailable for output."); - } - - if (sequenceParameterSet.SeparateColorPlaneFlag) - { - this.ColorPlaneId = (byte)reader.ReadBits(2); - if (this.ColorPlaneId > 2) - { - throw new InvalidImageContentException("The HEVC slice segment has an invalid separate color-plane identifier."); - } - } - - bool hasCombinedChromaPlanes = sequenceParameterSet.ChromaFormat != 0 - && !sequenceParameterSet.SeparateColorPlaneFlag; - - if (sequenceParameterSet.SampleAdaptiveOffsetEnabled) - { - this.SampleAdaptiveOffsetLumaEnabled = reader.ReadFlag(); - this.SampleAdaptiveOffsetChromaEnabled = hasCombinedChromaPlanes && reader.ReadFlag(); - } - - int sliceQuantizationParameterDelta = reader.ReadSignedExpGolomb(); - long quantizationParameter = 26L - + pictureParameterSet.InitialQuantizationParameterMinus26 - + sliceQuantizationParameterDelta; - - int minimumQuantizationParameter = -6 * (sequenceParameterSet.BitDepthLuma - 8); - if (quantizationParameter < minimumQuantizationParameter || quantizationParameter > 51) - { - throw new InvalidImageContentException("The HEVC slice segment has an invalid luma quantization parameter."); - } - - this.QuantizationParameter = (int)quantizationParameter; - if (pictureParameterSet.SliceChromaQuantizationParameterOffsetsPresent && hasCombinedChromaPlanes) - { - this.ChromaCbQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - this.ChromaCrQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); - if (pictureParameterSet.ChromaCbQuantizationParameterOffset + this.ChromaCbQuantizationParameterOffset is < -12 or > 12 - || pictureParameterSet.ChromaCrQuantizationParameterOffset + this.ChromaCrQuantizationParameterOffset is < -12 or > 12) - { - throw new InvalidImageContentException("The HEVC slice and picture chroma quantization offsets have an invalid sum."); - } - } - - if (pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count != 0) - { - this.ChromaQuantizationParameterOffsetListEnabled = reader.ReadFlag(); - } - - this.ReadDeblockingFilterFields(ref reader); - bool sampleAdaptiveOffsetEnabled = this.SampleAdaptiveOffsetLumaEnabled == true - || this.SampleAdaptiveOffsetChromaEnabled == true; - - if (pictureParameterSet.LoopFilterAcrossSlicesEnabled - && (sampleAdaptiveOffsetEnabled || this.DeblockingFilterDisabled == false)) - { - this.LoopFilterAcrossSlicesEnabled = reader.ReadFlag(); - } - else - { - this.LoopFilterAcrossSlicesEnabled = pictureParameterSet.LoopFilterAcrossSlicesEnabled; - } - } - - /// - /// Resolves the independent slice's effective deblocking mode and threshold offsets. - /// - /// The slice-segment raw byte sequence payload reader. - private void ReadDeblockingFilterFields(ref HevcBitReader reader) - { - HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; - bool overrideFilter = false; - if (pictureParameterSet.DeblockingFilterControlPresent - && pictureParameterSet.DeblockingFilterOverrideEnabled) - { - overrideFilter = reader.ReadFlag(); - } - - if (overrideFilter) - { - this.DeblockingFilterDisabled = reader.ReadFlag(); - if (this.DeblockingFilterDisabled == false) - { - this.DeblockingFilterBetaOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); - this.DeblockingFilterTcOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); - } - - return; - } - - this.DeblockingFilterDisabled = pictureParameterSet.DeblockingFilterControlPresent - && pictureParameterSet.DeblockingFilterDisabled; - - this.DeblockingFilterBetaOffsetDiv2 = pictureParameterSet.DeblockingFilterBetaOffsetDiv2; - this.DeblockingFilterTcOffsetDiv2 = pictureParameterSet.DeblockingFilterTcOffsetDiv2; - } - - /// - /// Reads tile or wavefront substream entry-point byte lengths. - /// - /// The slice-segment raw byte sequence payload reader. - /// The number of coding-tree blocks in the coded picture. - /// - /// The entry-point count, field width, or byte length exceeds the bounded picture or integer range. - /// - private void ReadEntryPoints(ref HevcBitReader reader, int codingTreeBlockCount) - { - HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; - if (!pictureParameterSet.TilesEnabled && !pictureParameterSet.EntropyCodingSynchronizationEnabled) - { - return; - } - - uint entryPointCount = reader.ReadUnsignedExpGolomb(); - if (entryPointCount >= codingTreeBlockCount) - { - throw new InvalidImageContentException("The HEVC slice segment declares too many entropy entry points."); - } - - if (entryPointCount == 0) - { - return; - } - - uint offsetLengthMinusOne = reader.ReadUnsignedExpGolomb(); - if (offsetLengthMinusOne > 31) - { - throw new InvalidImageContentException("The HEVC slice entry-point offset width is invalid."); - } - - int offsetBitCount = (int)offsetLengthMinusOne + 1; - int[] entryPointOffsets = new int[entryPointCount]; - for (int entryPoint = 0; entryPoint < entryPointOffsets.Length; entryPoint++) - { - uint entryPointOffsetMinusOne = reader.ReadBits(offsetBitCount); - if (entryPointOffsetMinusOne >= int.MaxValue) - { - throw new InvalidImageContentException("The HEVC slice entry-point byte length is too large."); - } - - entryPointOffsets[entryPoint] = (int)entryPointOffsetMinusOne + 1; - } - - this.entryPointOffsets = entryPointOffsets; - } - - /// - /// Converts an RBSP byte boundary to its corresponding encoded-payload boundary. - /// - /// The decoded raw-byte-sequence payload offset. - /// The removed encoded-payload byte positions. - /// The encoded byte-sequence payload offset at the same syntax boundary. - public static int GetEncodedPayloadOffset( - int rbspOffset, - ReadOnlySpan emulationPreventionBytePositions) - { - int encodedOffset = rbspOffset; - foreach (int preventionBytePosition in emulationPreventionBytePositions) - { - if (preventionBytePosition >= encodedOffset) - { - break; - } - - encodedOffset++; - } - - return encodedOffset; - } - - /// - /// Converts an encoded-payload byte boundary to its corresponding RBSP boundary. - /// - /// The encoded byte-sequence payload offset. - /// The removed encoded-payload byte positions. - /// The decoded raw-byte-sequence payload offset at the same syntax boundary. - public static int GetDecodedPayloadOffset( - int encodedOffset, - ReadOnlySpan emulationPreventionBytePositions) - { - int decodedOffset = encodedOffset; - foreach (int preventionBytePosition in emulationPreventionBytePositions) - { - if (preventionBytePosition >= encodedOffset) - { - break; - } - - decodedOffset--; - } - - return decodedOffset; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceType.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSliceType.cs deleted file mode 100644 index 1ccbe2187..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceType.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Identifies the prediction structure signaled for an HEVC slice segment. -/// -internal enum HevcSliceType -{ - /// - /// The slice can use intra and bidirectional inter prediction. - /// - Bidirectional = 0, - - /// - /// The slice can use intra and forward inter prediction. - /// - Predictive = 1, - - /// - /// The slice uses only intra-picture prediction. - /// - Intra = 2 -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSupplementalEnhancementInformation.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSupplementalEnhancementInformation.cs deleted file mode 100644 index 7bb89218e..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSupplementalEnhancementInformation.cs +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorProfiles; - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Reads the presentation and exposed metadata carried by prefix SEI NAL units for one bounded still picture. -/// -internal sealed class HevcSupplementalEnhancementInformation -{ - private const int DisplayOrientationPayloadType = 47; - private const int MasteringDisplayColorVolumePayloadType = 137; - private const int NoDisplayPayloadType = 135; - private const int ContentLightLevelPayloadType = 144; - private const int AlternativeTransferCharacteristicsPayloadType = 147; - private const int AmbientViewingEnvironmentPayloadType = 148; - private const int ContentColorVolumePayloadType = 149; - - /// - /// Gets a value indicating whether the selected still picture is marked as unavailable for display. - /// - public bool NoDisplay { get; private set; } - - /// - /// Gets a value indicating whether an active display-orientation message is present. - /// - public bool HasDisplayOrientation { get; private set; } - - /// - /// Gets a value indicating whether the cropped decoded picture is flipped horizontally before rotation. - /// - public bool HorizontalFlip { get; private set; } - - /// - /// Gets a value indicating whether the cropped decoded picture is flipped vertically before rotation. - /// - public bool VerticalFlip { get; private set; } - - /// - /// Gets the unsigned fraction of one complete anticlockwise turn applied after flipping. - /// - public ushort AnticlockwiseRotation { get; private set; } - - /// - /// Gets the preferred CICP transfer-characteristics code, when signaled. - /// - public byte? PreferredTransferCharacteristics { get; private set; } - - /// - /// Gets the content light-level description, when signaled. - /// - public HeifContentLightLevel? ContentLightLevel { get; private set; } - - /// - /// Gets the mastering-display color volume, when signaled. - /// - public HeifMasteringDisplayColorVolume? MasteringDisplayColorVolume { get; private set; } - - /// - /// Gets the content color volume, when signaled and not cancelled. - /// - public HeifContentColorVolume? ContentColorVolume { get; private set; } - - /// - /// Gets the ambient viewing environment, when signaled. - /// - public HeifAmbientViewingEnvironment? AmbientViewingEnvironment { get; private set; } - - /// - /// Reads every byte-aligned message from one prefix SEI RBSP in bitstream order. - /// - /// The decoded NAL payload, including its RBSP trailing byte. - public void ReadPrefixNalUnit(ReadOnlySpan rbsp) - { - if (rbsp.IsEmpty) - { - throw new InvalidImageContentException("The HEVC prefix SEI NAL unit is missing RBSP trailing bits."); - } - - int offset = 0; - while (rbsp.Length - offset > 1) - { - int payloadType = ReadExtendedValue(rbsp, ref offset, "payload type"); - int payloadSize = ReadExtendedValue(rbsp, ref offset, "payload size"); - if (payloadSize > rbsp.Length - offset) - { - throw new InvalidImageContentException("The HEVC prefix SEI message payload is truncated."); - } - - ReadOnlySpan payload = rbsp.Slice(offset, payloadSize); - offset += payloadSize; - switch (payloadType) - { - case DisplayOrientationPayloadType: - this.ReadDisplayOrientation(payload); - break; - case NoDisplayPayloadType: - this.ReadNoDisplay(payload); - break; - case MasteringDisplayColorVolumePayloadType: - this.ReadMasteringDisplayColorVolume(payload); - break; - case ContentLightLevelPayloadType: - this.ReadContentLightLevel(payload); - break; - case AlternativeTransferCharacteristicsPayloadType: - this.ReadAlternativeTransferCharacteristics(payload); - break; - case AmbientViewingEnvironmentPayloadType: - this.ReadAmbientViewingEnvironment(payload); - break; - case ContentColorVolumePayloadType: - this.ReadContentColorVolume(payload); - break; - } - } - - if (offset != rbsp.Length - 1 || rbsp[offset] != 0x80) - { - throw new InvalidImageContentException("The HEVC prefix SEI NAL unit has invalid RBSP trailing bits."); - } - } - - /// - /// Reads the legacy HEVC display-orientation payload retained by pinned HM. - /// - private void ReadDisplayOrientation(ReadOnlySpan payload) - { - HevcBitReader reader = new(payload); - bool cancel = reader.ReadFlag(); - if (cancel) - { - this.HasDisplayOrientation = false; - this.HorizontalFlip = false; - this.VerticalFlip = false; - this.AnticlockwiseRotation = 0; - ValidatePayloadExtension(ref reader, "display orientation"); - return; - } - - this.HorizontalFlip = reader.ReadFlag(); - this.VerticalFlip = reader.ReadFlag(); - this.AnticlockwiseRotation = (ushort)reader.ReadBits(16); - _ = reader.ReadFlag(); - ValidatePayloadExtension(ref reader, "display orientation"); - this.HasDisplayOrientation = true; - } - - /// - /// Records that the selected picture is not intended for display. - /// - private void ReadNoDisplay(ReadOnlySpan payload) - { - // Pinned HM writes no syntax bits for this message, producing a zero-byte payload. A nonempty payload can - // contain only the generic reserved extension and payload-alignment marker handled by the shared validator. - ValidateByteAlignedPayloadExtension(payload, "no-display"); - this.NoDisplay = true; - } - - /// - /// Reads mastering-display metadata in its HEVC fixed-point representation. - /// - private void ReadMasteringDisplayColorVolume(ReadOnlySpan payload) - { - const int syntaxLength = 24; - if (payload.Length < syntaxLength) - { - throw new InvalidImageContentException("The HEVC mastering-display color-volume SEI payload is truncated."); - } - - this.MasteringDisplayColorVolume = HeifPropertyParser.ParseMasteringDisplayColorVolume(payload[..syntaxLength]); - ValidateByteAlignedPayloadExtension(payload[syntaxLength..], "mastering-display color-volume"); - } - - /// - /// Reads content light-level metadata in its HEVC fixed-width representation. - /// - private void ReadContentLightLevel(ReadOnlySpan payload) - { - const int syntaxLength = 4; - if (payload.Length < syntaxLength) - { - throw new InvalidImageContentException("The HEVC content light-level SEI payload is truncated."); - } - - this.ContentLightLevel = HeifPropertyParser.ParseContentLightLevel(payload[..syntaxLength]); - ValidateByteAlignedPayloadExtension(payload[syntaxLength..], "content light-level"); - } - - /// - /// Reads the preferred transfer function applied when the container does not provide one. - /// - private void ReadAlternativeTransferCharacteristics(ReadOnlySpan payload) - { - if (payload.IsEmpty) - { - throw new InvalidImageContentException("The HEVC alternative-transfer-characteristics SEI payload is truncated."); - } - - this.PreferredTransferCharacteristics = payload[0]; - ValidateByteAlignedPayloadExtension(payload[1..], "alternative transfer characteristics"); - } - - /// - /// Reads the nominal ambient viewing environment. - /// - private void ReadAmbientViewingEnvironment(ReadOnlySpan payload) - { - const int syntaxLength = 8; - if (payload.Length < syntaxLength) - { - throw new InvalidImageContentException("The HEVC ambient-viewing-environment SEI payload is truncated."); - } - - this.AmbientViewingEnvironment = HeifPropertyParser.ParseAmbientViewingEnvironment(payload[..syntaxLength]); - ValidateByteAlignedPayloadExtension(payload[syntaxLength..], "ambient viewing environment"); - } - - /// - /// Reads the bit-packed content color-volume syntax and applies cancellation in message order. - /// - private void ReadContentColorVolume(ReadOnlySpan payload) - { - HevcBitReader reader = new(payload); - bool cancel = reader.ReadFlag(); - if (cancel) - { - this.ContentColorVolume = null; - ValidatePayloadExtension(ref reader, "content color-volume"); - return; - } - - _ = reader.ReadFlag(); - bool primariesPresent = reader.ReadFlag(); - bool minimumLuminancePresent = reader.ReadFlag(); - bool maximumLuminancePresent = reader.ReadFlag(); - bool averageLuminancePresent = reader.ReadFlag(); - RgbPrimariesChromaticityCoordinates? primaries = null; - if (primariesPresent) - { - int greenX = unchecked((int)reader.ReadBits(32)); - int greenY = unchecked((int)reader.ReadBits(32)); - int blueX = unchecked((int)reader.ReadBits(32)); - int blueY = unchecked((int)reader.ReadBits(32)); - int redX = unchecked((int)reader.ReadBits(32)); - int redY = unchecked((int)reader.ReadBits(32)); - const int maximumChromaticityValue = 5_000_000; - if (greenX is < -maximumChromaticityValue or > maximumChromaticityValue - || greenY is < -maximumChromaticityValue or > maximumChromaticityValue - || blueX is < -maximumChromaticityValue or > maximumChromaticityValue - || blueY is < -maximumChromaticityValue or > maximumChromaticityValue - || redX is < -maximumChromaticityValue or > maximumChromaticityValue - || redY is < -maximumChromaticityValue or > maximumChromaticityValue) - { - throw new InvalidImageContentException("The HEVC content color-volume SEI payload has an out-of-range primary coordinate."); - } - - const float chromaticityScale = 1F / 50000F; - - // H.274 stores signed primary coordinates in G, B, R order. Reorder them once at the codec boundary so - // the retained value has the same observable RGB coordinate contract as the equivalent item property. - primaries = new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(redX * chromaticityScale, redY * chromaticityScale), - new CieXyChromaticityCoordinates(greenX * chromaticityScale, greenY * chromaticityScale), - new CieXyChromaticityCoordinates(blueX * chromaticityScale, blueY * chromaticityScale)); - } - - uint? minimumLuminance = minimumLuminancePresent ? reader.ReadBits(32) : null; - uint? maximumLuminance = maximumLuminancePresent ? reader.ReadBits(32) : null; - uint? averageLuminance = averageLuminancePresent ? reader.ReadBits(32) : null; - if ((minimumLuminance is not null && averageLuminance is not null && minimumLuminance.Value > averageLuminance.Value) - || (averageLuminance is not null && maximumLuminance is not null && averageLuminance.Value > maximumLuminance.Value) - || (minimumLuminance is not null && maximumLuminance is not null && minimumLuminance.Value > maximumLuminance.Value)) - { - throw new InvalidImageContentException("The HEVC content color-volume SEI luminance values are not in ascending order."); - } - - ValidatePayloadExtension(ref reader, "content color-volume"); - const double luminanceScale = 1D / 10000000D; - - this.ContentColorVolume = new HeifContentColorVolume( - primaries, - minimumLuminance * luminanceScale, - maximumLuminance * luminanceScale, - averageLuminance * luminanceScale); - } - - /// - /// Reads an extended SEI payload type or size whose continuation bytes are all 255. - /// - private static int ReadExtendedValue(ReadOnlySpan data, ref int offset, string valueName) - { - int value = 0; - while (true) - { - if ((uint)offset >= (uint)data.Length) - { - throw new InvalidImageContentException($"The HEVC prefix SEI {valueName} is truncated."); - } - - int current = data[offset++]; - if (value > int.MaxValue - current) - { - throw new InvalidImageContentException($"The HEVC prefix SEI {valueName} is too large."); - } - - value += current; - if (current != byte.MaxValue) - { - return value; - } - } - } - - /// - /// Validates an optional extension following fixed byte-aligned SEI syntax. - /// - private static void ValidateByteAlignedPayloadExtension(ReadOnlySpan extension, string payloadName) - { - if (extension.IsEmpty) - { - return; - } - - HevcBitReader reader = new(extension); - ValidatePayloadExtension(ref reader, payloadName); - } - - /// - /// Validates reserved payload-extension data followed by its final one bit and zero padding. - /// - private static void ValidatePayloadExtension(ref HevcBitReader reader, string payloadName) - { - bool foundMarker = false; - while (reader.BitsRemaining > 0) - { - foundMarker |= reader.ReadFlag(); - } - - // The final set bit is payload_bit_equal_to_one; any preceding bits are the reserved extension data that - // pinned HM deliberately skips. An all-zero remainder has no marker and is therefore not a complete payload. - if (!foundMarker) - { - throw new InvalidImageContentException($"The HEVC {payloadName} SEI payload has invalid trailing bits."); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcTileLayout.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcTileLayout.cs deleted file mode 100644 index 83bd366c7..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcTileLayout.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Maps HEVC coding-tree blocks between picture raster order and tile-scan order. -/// -internal readonly struct HevcTileLayout -{ - /// - /// The tile widths in coding-tree blocks. - /// - private readonly IReadOnlyList columnWidths; - - /// - /// The tile heights in coding-tree blocks. - /// - private readonly IReadOnlyList rowHeights; - - /// - /// Initializes a new instance of the struct. - /// - /// The picture tile geometry. - public HevcTileLayout(HevcPictureParameterSet pictureParameterSet) - : this(pictureParameterSet.TileColumnWidths, pictureParameterSet.TileRowHeights) - { - } - - /// - /// Initializes a new instance of the struct from validated tile dimensions. - /// - /// The tile-column widths in coding-tree blocks. - /// The tile-row heights in coding-tree blocks. - public HevcTileLayout(IReadOnlyList columnWidths, IReadOnlyList rowHeights) - { - this.columnWidths = columnWidths; - this.rowHeights = rowHeights; - this.ColumnCount = this.columnWidths.Count; - this.RowCount = this.rowHeights.Count; - this.Width = Sum(this.columnWidths); - this.Height = Sum(this.rowHeights); - } - - /// - /// Gets the number of tile columns. - /// - public int ColumnCount { get; } - - /// - /// Gets the number of tile rows. - /// - public int RowCount { get; } - - /// - /// Gets the picture width in coding-tree blocks. - /// - public int Width { get; } - - /// - /// Gets the picture height in coding-tree blocks. - /// - public int Height { get; } - - /// - /// Gets the number of tiles in the picture. - /// - public int TileCount => this.ColumnCount * this.RowCount; - - /// - /// Converts a picture raster-scan address to tile-scan order. - /// - /// The raster-scan coding-tree-block address. - /// The corresponding tile-scan address. - public int GetTileScanAddress(int rasterAddress) - { - int x = rasterAddress % this.Width; - int y = rasterAddress / this.Width; - this.FindTile(x, y, out int tileColumn, out int tileRow, out int tileStartX, out int tileStartY); - int address = 0; - for (int row = 0; row < tileRow; row++) - { - address += this.rowHeights[row] * this.Width; - } - - for (int column = 0; column < tileColumn; column++) - { - address += this.columnWidths[column] * this.rowHeights[tileRow]; - } - - return address + ((y - tileStartY) * this.columnWidths[tileColumn]) + x - tileStartX; - } - - /// - /// Converts a tile-scan coding-tree-block address to picture raster order. - /// - /// The tile-scan address. - /// The corresponding raster-scan address. - public int GetRasterAddress(int tileScanAddress) - { - int remaining = tileScanAddress; - int tileStartY = 0; - for (int tileRow = 0; tileRow < this.RowCount; tileRow++) - { - int tileStartX = 0; - for (int tileColumn = 0; tileColumn < this.ColumnCount; tileColumn++) - { - int tileWidth = this.columnWidths[tileColumn]; - int tileHeight = this.rowHeights[tileRow]; - int tileArea = tileWidth * tileHeight; - if (remaining < tileArea) - { - int x = tileStartX + (remaining % tileWidth); - int y = tileStartY + (remaining / tileWidth); - return (y * this.Width) + x; - } - - remaining -= tileArea; - tileStartX += tileWidth; - } - - tileStartY += this.rowHeights[tileRow]; - } - - return this.Width * this.Height; - } - - /// - /// Gets the tile and tile-local position of one raster-scan coding-tree block. - /// - /// The raster-scan address. - /// The zero-based tile index. - /// The horizontal coding-tree-block offset within the tile. - /// The vertical coding-tree-block offset within the tile. - /// The tile width in coding-tree blocks. - /// The tile height in coding-tree blocks. - public void GetTilePosition( - int rasterAddress, - out int tileIndex, - out int columnInTile, - out int rowInTile, - out int tileWidth, - out int tileHeight) - { - int x = rasterAddress % this.Width; - int y = rasterAddress / this.Width; - this.FindTile(x, y, out int tileColumn, out int tileRow, out int tileStartX, out int tileStartY); - tileIndex = (tileRow * this.ColumnCount) + tileColumn; - columnInTile = x - tileStartX; - rowInTile = y - tileStartY; - tileWidth = this.columnWidths[tileColumn]; - tileHeight = this.rowHeights[tileRow]; - } - - /// - /// Locates the tile containing one coding-tree-block coordinate. - /// - /// The raster coding-tree-block X coordinate. - /// The raster coding-tree-block Y coordinate. - /// The containing tile column. - /// The containing tile row. - /// The containing tile's left coding-tree-block coordinate. - /// The containing tile's top coding-tree-block coordinate. - private void FindTile(int x, int y, out int tileColumn, out int tileRow, out int tileStartX, out int tileStartY) - { - tileStartX = 0; - tileColumn = 0; - while (x >= tileStartX + this.columnWidths[tileColumn]) - { - tileStartX += this.columnWidths[tileColumn++]; - } - - tileStartY = 0; - tileRow = 0; - while (y >= tileStartY + this.rowHeights[tileRow]) - { - tileStartY += this.rowHeights[tileRow++]; - } - } - - /// - /// Sums one complete tile dimension. - /// - /// The tile widths or heights. - /// The complete picture dimension in coding-tree blocks. - private static int Sum(IReadOnlyList values) - { - int sum = 0; - foreach (int value in values) - { - sum += value; - } - - return sum; - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcTransformComponentGeometry.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcTransformComponentGeometry.cs deleted file mode 100644 index 1725946c1..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcTransformComponentGeometry.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Describes one component rectangle within an HEVC transform-tree node. -/// -internal readonly struct HevcTransformComponentGeometry -{ - /// - /// Initializes a new instance of the struct. - /// - /// The component rectangle left coordinate. - /// The component rectangle top coordinate. - /// The component rectangle width. - /// The component rectangle height. - /// Whether this transform-tree section owns the component rectangle. - /// Whether every child section owns a distinct component rectangle. - public HevcTransformComponentGeometry(int x, int y, int width, int height, bool process, bool processesAllQuadrants) - { - this.X = x; - this.Y = y; - this.Width = width; - this.Height = height; - this.Process = process; - this.ProcessesAllQuadrants = processesAllQuadrants; - } - - /// - /// Gets the component rectangle left coordinate. - /// - public int X { get; } - - /// - /// Gets the component rectangle top coordinate. - /// - public int Y { get; } - - /// - /// Gets the component rectangle width. - /// - public int Width { get; } - - /// - /// Gets the component rectangle height. - /// - public int Height { get; } - - /// - /// Gets a value indicating whether this transform-tree section owns the component rectangle. - /// - public bool Process { get; } - - /// - /// Gets a value indicating whether each child section owns a distinct component rectangle. - /// - public bool ProcessesAllQuadrants { get; } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcTransformUnitGeometry.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcTransformUnitGeometry.cs deleted file mode 100644 index 82b990304..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcTransformUnitGeometry.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Maps one luma transform-tree node to its primary and subsampled component rectangles. -/// -internal readonly struct HevcTransformUnitGeometry -{ - /// - /// Initializes a new instance of the struct. - /// - /// The base-two logarithm of the luma transform-node side. - /// The primary plane coded with luma syntax. - /// The primary component rectangle. - /// The blue-difference chroma rectangle. - /// The red-difference chroma rectangle. - /// Whether chroma syntax accompanies the primary luma syntax. - private HevcTransformUnitGeometry( - int log2LumaSize, - HevcPlane primaryPlane, - HevcTransformComponentGeometry primary, - HevcTransformComponentGeometry chromaBlue, - HevcTransformComponentGeometry chromaRed, - bool hasCombinedChroma) - { - this.Log2LumaSize = log2LumaSize; - this.PrimaryPlane = primaryPlane; - this.Primary = primary; - this.ChromaBlue = chromaBlue; - this.ChromaRed = chromaRed; - this.HasCombinedChroma = hasCombinedChroma; - } - - /// - /// Gets the base-two logarithm of the luma transform-node side. - /// - public int Log2LumaSize { get; } - - /// - /// Gets the plane coded with luma transform syntax. - /// - public HevcPlane PrimaryPlane { get; } - - /// - /// Gets the primary component rectangle. - /// - public HevcTransformComponentGeometry Primary { get; } - - /// - /// Gets the blue-difference chroma rectangle. - /// - public HevcTransformComponentGeometry ChromaBlue { get; } - - /// - /// Gets the red-difference chroma rectangle. - /// - public HevcTransformComponentGeometry ChromaRed { get; } - - /// - /// Gets a value indicating whether combined chroma syntax accompanies the primary luma syntax. - /// - public bool HasCombinedChroma { get; } - - /// - /// Creates the root component geometry for one coding unit. - /// - /// The coding-unit left luma coordinate. - /// The coding-unit top luma coordinate. - /// The base-two logarithm of the coding-unit side. - /// The sequence chroma-format identifier. - /// Whether each 4:4:4 component is coded as an independent color plane. - /// The selected separate-color plane, or zero for combined coding. - /// The root transform-unit geometry. - public static HevcTransformUnitGeometry CreateRoot( - int x, - int y, - int log2Size, - byte chromaFormat, - bool separateColorPlane, - int colorPlaneIndex) - { - int size = 1 << log2Size; - HevcPlane primaryPlane = separateColorPlane ? (HevcPlane)colorPlaneIndex : HevcPlane.Y; - HevcTransformComponentGeometry primary = new(x, y, size, size, true, true); - if (chromaFormat == 0 || separateColorPlane) - { - return new HevcTransformUnitGeometry(log2Size, primaryPlane, primary, default, default, false); - } - - int subsamplingX = chromaFormat is 1 or 2 ? 1 : 0; - int subsamplingY = chromaFormat == 1 ? 1 : 0; - HevcTransformComponentGeometry chroma = new( - x >> subsamplingX, - y >> subsamplingY, - size >> subsamplingX, - size >> subsamplingY, - true, - true); - - return new HevcTransformUnitGeometry(log2Size, primaryPlane, primary, chroma, chroma, true); - } - - /// - /// Creates one of the four Z-ordered child transform nodes. - /// - /// The child section from zero through three. - /// The selected child geometry. - public HevcTransformUnitGeometry CreateChild(int section) - => new( - this.Log2LumaSize - 1, - this.PrimaryPlane, - SplitComponent(this.Primary, section), - SplitComponent(this.ChromaBlue, section), - SplitComponent(this.ChromaRed, section), - this.HasCombinedChroma); - - /// - /// Splits one component rectangle while retaining sub-minimum chroma at the owning parent level. - /// - /// The parent component rectangle. - /// The luma child section from zero through three. - /// The component rectangle visible from the selected child. - private static HevcTransformComponentGeometry SplitComponent(HevcTransformComponentGeometry parent, int section) - { - if (!parent.Process || parent.Width == 0) - { - return default; - } - - int width = parent.Width >> 1; - int height = parent.Height >> 1; - int sampleCount = width * height; - if ((width < 4 || height < 4) && sampleCount < 16) - { - // A component transform cannot be smaller than four by four. Its parent rectangle is associated with - // the final luma quadrant so CBF and coefficient syntax are consumed exactly once. - return new HevcTransformComponentGeometry(parent.X, parent.Y, parent.Width, parent.Height, section == 3, false); - } - - if (width < 4) - { - width = 4; - height = sampleCount / width; - } - else if (height < 4) - { - height = 4; - width = sampleCount / height; - } - - int columns = parent.Width / width; - int x = parent.X + ((section % columns) * width); - int y = parent.Y + ((section / columns) * height); - return new HevcTransformComponentGeometry(x, y, width, height, true, true); - } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs deleted file mode 100644 index f3e20e9fc..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the bounded HEVC video-parameter-set fields required to validate and decode one still-image item. -/// -internal sealed class HevcVideoParameterSet -{ - /// - /// Initializes a new instance of the class. - /// - /// The decoded video-parameter-set NAL unit. - /// - /// The NAL unit is not a supported, conforming base-layer video parameter set. - /// - public HevcVideoParameterSet(HevcNalUnit nalUnit) - { - const byte videoParameterSetNalUnitType = 32; - if (nalUnit.Header.NalUnitType != videoParameterSetNalUnitType - || nalUnit.Header.LayerId != 0 - || nalUnit.Header.TemporalId != 0) - { - throw new InvalidImageContentException("The HEVC video parameter set has an invalid NAL-unit header."); - } - - HevcBitReader reader = new(nalUnit.Rbsp.Span); - this.Id = (byte)reader.ReadBits(4); - - bool baseLayerInternal = reader.ReadFlag(); - bool baseLayerAvailable = reader.ReadFlag(); - if (!baseLayerInternal || !baseLayerAvailable) - { - throw new InvalidImageContentException("The HEVC video parameter set does not make its base layer available."); - } - - int maxLayersMinusOne = (int)reader.ReadBits(6); - if (maxLayersMinusOne != 0) - { - // HEIF auxiliary images are separate image items. Importing an HEVC multilayer selection model would - // exceed the one-presented-image contract and is not part of the exposed still-picture profiles. - throw new InvalidImageContentException("Layered HEVC video parameter sets are not supported for still-image items."); - } - - int maxSubLayersMinusOne = (int)reader.ReadBits(3); - if (maxSubLayersMinusOne > 6) - { - throw new InvalidImageContentException("The HEVC video parameter set declares too many temporal sublayers."); - } - - this.MaxSubLayers = maxSubLayersMinusOne + 1; - this.TemporalIdNestingFlag = reader.ReadFlag(); - if (maxSubLayersMinusOne == 0 && !this.TemporalIdNestingFlag) - { - throw new InvalidImageContentException("The HEVC video parameter set has invalid temporal nesting."); - } - - if (reader.ReadBits(16) != ushort.MaxValue) - { - throw new InvalidImageContentException("The HEVC video parameter set has invalid reserved bits."); - } - - this.ProfileTierLevel = new HevcProfileTierLevel(ref reader, maxSubLayersMinusOne); - - bool subLayerOrderingInfoPresent = reader.ReadFlag(); - int firstOrderingSubLayer = subLayerOrderingInfoPresent ? 0 : maxSubLayersMinusOne; - for (int subLayer = firstOrderingSubLayer; subLayer <= maxSubLayersMinusOne; subLayer++) - { - uint maxDecodedPictureBufferingMinusOne = reader.ReadUnsignedExpGolomb(); - uint maxNumReorderPictures = reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - if (maxNumReorderPictures > maxDecodedPictureBufferingMinusOne) - { - throw new InvalidImageContentException("The HEVC video parameter set has invalid sublayer ordering limits."); - } - } - - uint maxLayerId = reader.ReadBits(6); - uint numLayerSetsMinusOne = reader.ReadUnsignedExpGolomb(); - if (maxLayerId != 0 || numLayerSetsMinusOne != 0) - { - throw new InvalidImageContentException("HEVC layer sets are not supported for still-image items."); - } - - bool timingInfoPresent = reader.ReadFlag(); - if (timingInfoPresent) - { - // Timing and hypothetical-reference-decoder values are required for bit alignment but do not describe - // the pixels of the one image item, so they are deliberately consumed without retained playback state. - reader.ReadBits(32); - reader.ReadBits(32); - if (reader.ReadFlag()) - { - reader.ReadUnsignedExpGolomb(); - } - - uint hrdParameterCount = reader.ReadUnsignedExpGolomb(); - if (hrdParameterCount > 1024) - { - throw new InvalidImageContentException("The HEVC video parameter set declares too many HRD parameter sets."); - } - - bool nalHrdParametersPresent = false; - bool vclHrdParametersPresent = false; - bool subPictureHrdParametersPresent = false; - for (uint hrdIndex = 0; hrdIndex < hrdParameterCount; hrdIndex++) - { - uint layerSetIndex = reader.ReadUnsignedExpGolomb(); - if (layerSetIndex != 0) - { - throw new InvalidImageContentException("The HEVC HRD parameters reference an unsupported layer set."); - } - - bool commonInformationPresent = hrdIndex == 0 || reader.ReadFlag(); - HevcParameterSetSyntax.SkipHrdParameters( - ref reader, - commonInformationPresent, - maxSubLayersMinusOne, - ref nalHrdParametersPresent, - ref vclHrdParametersPresent, - ref subPictureHrdParametersPresent); - } - } - - if (reader.ReadFlag()) - { - while (reader.HasMoreRbspData()) - { - reader.ReadFlag(); - } - } - - reader.ReadRbspTrailingBits(); - } - - /// - /// Gets the four-bit video-parameter-set identifier. - /// - public byte Id { get; } - - /// - /// Gets the declared number of temporal sublayers. - /// - public int MaxSubLayers { get; } - - /// - /// Gets a value indicating whether temporal identifiers are nested. - /// - public bool TemporalIdNestingFlag { get; } - - /// - /// Gets the general profile, tier, constraint, and level description. - /// - public HevcProfileTierLevel ProfileTierLevel { get; } -} diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcVideoUsabilityInformation.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcVideoUsabilityInformation.cs deleted file mode 100644 index 711a8b92a..000000000 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcVideoUsabilityInformation.cs +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Hevc; - -/// -/// Contains the still-image presentation fields declared by HEVC video-usability information. -/// -internal sealed class HevcVideoUsabilityInformation -{ - /// - /// Initializes a new instance of the class. - /// - /// The sequence-parameter-set raw byte sequence payload reader. - /// The sequence chroma-format identifier. - /// Whether 4:4:4 components are coded as separate planes. - /// The highest declared temporal sublayer index. - /// The VUI syntax is invalid for a still-image item. - public HevcVideoUsabilityInformation( - ref HevcBitReader reader, - byte chromaFormat, - bool separateColorPlane, - int maxSubLayersMinusOne) - { - this.AspectRatioInfoPresent = reader.ReadFlag(); - if (this.AspectRatioInfoPresent) - { - this.AspectRatioIdc = (byte)reader.ReadBits(8); - if (this.AspectRatioIdc == byte.MaxValue) - { - this.SarWidth = (ushort)reader.ReadBits(16); - this.SarHeight = (ushort)reader.ReadBits(16); - if (this.SarWidth == 0 || this.SarHeight == 0) - { - throw new InvalidImageContentException("The HEVC VUI declares an invalid extended sample aspect ratio."); - } - } - else if (this.AspectRatioIdc > 16) - { - throw new InvalidImageContentException("The HEVC VUI declares a reserved sample aspect ratio."); - } - } - - if (reader.ReadFlag()) - { - reader.ReadFlag(); - } - - this.VideoSignalTypePresent = reader.ReadFlag(); - if (this.VideoSignalTypePresent) - { - reader.ReadBits(3); - this.FullRange = reader.ReadFlag(); - this.ColorDescriptionPresent = reader.ReadFlag(); - if (this.ColorDescriptionPresent) - { - this.ColorPrimaries = (byte)reader.ReadBits(8); - this.TransferCharacteristics = (byte)reader.ReadBits(8); - this.MatrixCoefficients = (byte)reader.ReadBits(8); - } - } - - this.ChromaLocationInfoPresent = reader.ReadFlag(); - if (this.ChromaLocationInfoPresent) - { - uint topFieldLocation = reader.ReadUnsignedExpGolomb(); - uint bottomFieldLocation = reader.ReadUnsignedExpGolomb(); - if (topFieldLocation > 5 || bottomFieldLocation > 5) - { - throw new InvalidImageContentException("The HEVC VUI declares an invalid chroma sample location."); - } - - this.ChromaSampleLocationTopField = (HevcChromaSampleLocation)topFieldLocation; - this.ChromaSampleLocationBottomField = (HevcChromaSampleLocation)bottomFieldLocation; - } - - reader.ReadFlag(); - if (reader.ReadFlag()) - { - // A field sequence requires paired-field presentation state and is not a single HEIF image item. - throw new InvalidImageContentException("Interlaced HEVC field sequences are not supported as still-image items."); - } - - reader.ReadFlag(); - - this.DefaultDisplayWindowPresent = reader.ReadFlag(); - if (this.DefaultDisplayWindowPresent) - { - int cropUnitWidth = HevcParameterSetSyntax.GetCropUnitWidth(chromaFormat, separateColorPlane); - int cropUnitHeight = HevcParameterSetSyntax.GetCropUnitHeight(chromaFormat, separateColorPlane); - this.DefaultDisplayWindowLeftOffset = ReadScaledOffset(ref reader, cropUnitWidth); - this.DefaultDisplayWindowRightOffset = ReadScaledOffset(ref reader, cropUnitWidth); - this.DefaultDisplayWindowTopOffset = ReadScaledOffset(ref reader, cropUnitHeight); - this.DefaultDisplayWindowBottomOffset = ReadScaledOffset(ref reader, cropUnitHeight); - } - - if (reader.ReadFlag()) - { - // VUI timing and HRD fields affect scheduling, not the reconstructed still-image samples. - reader.ReadBits(32); - reader.ReadBits(32); - if (reader.ReadFlag()) - { - reader.ReadUnsignedExpGolomb(); - } - - if (reader.ReadFlag()) - { - bool nalHrdParametersPresent = false; - bool vclHrdParametersPresent = false; - bool subPictureHrdParametersPresent = false; - HevcParameterSetSyntax.SkipHrdParameters( - ref reader, - true, - maxSubLayersMinusOne, - ref nalHrdParametersPresent, - ref vclHrdParametersPresent, - ref subPictureHrdParametersPresent); - } - } - - if (reader.ReadFlag()) - { - reader.ReadFlag(); - reader.ReadFlag(); - reader.ReadFlag(); - uint minimumSpatialSegmentation = reader.ReadUnsignedExpGolomb(); - if (minimumSpatialSegmentation >= 4096) - { - throw new InvalidImageContentException("The HEVC VUI spatial-segmentation value is invalid."); - } - - reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - reader.ReadUnsignedExpGolomb(); - } - } - - /// - /// Gets a value indicating whether sample-aspect-ratio information is present. - /// - public bool AspectRatioInfoPresent { get; } - - /// - /// Gets the registered sample-aspect-ratio identifier. - /// - public byte AspectRatioIdc { get; } - - /// - /// Gets the explicit horizontal sample spacing when is 255. - /// - public ushort SarWidth { get; } - - /// - /// Gets the explicit vertical sample spacing when is 255. - /// - public ushort SarHeight { get; } - - /// - /// Gets a value indicating whether video-signal-type information is present. - /// - public bool VideoSignalTypePresent { get; } - - /// - /// Gets a value indicating whether component samples use the full numeric range. - /// - public bool FullRange { get; } - - /// - /// Gets a value indicating whether color-description fields are present. - /// - public bool ColorDescriptionPresent { get; } - - /// - /// Gets the coded color-primary identifier. - /// - public byte ColorPrimaries { get; } - - /// - /// Gets the coded transfer-characteristic identifier. - /// - public byte TransferCharacteristics { get; } - - /// - /// Gets the coded matrix-coefficient identifier. - /// - public byte MatrixCoefficients { get; } - - /// - /// Gets a value indicating whether chroma sample-location information is present. - /// - public bool ChromaLocationInfoPresent { get; } - - /// - /// Gets the top-field chroma sample-location identifier. - /// - public HevcChromaSampleLocation ChromaSampleLocationTopField { get; } - - /// - /// Gets the bottom-field chroma sample-location identifier. - /// - public HevcChromaSampleLocation ChromaSampleLocationBottomField { get; } - - /// - /// Gets a value indicating whether a default display window is present. - /// - public bool DefaultDisplayWindowPresent { get; } - - /// - /// Gets the default display-window left offset in luma samples. - /// - public int DefaultDisplayWindowLeftOffset { get; } - - /// - /// Gets the default display-window right offset in luma samples. - /// - public int DefaultDisplayWindowRightOffset { get; } - - /// - /// Gets the default display-window top offset in luma samples. - /// - public int DefaultDisplayWindowTopOffset { get; } - - /// - /// Gets the default display-window bottom offset in luma samples. - /// - public int DefaultDisplayWindowBottomOffset { get; } - - /// - /// Reads a conformance-window offset and converts it to luma-sample units. - /// - /// The sequence-parameter-set raw byte sequence payload reader. - /// The chroma-dependent luma-sample unit. - /// The scaled offset. - /// The scaled offset exceeds the supported image dimension range. - private static int ReadScaledOffset(ref HevcBitReader reader, int unit) - { - uint offset = reader.ReadUnsignedExpGolomb(); - if (offset > int.MaxValue / unit) - { - throw new InvalidImageContentException("The HEVC VUI display-window offset is too large."); - } - - return (int)offset * unit; - } -} diff --git a/src/ImageSharp/Formats/Heif/HevcHeifItemDecoder.cs b/src/ImageSharp/Formats/Heif/HevcHeifItemDecoder.cs deleted file mode 100644 index 96df7c6ba..000000000 --- a/src/ImageSharp/Formats/Heif/HevcHeifItemDecoder.cs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Components.Alpha; -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Formats.Heif.Hevc.Color; -using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Formats.Heif; - -/// -/// Decodes a single HEVC-coded HEIF image item. -/// -/// The destination pixel type. -internal sealed class HevcHeifItemDecoder : IHeifItemDecoder, IHeifAlphaItemDecoder - where TPixel : unmanaged, IPixel -{ - private HevcSupplementalEnhancementInformation? supplementalEnhancementInformation; - - /// - /// Gets the HEVC-coded image item type. - /// - public Heif4CharCode Type => Heif4CharCode.Hvc1; - - /// - /// Gets the HEVC compression method. - /// - public HeifCompressionMethod CompressionMethod => HeifCompressionMethod.Hevc; - - /// - /// Decodes the encoded HEVC payload of an image item. - /// - /// The general options governing the containing HEIF decode. - /// The HEIF item whose encoded payload is being decoded. - /// The encoded HEVC payload. - /// The container color description that takes precedence over bitstream color information. - /// The token used to cancel the payload decode. - /// The decoded image. - public Image DecodeItemData( - DecoderOptions options, - HeifItem item, - Span data, - CicpProfile? colorProfile, - CancellationToken cancellationToken) - { - this.supplementalEnhancementInformation = null; - using HevcPictureDecoder decoder = DecodePicture( - options, - item, - data, - colorProfile, - cancellationToken, - out HevcCodecConfiguration codecConfiguration, - out HevcSequenceParameterSet sequenceParameterSet, - out CicpProfile effectiveColorProfile, - out HevcChromaSampleLocation chromaSampleLocation, - out HevcSupplementalEnhancementInformation supplementalEnhancementInformation); - - if (supplementalEnhancementInformation.NoDisplay) - { - throw new InvalidImageContentException($"HEVC image item {item.Id} is marked as unavailable for display."); - } - - ValidateSupplementalMetadata(item, supplementalEnhancementInformation); - this.supplementalEnhancementInformation = supplementalEnhancementInformation; - - ImageFrame? frame = null; - Image? image = null; - try - { - frame = new ImageFrame(options.Configuration, sequenceParameterSet.DisplayWidth, sequenceParameterSet.DisplayHeight); - HevcYuvConverter.ConvertToRgb( - options.Configuration, - decoder.Picture, - frame, - effectiveColorProfile, - chromaSampleLocation, - sequenceParameterSet.ConformanceWindowLeftOffset, - sequenceParameterSet.ConformanceWindowTopOffset); - - ImageMetadata metadata = new() - { - CicpProfile = effectiveColorProfile.DeepClone() - }; - - HeifMetadata heifMetadata = metadata.GetHeifMetadata(); - heifMetadata.CompressionMethod = this.CompressionMethod; - heifMetadata.BitDepth = codecConfiguration.BitDepth; - heifMetadata.IsMonochrome = codecConfiguration.IsMonochrome; - heifMetadata.ContentLightLevel = supplementalEnhancementInformation.ContentLightLevel; - heifMetadata.MasteringDisplayColorVolume = supplementalEnhancementInformation.MasteringDisplayColorVolume; - heifMetadata.ContentColorVolume = supplementalEnhancementInformation.ContentColorVolume; - heifMetadata.AmbientViewingEnvironment = supplementalEnhancementInformation.AmbientViewingEnvironment; - - image = new Image(options.Configuration, metadata, [frame]); - frame = null; - return image; - } - catch - { - // Before the image constructor succeeds the frame remains locally owned. Afterwards the image owns it and - // every processor-created replacement buffer, so unwind exactly one of those two ownership states. - image?.Dispose(); - frame?.Dispose(); - throw; - } - } - - /// - /// Applies the active HEVC display-orientation message to the complete presented image. - /// - /// The decoded image after item scaling and auxiliary-alpha composition. - public void ApplySupplementalPresentation(Image image) - { - HevcSupplementalEnhancementInformation supplementalEnhancementInformation - = this.supplementalEnhancementInformation!; - - if (!supplementalEnhancementInformation.HasDisplayOrientation) - { - return; - } - - image.Mutate(context => - { - // H.265 applies both flips to the cropped decoded picture before its anticlockwise rotation. - // ImageSharp's positive rotation is clockwise, so quarter turns use the exact optimized modes and - // all other coded angles use the equivalent positive clockwise angle. - if (supplementalEnhancementInformation.HorizontalFlip) - { - context.Flip(FlipMode.Horizontal); - } - - if (supplementalEnhancementInformation.VerticalFlip) - { - context.Flip(FlipMode.Vertical); - } - - ushort rotation = supplementalEnhancementInformation.AnticlockwiseRotation; - switch (rotation) - { - case 0: - break; - case 16384: - context.Rotate(RotateMode.Rotate270); - break; - case 32768: - context.Rotate(RotateMode.Rotate180); - break; - case 49152: - context.Rotate(RotateMode.Rotate90); - break; - default: - context.Rotate(360F - ((360F * rotation) / 65536F)); - break; - } - }); - } - - /// - public void DecodeAlphaItemData( - DecoderOptions options, - HeifItem item, - Span data, - ImageFrame destination, - Size outputSize, - Rectangle destinationRectangle, - bool premultiplied, - CancellationToken cancellationToken) - { - using HevcPictureDecoder decoder = DecodePicture( - options, - item, - data, - item.CicpProfile, - cancellationToken, - out _, - out HevcSequenceParameterSet sequenceParameterSet, - out CicpProfile effectiveColorProfile, - out HevcChromaSampleLocation chromaSampleLocation, - out _); - - Rectangle sourceRectangle = new( - sequenceParameterSet.ConformanceWindowLeftOffset, - sequenceParameterSet.ConformanceWindowTopOffset, - sequenceParameterSet.DisplayWidth, - sequenceParameterSet.DisplayHeight); - - if (decoder.Picture.ChromaFormat != 0) - { - throw new InvalidImageContentException($"HEVC alpha image item {item.Id} is not monochrome."); - } - - HevcYuvConverter.ComposeAlpha( - options.Configuration, - decoder.Picture, - destination, - effectiveColorProfile, - chromaSampleLocation, - sourceRectangle, - outputSize, - destinationRectangle, - premultiplied); - } - - /// - /// Validates and reconstructs one HEVC image item while retaining the native picture for its caller. - /// - /// The general options governing the containing HEIF decode. - /// The HEVC image item being decoded. - /// The encoded HEVC payload. - /// The container color description that takes precedence over bitstream color information. - /// The token used to cancel the payload decode. - /// Receives the validated HEVC codec configuration. - /// Receives the sequence parameters describing the visible picture. - /// Receives the effective CICP description used for presentation. - /// Receives the progressive-frame chroma sample location. - /// Receives the bounded presentation and metadata SEI state. - /// The decoder owning the reconstructed native picture. Ownership transfers to the caller. - private static HevcPictureDecoder DecodePicture( - DecoderOptions options, - HeifItem item, - ReadOnlySpan data, - CicpProfile? colorProfile, - CancellationToken cancellationToken, - out HevcCodecConfiguration codecConfiguration, - out HevcSequenceParameterSet sequenceParameterSet, - out CicpProfile effectiveColorProfile, - out HevcChromaSampleLocation chromaSampleLocation, - out HevcSupplementalEnhancementInformation supplementalEnhancementInformation) - { - cancellationToken.ThrowIfCancellationRequested(); - codecConfiguration = item.HevcCodecConfiguration - ?? throw new InvalidImageContentException($"HEVC image item {item.Id} has no codec configuration property."); - - if (item.ChannelBitDepths is not null) - { - codecConfiguration.ValidateChannelBitDepths(item.ChannelBitDepths); - } - - HevcImageItemBitstream bitstream = new(data, codecConfiguration); - supplementalEnhancementInformation = bitstream.SupplementalEnhancementInformation; - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - HevcVideoUsabilityInformation? vui = sequenceParameterSet.VideoUsabilityInformation; - byte transferCharacteristics = vui?.ColorDescriptionPresent == true - ? vui.TransferCharacteristics - : (byte)CicpTransferCharacteristics.Unspecified; - - byte? preferredTransferCharacteristics = supplementalEnhancementInformation.PreferredTransferCharacteristics; - if (colorProfile is null && preferredTransferCharacteristics is not null) - { - transferCharacteristics = preferredTransferCharacteristics.Value; - } - - // ISO BMFF color information takes precedence when both the container and HEVC VUI describe the image. - // Otherwise, retain the VUI values and the SEI-preferred transfer function used by conversion so bitstream-only - // color information reaches metadata. - effectiveColorProfile = colorProfile is not null - ? new CicpProfile( - (byte)colorProfile.ColorPrimaries, - (byte)colorProfile.TransferCharacteristics, - (byte)colorProfile.MatrixCoefficients, - colorProfile.FullRange) - : new CicpProfile( - vui?.ColorDescriptionPresent == true ? vui.ColorPrimaries : (byte)CicpColorPrimaries.Unspecified, - transferCharacteristics, - vui?.ColorDescriptionPresent == true ? vui.MatrixCoefficients : (byte)CicpMatrixCoefficients.Unspecified, - vui?.VideoSignalTypePresent == true && vui.FullRange); - - chromaSampleLocation = vui?.ChromaLocationInfoPresent == true - ? vui.ChromaSampleLocationTopField - : HevcChromaSampleLocation.Left; - - HevcPictureDecoder decoder = new(options.Configuration, pictureParameterSet); - try - { - decoder.Decode(bitstream); - cancellationToken.ThrowIfCancellationRequested(); - return decoder; - } - catch - { - decoder.Dispose(); - throw; - } - } - - /// - /// Validates equivalent codec and item-property HDR metadata before either representation is exposed. - /// - private static void ValidateSupplementalMetadata( - HeifItem item, - HevcSupplementalEnhancementInformation supplementalEnhancementInformation) - { - HeifContentLightLevel? supplementalContentLightLevel = supplementalEnhancementInformation.ContentLightLevel; - HeifContentLightLevel? itemContentLightLevel = item.ContentLightLevel; - if (supplementalContentLightLevel is not null - && itemContentLightLevel is not null - && (supplementalContentLightLevel.Value.MaximumContentLightLevel != itemContentLightLevel.Value.MaximumContentLightLevel - || supplementalContentLightLevel.Value.MaximumPictureAverageLightLevel - != itemContentLightLevel.Value.MaximumPictureAverageLightLevel)) - { - throw new InvalidImageContentException($"HEVC image item {item.Id} has conflicting content light-level metadata."); - } - - HeifMasteringDisplayColorVolume? supplementalMasteringDisplayColorVolume - = supplementalEnhancementInformation.MasteringDisplayColorVolume; - - if (supplementalMasteringDisplayColorVolume is not null - && item.MasteringDisplayColorVolume is not null - && supplementalMasteringDisplayColorVolume.Value != item.MasteringDisplayColorVolume.Value) - { - throw new InvalidImageContentException($"HEVC image item {item.Id} has conflicting mastering-display metadata."); - } - - HeifContentColorVolume? supplementalContentColorVolume = supplementalEnhancementInformation.ContentColorVolume; - if (supplementalContentColorVolume is not null - && item.ContentColorVolume is not null - && supplementalContentColorVolume.Value != item.ContentColorVolume.Value) - { - throw new InvalidImageContentException($"HEVC image item {item.Id} has conflicting content color-volume metadata."); - } - - HeifAmbientViewingEnvironment? supplementalAmbientViewingEnvironment - = supplementalEnhancementInformation.AmbientViewingEnvironment; - - if (supplementalAmbientViewingEnvironment is not null - && item.AmbientViewingEnvironment is not null - && supplementalAmbientViewingEnvironment.Value != item.AmbientViewingEnvironment.Value) - { - throw new InvalidImageContentException($"HEVC image item {item.Id} has conflicting ambient-viewing metadata."); - } - } -} diff --git a/src/ImageSharp/Formats/Heif/Readme.md b/src/ImageSharp/Formats/Heif/Readme.md index 6c7469528..ee16813fc 100644 --- a/src/ImageSharp/Formats/Heif/Readme.md +++ b/src/ImageSharp/Formats/Heif/Readme.md @@ -2,6 +2,6 @@ [MPEG-4 register authority](https://mp4ra.org/) -[HEIF reference implementation from Nokia](https://github.com/nokiatech/heif) +[AV1 bitstream and decoding reference implementation](https://aomedia.googlesource.com/aom/) -[Apple's metadata syntax in HEIC images](http://cheeky4n6monkey.blogspot.com/2017/10/monkey-takes-heic.html) +[AV1 bitstream specification](https://aomediacodec.github.io/av1-spec/) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcCoefficientScanBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcCoefficientScanBenchmarks.cs deleted file mode 100644 index 1c33134f8..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcCoefficientScanBenchmarks.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures full-frame HEVC grouped coefficient-scan generation into reusable transform scratch. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcCoefficientScanBenchmarks -{ - /// - /// The coded frame width, which is an exact multiple of the maximum transform-block side. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The maximum transform-block side. - /// - private const int BlockSize = 32; - - /// - /// The reusable maximum-size grouped scan destination. - /// - private readonly int[] scan = new int[BlockSize * BlockSize]; - - /// - /// Measures diagonal scan generation for every maximum-size transform block in one coded full-HD frame. - /// - /// The final last-significant scan position, keeping the generated scan observable. - [Benchmark] - public int WriteDiagonalFrame() - { - int lastScanPosition = 0; - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - lastScanPosition = HevcCoefficientScanOrder.Write( - this.scan, - BlockSize, - BlockSize, - HevcCoefficientScanType.Diagonal, - (BlockSize * BlockSize) - 1); - } - } - - return lastScanPosition; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcColorConversionBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcColorConversionBenchmarks.cs deleted file mode 100644 index 010d9498b..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcColorConversionBenchmarks.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Formats.Heif.Hevc.Color; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures frame-wide HEVC YUV 4:2:0 color conversion in both directions. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcColorConversionBenchmarks -{ - /// - /// The benchmark frame width. - /// - private const int Width = 1920; - - /// - /// The benchmark frame height. - /// - private const int Height = 1080; - - /// - /// The eight-bit source RGB image. - /// - private Image byteSource = null!; - - /// - /// The eight-bit destination RGB image. - /// - private Image byteDestination = null!; - - /// - /// The high-bit-depth source RGB image. - /// - private Image highBitDepthSource = null!; - - /// - /// The high-bit-depth destination RGB image. - /// - private Image highBitDepthDestination = null!; - - /// - /// The reusable HEVC component planes. - /// - private HevcPictureBuffer picture = null!; - - /// - /// The H.273 profile used by both conversion directions. - /// - private CicpProfile colorProfile = null!; - - /// - /// Gets or sets the encoded HEVC bit depth. - /// - [Params(8, 10, 12)] - public int BitDepth { get; set; } - - /// - /// Allocates and populates deterministic full-HD RGB and YUV frames outside the measured operations. - /// - [GlobalSetup] - public void Setup() - { - this.byteSource = new Image(Width, Height); - this.byteDestination = new Image(Width, Height); - this.highBitDepthSource = new Image(Width, Height); - this.highBitDepthDestination = new Image(Width, Height); - for (int y = 0; y < Height; y++) - { - Span byteRow = this.byteSource.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); - Span highBitDepthRow = this.highBitDepthSource.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); - for (int x = 0; x < Width; x++) - { - // Relatively prime channel steps exercise the row-wide converter without introducing setup randomness. - byteRow[x] = new Rgba32((byte)((x * 29) + (y * 11)), (byte)((x * 17) + (y * 31)), (byte)((x * 7) + (y * 43))); - highBitDepthRow[x] = new Rgb48( - (ushort)((x * 1879) + (y * 791)), - (ushort)((x * 977) + (y * 3251)), - (ushort)((x * 613) + (y * 4987))); - } - } - - this.colorProfile = new CicpProfile( - (byte)CicpColorPrimaries.ItuRBt709_6, - (byte)CicpTransferCharacteristics.ItuRBt709_6, - (byte)CicpMatrixCoefficients.ItuRBt709_6, - false); - - this.picture = new HevcPictureBuffer(Configuration.Default, Width, Height, this.BitDepth, this.BitDepth, 1, false); - if (this.BitDepth == 8) - { - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - this.byteSource.Frames.RootFrame, - this.picture, - this.colorProfile, - HevcChromaSampleLocation.Left); - } - else - { - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - this.highBitDepthSource.Frames.RootFrame, - this.picture, - this.colorProfile, - HevcChromaSampleLocation.Left); - } - } - - /// - /// Releases the benchmark images and component planes. - /// - [GlobalCleanup] - public void Cleanup() - { - this.picture.Dispose(); - this.highBitDepthDestination.Dispose(); - this.highBitDepthSource.Dispose(); - this.byteDestination.Dispose(); - this.byteSource.Dispose(); - } - - /// - /// Measures full-frame YUV-to-RGB conversion, including chroma reconstruction and packed-pixel conversion. - /// - /// A converted component that keeps the frame result observable. - [Benchmark] - public ushort ConvertToRgb() - { - if (this.BitDepth == 8) - { - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - this.picture, - this.byteDestination.Frames.RootFrame, - this.colorProfile, - HevcChromaSampleLocation.Left); - - return this.byteDestination[Width - 1, Height - 1].R; - } - - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - this.picture, - this.highBitDepthDestination.Frames.RootFrame, - this.colorProfile, - HevcChromaSampleLocation.Left); - - return this.highBitDepthDestination[Width - 1, Height - 1].R; - } - - /// - /// Measures full-frame RGB-to-YUV conversion, including planar unpacking and chroma downsampling. - /// - /// An encoded luma sample that keeps the frame result observable. - [Benchmark] - public ushort ConvertFromRgb() - { - if (this.BitDepth == 8) - { - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - this.byteSource.Frames.RootFrame, - this.picture, - this.colorProfile, - HevcChromaSampleLocation.Left); - } - else - { - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - this.highBitDepthSource.Frames.RootFrame, - this.picture, - this.colorProfile, - HevcChromaSampleLocation.Left); - } - - return this.picture.GetRowSpan(HevcPlane.Y, Height - 1)[Width - 1]; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraPredictionBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraPredictionBenchmarks.cs deleted file mode 100644 index 94c885478..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraPredictionBenchmarks.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures complete coded-frame traversal through representative HEVC intra-prediction modes. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcIntraPredictionBenchmarks -{ - /// - /// The coded frame width, which is an exact multiple of the maximum transform-block side. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The base-two logarithm of the benchmark prediction-block side. - /// - private const int BlockLog2 = 5; - - /// - /// The prediction-block side in samples. - /// - private const int BlockSize = 1 << BlockLog2; - - /// - /// The prepared top reference shared by deterministic benchmark blocks. - /// - private readonly ushort[] top = new ushort[(BlockSize * 2) + 1]; - - /// - /// The prepared left reference shared by deterministic benchmark blocks. - /// - private readonly ushort[] left = new ushort[(BlockSize * 2) + 1]; - - /// - /// The frame-wide reconstructed prediction samples. - /// - private readonly ushort[] destination = new ushort[Width * Height]; - - /// - /// The maximum-block scratch reused throughout each coded frame. - /// - private readonly ushort[] scratch = new ushort[HevcIntraPredictor.GetScratchLength(BlockLog2)]; - - /// - /// Populates deterministic twelve-bit reference samples outside the measured frame traversal. - /// - [GlobalSetup] - public void Setup() - { - this.top[0] = this.left[0] = 1365; - for (int i = 1; i < this.top.Length; i++) - { - this.top[i] = (ushort)((1365 + (37 * i)) & 4095); - this.left[i] = (ushort)((1365 + (53 * i)) & 4095); - } - } - - /// - /// Measures frame-wide planar prediction with 512-, 256-, and 128-bit row dispatch where available. - /// - /// The final reconstructed sample, keeping the frame output observable. - [Benchmark(Baseline = true)] - public ushort PredictPlanarFrame() => this.PredictFrame(0); - - /// - /// Measures frame-wide fractional vertical prediction using contiguous SIMD interpolation. - /// - /// The final reconstructed sample, keeping the frame output observable. - [Benchmark] - public ushort PredictVerticalAngularFrame() => this.PredictFrame(30); - - /// - /// Measures frame-wide horizontal prediction including the SIMD block transposition stage. - /// - /// The final reconstructed sample, keeping the frame output observable. - [Benchmark] - public ushort PredictHorizontalAngularFrame() => this.PredictFrame(2); - - /// - /// Reconstructs every maximum-size prediction block in the coded benchmark frame. - /// - /// The HEVC intra-prediction mode. - /// The final reconstructed sample. - private ushort PredictFrame(int mode) - { - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - HevcIntraPredictor.Predict( - this.top, - this.left, - this.destination.AsSpan((y * Width) + x), - Width, - BlockLog2, - mode, - 12, - true, - this.scratch); - } - } - - return this.destination[^1]; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs deleted file mode 100644 index 89f993715..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures frame-wide preparation of complete and partially substituted HEVC intra references. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcIntraReferencePreparationBenchmarks -{ - /// - /// The coded frame width. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The base-two logarithm of the benchmark prediction-block side. - /// - private const int BlockLog2 = 5; - - /// - /// The prediction-block side in samples. - /// - private const int BlockSize = 1 << BlockLog2; - - /// - /// The availability-unit side in samples. - /// - private const int UnitSize = 4; - - /// - /// The number of availability units surrounding one benchmark block. - /// - private const int AvailabilityUnitCount = ((BlockSize * 2) / UnitSize * 2) + 1; - - /// - /// The complete reference availability pattern. - /// - private readonly bool[] completeAvailability = new bool[AvailabilityUnitCount]; - - /// - /// The partial availability pattern exercising forward substitution. - /// - private readonly bool[] partialAvailability = new bool[AvailabilityUnitCount]; - - /// - /// The reusable top reference destination. - /// - private readonly ushort[] top = new ushort[(BlockSize * 2) + 1]; - - /// - /// The reusable left reference destination. - /// - private readonly ushort[] left = new ushort[(BlockSize * 2) + 1]; - - /// - /// The reusable reference-line scratch storage. - /// - private readonly ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(BlockLog2, UnitSize)]; - - /// - /// The reconstructed picture providing benchmark reference samples. - /// - private HevcPictureBuffer picture; - - /// - /// Allocates and initializes the reconstructed benchmark picture outside measured operations. - /// - [GlobalSetup] - public void Setup() - { - this.picture = new HevcPictureBuffer( - Configuration.Default, - Width + (BlockSize * 2), - Height + (BlockSize * 2), - 12, - 12, - 1, - false); - - for (int y = 0; y < this.picture.Height; y++) - { - Span row = this.picture.GetRowSpan(HevcPlane.Y, y); - for (int x = 0; x < row.Length; x++) - { - row[x] = (ushort)(((37 * x) + (53 * y)) & 4095); - } - } - - this.completeAvailability.AsSpan().Fill(true); - for (int i = 0; i < this.partialAvailability.Length; i++) - { - this.partialAvailability[i] = (i & 3) != 0; - } - } - - /// - /// Releases the reconstructed benchmark picture. - /// - [GlobalCleanup] - public void Cleanup() => this.picture.Dispose(); - - /// - /// Measures the complete-border fast path across a padded 1080p coded frame. - /// - /// A prepared reference sample, keeping the result observable. - [Benchmark(Baseline = true)] - public ushort PrepareCompleteFrame() => this.PrepareFrame(this.completeAvailability); - - /// - /// Measures partial-unit collection and substitution across a padded 1080p coded frame. - /// - /// A prepared reference sample, keeping the result observable. - [Benchmark] - public ushort PreparePartialFrame() => this.PrepareFrame(this.partialAvailability); - - /// - /// Prepares references for every maximum-size block in the benchmark frame. - /// - /// The repeated availability pattern. - /// The final prepared reference sample. - private ushort PrepareFrame(ReadOnlySpan availability) - { - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - HevcIntraPredictor.PrepareReferenceSamples( - this.picture, - HevcPlane.Y, - x + BlockSize, - y + BlockSize, - BlockLog2, - UnitSize, - UnitSize, - availability, - this.top, - this.left, - this.scratch); - } - } - - return this.left[^1]; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseQuantizationBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseQuantizationBenchmarks.cs deleted file mode 100644 index aa538c016..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseQuantizationBenchmarks.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures complete coded-frame traversal through HEVC inverse quantization. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcInverseQuantizationBenchmarks -{ - /// - /// The coded frame width, which is an exact multiple of the maximum transform-block side. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The base-two logarithm of the benchmark transform-block side. - /// - private const int BlockLog2 = 5; - - /// - /// The transform-block side in samples. - /// - private const int BlockSize = 1 << BlockLog2; - - /// - /// The deterministic quantized coefficients reused by each benchmark block. - /// - private readonly int[] quantized = new int[BlockSize * BlockSize]; - - /// - /// The reusable dequantized coefficient block. - /// - private readonly int[] destination = new int[BlockSize * BlockSize]; - - /// - /// The effective default scaling matrices. - /// - private readonly HevcScalingList scalingList = new(); - - /// - /// Populates a dense, deterministic twelve-bit transform workload outside the measured frame traversal. - /// - [GlobalSetup] - public void Setup() - { - for (int i = 0; i < this.quantized.Length; i++) - { - this.quantized[i] = (((i * 7919) + 257) & 131071) - 65536; - } - } - - /// - /// Measures frame-wide inverse quantization using one uniform scale. - /// - /// The final coefficient, keeping the block output observable. - [Benchmark(Baseline = true)] - public int DequantizeFlatFrame() => this.DequantizeFrame(false); - - /// - /// Measures frame-wide inverse quantization using an expanded thirty-two-by-thirty-two scaling matrix. - /// - /// The final coefficient, keeping the block output observable. - [Benchmark] - public int DequantizeScalingListFrame() => this.DequantizeFrame(true); - - /// - /// Dequantizes every maximum-size transform block in the coded benchmark frame. - /// - /// Whether the default intra-luma scaling matrix applies. - /// The final dequantized coefficient. - private int DequantizeFrame(bool scalingListEnabled) - { - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - HevcInverseQuantizer.Dequantize( - this.quantized, - this.destination, - BlockLog2, - 12, - 18, - 75, - scalingListEnabled, - this.scalingList, - HevcPlane.Y, - true, - false, - true); - } - } - - return this.destination[^1]; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseTransformBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseTransformBenchmarks.cs deleted file mode 100644 index 2195b82c9..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcInverseTransformBenchmarks.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures complete coded-frame traversal through HEVC inverse-transform reconstruction. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcInverseTransformBenchmarks -{ - /// - /// The coded frame width, which is an exact multiple of the maximum transform-block side. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The base-two logarithm of the benchmark transform-block side. - /// - private const int BlockLog2 = 5; - - /// - /// The transform-block side in samples. - /// - private const int BlockSize = 1 << BlockLog2; - - /// - /// The deterministic dequantized coefficients reused by each benchmark block. - /// - private readonly int[] coefficients = new int[BlockSize * BlockSize]; - - /// - /// The frame-wide predicted and reconstructed samples. - /// - private readonly ushort[] destination = new ushort[Width * Height]; - - /// - /// The maximum-block inverse-transform scratch reused throughout each coded frame. - /// - private readonly int[] scratch = new int[HevcInverseTransformer.GetScratchLength(BlockLog2, BlockLog2)]; - - /// - /// Populates a dense, deterministic twelve-bit transform workload outside the measured frame traversal. - /// - [GlobalSetup] - public void Setup() - { - for (int i = 0; i < this.coefficients.Length; i++) - { - this.coefficients[i] = (((i * 37) + 11) % 127) - 63; - } - - this.destination.AsSpan().Fill(2048); - } - - /// - /// Measures factorized inverse DCT, both transpositions, and saturated prediction addition for a coded frame. - /// - /// The final reconstructed sample, keeping the frame output observable. - [Benchmark] - public ushort TransformFrame() - { - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - HevcInverseTransformer.TransformAdd( - this.coefficients, - this.destination.AsSpan((y * Width) + x), - Width, - BlockLog2, - BlockLog2, - 12, - 18, - false, - this.scratch); - } - } - - return this.destination[^1]; - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcResidualReconstructionBenchmarks.cs b/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcResidualReconstructionBenchmarks.cs deleted file mode 100644 index 664b44b67..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Heif/HevcResidualReconstructionBenchmarks.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; - -/// -/// Measures complete coded-frame traversal through HEVC transform-skip and residual differential reconstruction. -/// -[MemoryDiagnoser(displayGenColumns: false)] -public class HevcResidualReconstructionBenchmarks -{ - /// - /// The coded frame width, which is an exact multiple of the maximum transform-block side. - /// - private const int Width = 1920; - - /// - /// The coded frame height including the final padded coding-tree row for a 1080-line presentation. - /// - private const int Height = 1088; - - /// - /// The benchmark transform-block side in samples. - /// - private const int BlockSize = 32; - - /// - /// The deterministic dequantized coefficients reused by each benchmark block. - /// - private readonly int[] coefficients = new int[BlockSize * BlockSize]; - - /// - /// The reusable packed residual block. - /// - private readonly int[] residual = new int[BlockSize * BlockSize]; - - /// - /// Populates a dense, deterministic twelve-bit transform-skip workload outside the measured frame traversal. - /// - [GlobalSetup] - public void Setup() - { - for (int i = 0; i < this.coefficients.Length; i++) - { - this.coefficients[i] = (((i * 104729) + 4099) & 8191) - 4096; - } - } - - /// - /// Measures frame-wide transform-skip normalization. - /// - /// The final residual, keeping the block output observable. - [Benchmark(Baseline = true)] - public int TransformSkipFrame() => this.ReconstructFrame(HevcResidualDpcmMode.None); - - /// - /// Measures frame-wide transform-skip normalization followed by horizontal residual differential reconstruction. - /// - /// The final residual, keeping the block output observable. - [Benchmark] - public int HorizontalResidualDpcmFrame() => this.ReconstructFrame(HevcResidualDpcmMode.Horizontal); - - /// - /// Measures frame-wide transform-skip normalization followed by vertical residual differential reconstruction. - /// - /// The final residual, keeping the block output observable. - [Benchmark] - public int VerticalResidualDpcmFrame() => this.ReconstructFrame(HevcResidualDpcmMode.Vertical); - - /// - /// Reconstructs every maximum-size transform block in the coded benchmark frame. - /// - /// The residual differential mode applied after transform-skip normalization. - /// The final reconstructed residual. - private int ReconstructFrame(HevcResidualDpcmMode mode) - { - for (int y = 0; y < Height; y += BlockSize) - { - for (int x = 0; x < Width; x += BlockSize) - { - HevcResidualReconstructor.ApplyTransformSkip(this.coefficients, this.residual, BlockSize, BlockSize, 12, 18, 5, true, false); - HevcResidualReconstructor.ApplyResidualDpcm(this.residual, BlockSize, BlockSize, mode); - } - } - - return this.residual[^1]; - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs index 5c4f6cc05..27927fcd1 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs @@ -10,7 +10,6 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.ColorProfiles.Icc; -using SixLabors.ImageSharp.Tests.Memory; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -22,9 +21,6 @@ public class HeifDecoderTests { private const uint UnknownBoxType = 0x74657374U; - private const HwIntrinsics HevcPresentationConfigurations = - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; - private static ReadOnlySpan MalformedJpegApp13 => [ 0xFF, 0xED, @@ -36,9 +32,6 @@ public class HeifDecoderTests ]; [Theory] - [InlineData(TestImages.Heif.Image1, HeifCompressionMethod.Hevc, HeifBitDepth.Bit8, 3992, 2992)] - [InlineData(TestImages.Heif.Sample640x427, HeifCompressionMethod.Hevc, HeifBitDepth.Bit8, 640, 428)] - [InlineData(TestImages.Heif.FujiFilmHif, HeifCompressionMethod.LegacyJpeg, HeifBitDepth.Bit8, 7728, 5152)] [InlineData(TestImages.Heif.IrvineAvif, HeifCompressionMethod.Av1, HeifBitDepth.Bit8, 480, 640)] public void Identify(string imagePath, HeifCompressionMethod compressionMethod, HeifBitDepth bitDepth, int width, int height) { @@ -46,305 +39,16 @@ public class HeifDecoderTests using MemoryStream stream = new(testFile.Bytes, false); ImageInfo imageInfo = Image.Identify(stream); - HeifMetadata heicMetadata = imageInfo.Metadata.GetHeifMetadata(); + HeifMetadata heifMetadata = imageInfo.Metadata.GetHeifMetadata(); Assert.NotNull(imageInfo); Assert.Equal(HeifFormat.Instance, imageInfo.Metadata.DecodedImageFormat); - Assert.Equal(compressionMethod, heicMetadata.CompressionMethod); - Assert.Equal(bitDepth, heicMetadata.BitDepth); + Assert.Equal(compressionMethod, heifMetadata.CompressionMethod); + Assert.Equal(bitDepth, heifMetadata.BitDepth); Assert.Equal(width, imageInfo.Width); Assert.Equal(height, imageInfo.Height); } - [Theory] - [WithFile(TestImages.Heif.FujiFilmHif, PixelTypes.Rgba32)] - public void Decode(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - HeifMetadata heicMetadata = image.Metadata.GetHeifMetadata(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(provider); - Assert.Equal(HeifCompressionMethod.LegacyJpeg, heicMetadata.CompressionMethod); - } - - /// - /// Verifies complete HEVC still-image decoding for real grid, auxiliary-alpha, 4:2:0, and 4:4:4 HEIC inputs. - /// - /// The real HEIC input and matching output naming context. - /// The independently reported presented width. - /// The independently reported presented height. - /// Whether the presented image carries an ICC profile. - [Theory] - [WithFile(TestImages.Heif.Image1, PixelTypes.Rgba32, 3992, 2992, true)] - [WithFile(TestImages.Heif.Image2, PixelTypes.Rgba32, 3464, 2130, true)] - [WithFile(TestImages.Heif.Image3, PixelTypes.Rgba32, 4242, 2828, true)] - [WithFile(TestImages.Heif.Image4, PixelTypes.Rgba32, 700, 476, true)] - [WithFile(TestImages.Heif.Sample640x427, PixelTypes.Rgba32, 640, 428, false)] - public void DecodeHevcStillImage(TestImageProvider provider, int width, int height, bool hasIccProfile) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - HeifMetadata metadata = image.Metadata.GetHeifMetadata(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - - Assert.Equal(new Size(width, height), image.Size); - Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); - Assert.Equal(HeifBitDepth.Bit8, metadata.BitDepth); - Assert.Equal(hasIccProfile, image.Metadata.IccProfile is not null); - } - - /// - /// Verifies genuine HEIC presentation for the official HEVC profile and Range Extensions matrix against - /// independently pinned presentation references. - /// - /// The genuine HEIC input and matching reference-output naming context. - /// The independently reported presented width. - /// The independently reported presented height. - /// The maximum coded component precision. - [Theory] - [WithFile(TestImages.Heif.General8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.General10Bit420Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.General10Bit422Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.General10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.General12Bit420Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.General12Bit422Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.RangeExtensionChromaAngle422Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.RangeExtensionCrossComponent8Bit444Heic, PixelTypes.Rgba32, 1280, 720, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.RangeExtensionCrossComponent10Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.RangeExtensionCrossComponent12Bit444Heic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.ExtendedPrecision8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.ExtendedPrecision10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.ExtendedPrecision12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.RangeExtensionPcm10Bit422Heic, PixelTypes.Rgba32, 416, 240, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.RangeExtensionPcm12Bit444Heic, PixelTypes.Rgba32, 416, 240, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.PersistentRice12Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.TransformSkipContext8Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.TransformSkipContext10Bit444Heic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.TransformSkipContext12Bit444Heic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit12)] - [WithFile(TestImages.Heif.Main42210AHeic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.Main42210BHeic, PixelTypes.Rgba32, 2560, 1600, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.HighThroughput8Bit420WavefrontHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)] - public void DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation( - TestImageProvider provider, - int width, - int height, - HeifBitDepth bitDepth) - { - using Image image = provider.GetImage(); - HeifMetadata metadata = image.Metadata.GetHeifMetadata(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - - Assert.Equal(new Size(width, height), image.Size); - Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); - Assert.Equal(bitDepth, metadata.BitDepth); - Assert.Null(image.Metadata.IccProfile); - } - - /// - /// Verifies genuine monochrome HEIC presentation against pinned libheif output. - /// - /// The image provider. - /// The coded luma precision. - [Theory] - [WithFile(TestImages.Heif.General8BitMonochromeHeic, PixelTypes.Rgba32, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba32, HeifBitDepth.Bit12)] - public void DecodeHevcMonochromeStillImageMatchesPinnedLibheif( - TestImageProvider provider, - HeifBitDepth bitDepth) - { - using Image 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(bitDepth, metadata.BitDepth); - Assert.Null(image.Metadata.IccProfile); - } - - /// - /// Verifies that high-bit-depth monochrome HEIC presentation preserves its source precision. - /// - /// The image provider. - [Theory] - [WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba64)] - public void DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision(TestImageProvider provider) - { - using Image 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); - } - - /// - /// Verifies genuine HEIC presentation for the official high-throughput Range Extensions streams against - /// independently pinned presentation references. - /// - /// The genuine HEIC input and matching reference-output naming context. - /// The independently reported presented width. - /// The independently reported presented height. - /// The maximum coded component precision. - [Theory] - [WithFile(TestImages.Heif.HighThroughputExtendedPrecision8Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.HighThroughputExtendedPrecision10Bit444Heic, PixelTypes.Rgba32, 400, 384, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.HighThroughput10Bit422TilesWavefrontHeic, PixelTypes.Rgba32, 1920, 1080, HeifBitDepth.Bit10)] - [WithFile(TestImages.Heif.HighThroughput8Bit420TilesWavefrontHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.HighThroughput8Bit420CabacBypassAlignmentHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)] - [WithFile(TestImages.Heif.HighThroughput8Bit420ExtendedPrecisionHeic, PixelTypes.Rgba32, 1024, 768, HeifBitDepth.Bit8)] - public void DecodeHevcHighThroughputStillImageMatchesPinnedPresentation( - TestImageProvider provider, - int width, - int height, - HeifBitDepth bitDepth) - { - using Image image = provider.GetImage(); - HeifMetadata metadata = image.Metadata.GetHeifMetadata(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - - Assert.Equal(new Size(width, height), image.Size); - Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); - Assert.Equal(bitDepth, metadata.BitDepth); - Assert.Null(image.Metadata.IccProfile); - } - - /// - /// Verifies genuine HEIC presentation for unequal luma and chroma precision, which independent HEIF decoders - /// currently reject after reconstructing the native planes. - /// - /// The retained genuine HEIC input. - [Theory] - [WithFile(TestImages.Heif.RangeExtensionLuma12Chroma8Heic, PixelTypes.Rgba32)] - [WithFile(TestImages.Heif.RangeExtensionLuma8Chroma12Heic, PixelTypes.Rgba32)] - public void DecodeHevcRangeExtensionUnequalBitDepthStillImage(TestImageProvider provider) - { - using Image image = provider.GetImage(); - HeifMetadata metadata = image.Metadata.GetHeifMetadata(); - - Assert.Equal(new Size(1920, 1080), image.Size); - Assert.Equal(HeifCompressionMethod.Hevc, metadata.CompressionMethod); - Assert.Equal(HeifBitDepth.Bit12, metadata.BitDepth); - Assert.Null(image.Metadata.IccProfile); - } - - /// - /// Verifies representative genuine HEIC Range Extensions presentation with allocator-split buffers and - /// exactly-once final disposal. - /// - /// The genuine HEIC input and allocator configuration. - /// The independently reported presented width. - /// The independently reported presented height. - [Theory] - [WithFile(TestImages.Heif.General8Bit420Heic, PixelTypes.Rgba32, 400, 384)] - [WithFile(TestImages.Heif.General10Bit422Heic, PixelTypes.Rgba32, 400, 384)] - [WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba32, 400, 384)] - [WithFile(TestImages.Heif.General12BitMonochromeHeic, PixelTypes.Rgba32, 400, 384)] - [WithFile(TestImages.Heif.HighThroughput8Bit420ExtendedPrecisionHeic, PixelTypes.Rgba32, 1024, 768)] - public void DecodeHevcRangeExtensionStillImageWithConstrainedAllocator( - TestImageProvider provider, - int width, - int height) - { - TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 4_096 }; - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - provider.Configuration = configuration; - - using (Image image = provider.GetImage()) - { - Assert.Equal(new Size(width, height), image.Size); - } - - Assert.NotEmpty(allocator.AllocationLog); - Assert.Equal(allocator.AllocationLog.Count, allocator.ReturnLog.Count); - Assert.All( - allocator.AllocationLog, - allocation => Assert.Single( - allocator.ReturnLog, - returned => returned.AllocationId == allocation.AllocationId)); - - } - - /// - /// Verifies representative eight-bit subsampled and high-bit-depth full-resolution presentation through every - /// available vector width and the scalar fallback. - /// - /// The genuine HEIC input and matching reference-output naming context. - [Theory] - [WithFile(TestImages.Heif.General8Bit420Heic, PixelTypes.Rgba32)] - [WithFile(TestImages.Heif.HighThroughputExtendedPrecision12Bit444Heic, PixelTypes.Rgba32)] - public void DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths( - TestImageProvider provider) - => FeatureTestRunner.RunWithHwIntrinsicsFeature( - ValidateHevcRangeExtensionPresentation, - HevcPresentationConfigurations, - provider); - - /// - /// Verifies high-bit-depth HEVC presentation precision through every available vector width and the scalar fallback. - /// - /// The genuine HEIC input and matching reference-output naming context. - [Theory] - [WithFile(TestImages.Heif.General12Bit444Heic, PixelTypes.Rgba64)] - public void DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths( - TestImageProvider provider) - { - using Image image = provider.GetImage(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - - FeatureTestRunner.RunWithHwIntrinsicsFeature( - ValidateHevcHighBitDepthPresentation, - HevcPresentationConfigurations, - provider); - } - - /// - /// Verifies one HEVC presentation with the feature-runner configuration and the repository reference API. - /// - /// The serialized input provider and reference-output naming context. - private static void ValidateHevcRangeExtensionPresentation(string providerDump) - { - TestImageProvider provider = - FeatureTestRunner.DeserializeForXunit>(providerDump); - - using Image image = provider.GetImage(); - image.DebugSave(provider); - - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - } - - /// - /// Verifies one high-bit-depth HEVC presentation with the feature-runner configuration and the repository reference API. - /// - /// The serialized input provider and reference-output naming context. - private static void ValidateHevcHighBitDepthPresentation(string providerDump) - { - TestImageProvider provider = - FeatureTestRunner.DeserializeForXunit>(providerDump); - - using Image image = provider.GetImage(); - image.CompareToReferenceOutput(ImageComparer.Exact, provider); - } - /// /// Verifies that AVIF decoding preserves the exact embedded ICC profile bytes. /// @@ -919,8 +623,6 @@ public class HeifDecoderTests } [Theory] - [InlineData(Heif4CharCode.Heic)] - [InlineData(Heif4CharCode.Heix)] [InlineData(Heif4CharCode.Mif1)] [InlineData(Heif4CharCode.Avif)] [InlineData(Heif4CharCode.Jpeg)] @@ -936,20 +638,7 @@ public class HeifDecoderTests Assert.Same(HeifFormat.Instance, format); } - [Fact] - public void IdentifyAcceptsSupportedCompatibleBrand() - { - byte[] data = CreateEncodedContainer(); - BinaryPrimitives.WriteUInt32BigEndian(data.AsSpan(8), UnknownBoxType); - - ImageInfo imageInfo = Image.Identify(data); - - Assert.Equal(new Size(2, 3), imageInfo.Size); - } - [Theory] - [InlineData(Heif4CharCode.Hevc)] - [InlineData(Heif4CharCode.Hevx)] [InlineData(Heif4CharCode.Avis)] public void DetectorRecognizesSupportedSequenceMajorBrand(Heif4CharCode brand) { @@ -964,8 +653,6 @@ public class HeifDecoderTests } [Theory] - [InlineData(Heif4CharCode.Hevm)] - [InlineData(Heif4CharCode.Hevs)] [InlineData(Heif4CharCode.Jpgs)] public void DetectorRejectsUnsupportedSequenceMajorBrand(Heif4CharCode brand) { diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs index e6cb6ccf8..04b4adb95 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs @@ -96,7 +96,7 @@ public class HeifEncoderTests } [Theory] - [WithFile(TestImages.Heif.Sample640x427, PixelTypes.Rgba32, HeifCompressionMethod.LegacyJpeg)] + [WithFile(TestImages.Heif.IrvineAvif, PixelTypes.Rgba32, HeifCompressionMethod.LegacyJpeg)] public static void Encode(TestImageProvider provider, HeifCompressionMethod compressionMethod) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifMetadataTests.cs index 11b1cc3ee..7c75dfea0 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/HeifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/HeifMetadataTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Heif; -using SixLabors.ImageSharp.Formats.Heif.Hevc; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.Formats.Heif; @@ -113,48 +112,4 @@ public class HeifMetadataTests Assert.Equal((int)bitDepth, componentInfo.GetMaximumComponentPrecision()); } - [Theory] - [InlineData(8, HeifBitDepth.Bit8)] - [InlineData(10, HeifBitDepth.Bit10)] - [InlineData(12, HeifBitDepth.Bit12)] - public void HevcConfigurationAcceptsExposedBitDepths(int componentBitDepth, HeifBitDepth expected) - { - HevcCodecConfiguration configuration = new(CreateHevcCodecConfiguration(componentBitDepth)); - - Assert.Equal(expected, configuration.BitDepth); - } - - [Theory] - [InlineData(9)] - [InlineData(11)] - [InlineData(13)] - [InlineData(14)] - [InlineData(15)] - public void HevcConfigurationRejectsUnexposedBitDepths(int componentBitDepth) - { - byte[] configuration = CreateHevcCodecConfiguration(componentBitDepth); - - Assert.Throws(() => new HevcCodecConfiguration(configuration)); - } - - /// - /// Creates the fixed HEVC decoder-configuration record needed to exercise component bit-depth validation. - /// - /// The luma and chroma sample precision to encode in the record. - /// The complete configuration record without parameter-set arrays. - private static byte[] CreateHevcCodecConfiguration(int componentBitDepth) - { - byte[] data = new byte[23]; - data[0] = 1; - data[13] = 0xF0; - data[15] = 0xFC; - data[16] = 0xFD; - data[17] = (byte)(0xF8 | (componentBitDepth - 8)); - data[18] = (byte)(0xF8 | (componentBitDepth - 8)); - data[21] = 3; - - // An empty array list is sufficient here because bit-depth validation belongs to the fixed record and runs - // before parameter-set matching. Parameter-set conformance is covered separately by the container tests. - return data; - } } diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs index 5407eaa70..99f77f654 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs @@ -437,28 +437,6 @@ public class HeifSequenceParserTests Assert.Throws(() => parser.Parse(stream, GetMoviePayloadLength(data))); } - /// - /// Verifies that HEVC composition offsets and composition-to-decode information identify hidden samples and - /// assign the expected presentation time to the visible sample. - /// - [Fact] - public void ParseMarksHiddenHevcSamples() - { - byte[] data = CreateSequenceFile(SyntheticChunkOffset, hevc: true, compositionOffsets: true); - using MemoryStream stream = new(data, false); - HeifSequenceParser parser = CreateParser(SyntheticSampleCount); - stream.Position = BoxHeaderLength; - - HeifSequence sequence = parser.Parse(stream, GetMoviePayloadLength(data)); - - Assert.Equal(Heif4CharCode.Hvc1, sequence.ColorTrack.CodecType); - Assert.NotNull(sequence.ColorTrack.HevcCodecConfiguration); - Assert.True(sequence.ColorTrack.Samples[0].IsHidden); - Assert.Equal(long.MinValue, sequence.ColorTrack.Samples[0].CompositionTime); - Assert.False(sequence.ColorTrack.Samples[1].IsHidden); - Assert.Equal(100, sequence.ColorTrack.Samples[1].CompositionTime); - } - /// /// Verifies that auxiliary-track and premultiplication references are resolved by track identifier and remain /// valid when matching presentation properties are present on either track. @@ -559,8 +537,7 @@ public class HeifSequenceParserTests } /// - /// Verifies that AV1 image-sequence tracks reject composition-offset tables, which are only supported for the - /// HEVC hidden-sample presentation model. + /// Verifies that AV1 image-sequence tracks reject prohibited composition timing boxes. /// [Fact] public void ParseRejectsCompositionOffsetsForAv1() @@ -756,8 +733,7 @@ public class HeifSequenceParserTests /// outside the movie so tests can independently control sample offsets and source-length validation. /// /// The absolute file offset of the track's single sample chunk. - /// Whether the visual sample entry describes HEVC instead of AV1. - /// Whether to write HEVC composition-offset and shift boxes. + /// Whether to write prohibited composition timing boxes. /// Whether to write AV1 direct-reference sample groups. /// The sample identifier named by the dependent sample. /// Whether to write image presentation properties in the sample entry. @@ -778,7 +754,6 @@ public class HeifSequenceParserTests /// The fixed-length synthetic file containing the serialized movie box. private static byte[] CreateSequenceFile( uint chunkOffset, - bool hevc = false, bool compositionOffsets = false, bool directReferences = false, uint directReferenceSampleId = ColorTrackId, @@ -843,7 +818,6 @@ public class HeifSequenceParserTests WriteSampleTable( writer, chunkOffset, - hevc, compositionOffsets, directReferences, directReferenceSampleId, @@ -933,7 +907,6 @@ public class HeifSequenceParserTests alphaChunkOffset ?? chunkOffset, false, false, - false, ColorTrackId, alphaTransforms, false, @@ -1341,8 +1314,7 @@ public class HeifSequenceParserTests /// /// The writer receiving the SampleTableBox. /// The absolute file offset of the single sample chunk. - /// Whether the sample description uses HEVC instead of AV1. - /// Whether to write composition-offset and shift boxes. + /// Whether to write prohibited composition timing boxes. /// Whether to write AV1 direct-reference grouping. /// The sample identifier referenced by the second sample. /// Whether the visual sample entry contains presentation properties. @@ -1357,7 +1329,6 @@ public class HeifSequenceParserTests private static void WriteSampleTable( BinaryWriter writer, uint chunkOffset, - bool hevc, bool compositionOffsets, bool directReferences, uint directReferenceSampleId, @@ -1377,7 +1348,7 @@ public class HeifSequenceParserTests const uint variableSampleSizes = 0; long sampleTable = BeginBox(writer, Heif4CharCode.Stbl); - WriteSampleDescription(writer, hevc, trackProperties, invalidRotation, width, height, av1Configuration, allSamplesSync, alpha); + WriteSampleDescription(writer, trackProperties, invalidRotation, width, height, av1Configuration, allSamplesSync, alpha); long timing = BeginBox(writer, Heif4CharCode.Stts); WriteFullBoxHeader(writer, 0, 0); @@ -1508,7 +1479,6 @@ public class HeifSequenceParserTests /// properties, and CodingConstraintsBox. /// /// The writer receiving the SampleDescriptionBox. - /// Whether the entry uses HEVC instead of AV1. /// Whether to append image presentation properties. /// Whether the rotation property contains reserved high bits. /// The coded sample width in pixels. @@ -1518,7 +1488,6 @@ public class HeifSequenceParserTests /// Whether the entry carries the HEIF alpha auxiliary type. private static void WriteSampleDescription( BinaryWriter writer, - bool hevc, bool trackProperties, bool invalidRotation, int width, @@ -1539,7 +1508,7 @@ public class HeifSequenceParserTests long description = BeginBox(writer, Heif4CharCode.Stsd); WriteFullBoxHeader(writer, 0, 0); WriteUInt32(writer, 1); - long sampleEntry = BeginBox(writer, hevc ? Heif4CharCode.Hvc1 : Heif4CharCode.Av01); + long sampleEntry = BeginBox(writer, Heif4CharCode.Av01); // ISO/IEC 14496-12 Section 12.1.3 defines six reserved bytes and a data-reference index before the visual // sample entry's predefined words, dimensions, 16.16 resolution, frame count, fixed compressor-name field, @@ -1557,18 +1526,11 @@ public class HeifSequenceParserTests WriteUInt16(writer, SyntheticPixelDepth); WriteUInt16(writer, noColorTable); - if (hevc) - { - WriteHevcConfiguration(writer); - } - else - { - long configuration = BeginBox(writer, Heif4CharCode.Av1C); - ReadOnlySpan configurationPayload = av1Configuration is null ? DefaultAv1Configuration : av1Configuration; + long configuration = BeginBox(writer, Heif4CharCode.Av1C); + ReadOnlySpan configurationPayload = av1Configuration is null ? DefaultAv1Configuration : av1Configuration; - writer.Write(configurationPayload); - EndBox(writer, configuration); - } + writer.Write(configurationPayload); + EndBox(writer, configuration); if (alpha) { @@ -1729,41 +1691,6 @@ public class HeifSequenceParserTests EndBox(writer, nominalDiffuseWhite); } - /// - /// Writes the minimal HEVCDecoderConfigurationRecord accepted by the parser, with no parameter-set arrays, - /// because sample-table tests exercise container timing rather than HEVC bitstream decoding. - /// - /// The writer receiving the HEVCConfigurationBox. - private static void WriteHevcConfiguration(BinaryWriter writer) - { - const byte configurationVersion = 1; - const byte mainProfileIdc = 1; - const ushort reservedMinSpatialSegmentationIdc = 0xF000; - const byte reservedParallelismType = 0xFC; - const byte reservedChromaFormat420 = 0xFD; - const byte reservedEightBitDepth = 0xF8; - const byte fourByteNalUnitLength = 3; - - long configuration = BeginBox(writer, Heif4CharCode.HvcC); - - // ISO/IEC 14496-15 defines this HEVCDecoderConfigurationRecord layout. Only the profile and NAL-unit length - // fields are material to these container tests; compatibility, constraints, level, timing, and arrays are empty. - writer.Write(configurationVersion); - writer.Write(mainProfileIdc); - WriteUInt32(writer, 0); - WriteZeros(writer, 6); - writer.Write((byte)0); - WriteUInt16(writer, reservedMinSpatialSegmentationIdc); - writer.Write(reservedParallelismType); - writer.Write(reservedChromaFormat420); - writer.Write(reservedEightBitDepth); - writer.Write(reservedEightBitDepth); - WriteUInt16(writer, 0); - writer.Write(fourByteNalUnitLength); - writer.Write((byte)0); - EndBox(writer, configuration); - } - /// /// Starts a small ISO base media box with a placeholder 32-bit size that patches later. /// diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCabacDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCabacDecoderTests.cs deleted file mode 100644 index cd7b1e1cb..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCabacDecoderTests.cs +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC context initialization, arithmetic decoding, bypass decoding, termination, and PCM restart. -/// -[Trait("Format", "Heic")] -public class HevcCabacDecoderTests -{ - /// - /// Verifies every intra-slice context initialization against the table in the pinned HM ContextTables.h. - /// - /// The luma quantization parameter used to initialize the contexts. - [Theory] - [InlineData(0)] - [InlineData(22)] - [InlineData(51)] - public void IntraContextInitializationMatchesPinnedHmTable(int quantizationParameter) - { - ReadOnlySpan initializationValues = - [ - 154, - 139, 141, 157, - 184, - 184, - 63, 139, - 154, 154, 154, - 154, - 154, - 111, 141, 154, 154, 154, - 94, 138, 182, 154, 154, - 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, - 108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, - 108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - 91, 171, 134, 141, - 111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153, - 125, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 141, - 140, 139, 182, 182, 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 111, - 140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152, - 140, 179, 166, 182, 140, 227, 122, 197, - 138, 153, 136, 167, 152, 152, - 153, - 200, - 153, 138, 138, - 139, 139, - 154, 154, 154, 154, 154, 154, 154, 154, 154, 154 - ]; - - Assert.Equal(HevcCabacContexts.ContextCount, initializationValues.Length); - HevcCabacContexts contexts = new(quantizationParameter); - Span actual = stackalloc HevcCabacContext[HevcCabacContexts.ContextCount]; - contexts.CopyTo(actual); - - for (int index = 0; index < actual.Length; index++) - { - int expectedState = GetInitializedPackedState(quantizationParameter, initializationValues[index]); - Assert.Equal(expectedState, GetPackedState(actual[index])); - } - } - - /// - /// Verifies every reachable probability-state transition against the pinned HM ContextModel.cpp tables. - /// - [Fact] - public void ReachableContextTransitionsMatchPinnedHmTables() - { - ReadOnlySpan mostProbableTransitions = - [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 124, 125, 126, 127 - ]; - - ReadOnlySpan leastProbableTransitions = - [ - 1, 0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 18, 19, 22, 23, 22, 23, 24, 25, - 26, 27, 26, 27, 30, 31, 30, 31, 32, 33, 32, 33, 36, 37, 36, 37, - 38, 39, 38, 39, 42, 43, 42, 43, 44, 45, 44, 45, 46, 47, 48, 49, - 48, 49, 50, 51, 52, 53, 52, 53, 54, 55, 54, 55, 56, 57, 58, 59, - 58, 59, 60, 61, 60, 61, 60, 61, 62, 63, 64, 65, 64, 65, 66, 67, - 66, 67, 66, 67, 68, 69, 68, 69, 70, 71, 70, 71, 70, 71, 72, 73, - 72, 73, 72, 73, 74, 75, 74, 75, 74, 75, 76, 77, 76, 77, 126, 127 - ]; - - Span visited = stackalloc bool[128]; - int visitedCount = 0; - for (int quantizationParameter = 0; quantizationParameter <= 51; quantizationParameter++) - { - for (int initializationValue = 0; initializationValue <= byte.MaxValue; initializationValue++) - { - HevcCabacContext context = new(quantizationParameter, (byte)initializationValue); - int packedState = GetPackedState(context); - if (visited[packedState]) - { - continue; - } - - visited[packedState] = true; - visitedCount++; - - HevcCabacContext mostProbableContext = context; - mostProbableContext.UpdateMostProbableSymbol(); - Assert.Equal(mostProbableTransitions[packedState], GetPackedState(mostProbableContext)); - - HevcCabacContext leastProbableContext = context; - leastProbableContext.UpdateLeastProbableSymbol(); - Assert.Equal(leastProbableTransitions[packedState], GetPackedState(leastProbableContext)); - } - } - - // The clipped initialization equation reaches packed states 0 through 125. HM's terminal states 126 and 127 - // cannot be entered from those states, so they are not transitions of a conforming decoder execution. - Assert.Equal(126, visitedCount); - Assert.False(visited[126]); - Assert.False(visited[127]); - } - - /// - /// Verifies most- and least-probable arithmetic decisions against hand-calculated pinned-HM register vectors. - /// - [Fact] - public void DecisionDecodingMatchesPinnedHmVectors() - { - ReadOnlySpan mostProbableData = [0x00, 0x00, 0x00]; - - HevcCabacDecoder mostProbableDecoder = new(mostProbableData); - HevcCabacContext mostProbableContext = new(22, 154); - Assert.True(mostProbableDecoder.ReadDecision(ref mostProbableContext)); - Assert.Equal(3, GetPackedState(mostProbableContext)); - Assert.Equal(2, mostProbableDecoder.BytesConsumed); - - ReadOnlySpan leastProbableData = [0xFF, 0xFF, 0x00]; - - HevcCabacDecoder leastProbableDecoder = new(leastProbableData); - HevcCabacContext leastProbableContext = new(22, 154); - Assert.False(leastProbableDecoder.ReadDecision(ref leastProbableContext)); - Assert.Equal(0, GetPackedState(leastProbableContext)); - Assert.Equal(2, leastProbableDecoder.BytesConsumed); - } - - /// - /// Verifies aligned bypass extraction and terminating decisions against pinned-HM register vectors. - /// - [Fact] - public void BypassAndTerminationMatchPinnedHmVectors() - { - ReadOnlySpan bypassData = [0x2A, 0x80, 0xCC]; - - HevcCabacDecoder bypassDecoder = new(bypassData); - bypassDecoder.AlignBypass(); - Assert.Equal(0x55U, bypassDecoder.ReadBypassBits(8)); - Assert.Equal(3, bypassDecoder.BytesConsumed); - - ReadOnlySpan terminatingData = [0xFF, 0xFF]; - - HevcCabacDecoder terminatingDecoder = new(terminatingData); - Assert.True(terminatingDecoder.ReadTerminate()); - - ReadOnlySpan continuingData = [0x00, 0x00]; - - HevcCabacDecoder continuingDecoder = new(continuingData); - Assert.False(continuingDecoder.ReadTerminate()); - } - - /// - /// Verifies the required stop bit and zero padding after a terminating CABAC value. - /// - [Fact] - public void TerminationAlignmentRejectsInvalidPattern() - { - ReadOnlySpan validData = [0x00, 0x80]; - - HevcCabacDecoder validDecoder = new(validData); - validDecoder.ValidateTerminationAlignment(); - - Assert.Throws(ValidateInvalidTerminationAlignment); - } - - /// - /// Verifies that PCM samples begin after the terminating arithmetic bytes and that arithmetic decoding resumes after the raw payload. - /// - [Fact] - public void PcmPayloadSuspendsAndRestartsArithmeticDecoding() - { - ReadOnlySpan data = [0xFF, 0xFF, 0xAB, 0xFF, 0xFF]; - HevcCabacDecoder decoder = new(data); - - Assert.True(decoder.ReadPcmFlag()); - Assert.Equal((ushort)0xA, decoder.ReadPcmSample(4)); - Assert.Equal((ushort)0xB, decoder.ReadPcmSample(4)); - - decoder.RestartAfterPcm(); - - Assert.True(decoder.ReadTerminate()); - } - - /// - /// Verifies that a PCM sample cannot read beyond its bounded entropy substream. - /// - [Fact] - public void PcmPayloadRejectsTruncatedSample() - { - Assert.Throws(ReadTruncatedPcmSample); - } - - /// - /// Attempts to read a sample wider than the remaining raw PCM payload. - /// - private static void ReadTruncatedPcmSample() - { - ReadOnlySpan data = [0xFF, 0xFF, 0x80]; - HevcCabacDecoder decoder = new(data); - - Assert.True(decoder.ReadPcmFlag()); - decoder.ReadPcmSample(16); - } - - /// - /// Validates an entropy substream without the required termination stop bit. - /// - private static void ValidateInvalidTerminationAlignment() - { - ReadOnlySpan data = [0x00, 0x00]; - - HevcCabacDecoder decoder = new(data); - decoder.ValidateTerminationAlignment(); - } - - /// - /// Calculates the packed context state prescribed by the HEVC initialization equation. - /// - /// The luma quantization parameter. - /// The context initialization byte. - /// The probability-state index and most-probable symbol packed into one integer. - private static int GetInitializedPackedState(int quantizationParameter, byte initializationValue) - { - int clippedQuantizationParameter = Math.Clamp(quantizationParameter, 0, 51); - int slope = ((initializationValue >> 4) * 5) - 45; - int offset = ((initializationValue & 15) << 3) - 16; - int initializationState = Math.Clamp( - ((slope * clippedQuantizationParameter) >> 4) + offset, - 1, - 126); - - bool mostProbableSymbol = initializationState >= 64; - return ((mostProbableSymbol ? initializationState - 64 : 63 - initializationState) << 1) - + (mostProbableSymbol ? 1 : 0); - } - - /// - /// Packs a decoded context's observable probability state for table comparison. - /// - /// The context to inspect. - /// The probability-state index and most-probable symbol packed into one integer. - private static int GetPackedState(HevcCabacContext context) - => (context.StateIndex << 1) + (context.MostProbableSymbol ? 1 : 0); -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCoefficientDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCoefficientDecoderTests.cs deleted file mode 100644 index 299e84dfe..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcCoefficientDecoderTests.cs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Tests.Memory; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC coefficient scan selection, grouped ordering, and CABAC level reconstruction. -/// -public class HevcCoefficientDecoderTests -{ - /// - /// Verifies the three normative four-by-four coefficient scans. - /// - /// The scan direction under test. - /// The expected raster indices in scan order. - [Theory] - [MemberData(nameof(GetFourByFourScans))] - public void WritesFourByFourScan(int scanType, int[] expected) - { - int[] actual = new int[16]; - - int lastScanPosition = HevcCoefficientScanOrder.Write(actual, 4, 4, (HevcCoefficientScanType)scanType, expected[^1]); - - Assert.Equal(expected, actual); - Assert.Equal(15, lastScanPosition); - } - - /// - /// Verifies that grouped scans visit each coefficient exactly once for every supported transform geometry. - /// - /// The transform-block width. - /// The transform-block height. - /// The scan direction under test. - [Theory] - [InlineData(4, 4, HevcCoefficientScanType.Diagonal)] - [InlineData(8, 8, HevcCoefficientScanType.Diagonal)] - [InlineData(8, 4, HevcCoefficientScanType.Horizontal)] - [InlineData(4, 8, HevcCoefficientScanType.Vertical)] - [InlineData(16, 32, HevcCoefficientScanType.Diagonal)] - [InlineData(32, 16, HevcCoefficientScanType.Horizontal)] - [InlineData(32, 32, HevcCoefficientScanType.Vertical)] - public void GroupedScanVisitsEveryCoefficient(int width, int height, int scanType) - { - int coefficientCount = width * height; - int[] scan = new int[coefficientCount]; - bool[] visited = new bool[coefficientCount]; - - int lastScanPosition = HevcCoefficientScanOrder.Write(scan, width, height, (HevcCoefficientScanType)scanType, coefficientCount - 1); - - Assert.InRange(lastScanPosition, 0, coefficientCount - 1); - foreach (int rasterPosition in scan) - { - Assert.InRange(rasterPosition, 0, coefficientCount - 1); - Assert.False(visited[rasterPosition]); - visited[rasterPosition] = true; - } - - Assert.All(visited, Assert.True); - } - - /// - /// Verifies that an eight-by-eight diagonal scan groups coefficients in the normative group order. - /// - [Fact] - public void DiagonalEightByEightScanUsesGroupedOrder() - { - int[] scan = new int[64]; - - HevcCoefficientScanOrder.Write(scan, 8, 8, HevcCoefficientScanType.Diagonal, 63); - - Assert.Equal(0, scan[0]); - Assert.Equal(32, scan[16]); - Assert.Equal(4, scan[32]); - Assert.Equal(36, scan[48]); - } - - /// - /// Verifies transform geometry and intra direction select horizontal, vertical, or diagonal scans. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// Whether the containing coding unit uses intra prediction. - /// The effective intra mode. - /// The sequence chroma format. - /// The expected coefficient scan. - [Theory] - [InlineData(8, 8, HevcPlane.Y, false, 26, 3, HevcCoefficientScanType.Diagonal)] - [InlineData(8, 8, HevcPlane.Y, true, 26, 3, HevcCoefficientScanType.Horizontal)] - [InlineData(8, 8, HevcPlane.Y, true, 10, 3, HevcCoefficientScanType.Vertical)] - [InlineData(8, 8, HevcPlane.Y, true, 18, 3, HevcCoefficientScanType.Diagonal)] - [InlineData(16, 16, HevcPlane.Y, true, 26, 3, HevcCoefficientScanType.Diagonal)] - [InlineData(8, 8, HevcPlane.Cb, true, 26, 1, HevcCoefficientScanType.Diagonal)] - [InlineData(4, 4, HevcPlane.Cb, true, 26, 1, HevcCoefficientScanType.Horizontal)] - public void SelectsScanType( - int width, - int height, - int plane, - bool isIntra, - int mode, - byte chromaFormat, - int expected) - { - HevcCoefficientScanType actual = HevcCoefficientCodingParameters.SelectScanType( - width, - height, - (HevcPlane)plane, - isIntra, - mode, - chromaFormat, - false); - - Assert.Equal((HevcCoefficientScanType)expected, actual); - } - - /// - /// Verifies the significance-map context bases selected by transform size, scan, channel, and Range Extensions. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// The selected coefficient scan. - /// Whether the Range Extensions single-context mode applies. - /// The expected first significance-map context. - [Theory] - [InlineData(4, 4, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false, 0)] - [InlineData(8, 8, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false, 9)] - [InlineData(8, 8, HevcPlane.Y, HevcCoefficientScanType.Horizontal, false, 15)] - [InlineData(16, 16, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false, 21)] - [InlineData(8, 8, HevcPlane.Cb, HevcCoefficientScanType.Vertical, false, 9)] - [InlineData(16, 16, HevcPlane.Cr, HevcCoefficientScanType.Diagonal, false, 12)] - [InlineData(4, 4, HevcPlane.Y, HevcCoefficientScanType.Diagonal, true, 27)] - [InlineData(4, 4, HevcPlane.Cb, HevcCoefficientScanType.Diagonal, true, 15)] - public void SelectsFirstSignificanceContext( - int width, - int height, - int plane, - int scanType, - bool singleContext, - int expected) - { - HevcCoefficientCodingParameters parameters = CreateParameters( - width, - height, - (HevcPlane)plane, - (HevcCoefficientScanType)scanType, - singleContext); - - Assert.Equal(expected, parameters.FirstSignificanceMapContext); - } - - /// - /// Verifies the complete four-by-four raster-position context mapping. - /// - [Fact] - public void FourByFourSignificanceContextsMatchNormativeMap() - { - int[] expected = [0, 1, 4, 5, 2, 3, 4, 5, 6, 6, 8, 8, 7, 7, 8, 8]; - HevcCoefficientCodingParameters parameters = CreateParameters(4, 4, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false); - - for (int rasterPosition = 0; rasterPosition < expected.Length; rasterPosition++) - { - Assert.Equal(expected[rasterPosition], parameters.GetSignificantCoefficientContext(rasterPosition, 0)); - } - } - - /// - /// Verifies significant-group and coefficient contexts use already decoded right and lower groups. - /// - [Fact] - public void SignificanceContextsUseRightAndLowerGroups() - { - int[] groupFlags = [0, 1, 1, 0]; - HevcCoefficientCodingParameters luma = CreateParameters(8, 8, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false); - HevcCoefficientCodingParameters chroma = CreateParameters(8, 8, HevcPlane.Cb, HevcCoefficientScanType.Diagonal, false); - - Assert.Equal(1, luma.GetSignificantGroupContext(groupFlags, 0, 0)); - Assert.Equal(3, luma.GetSignificancePattern(groupFlags, 0, 0)); - Assert.Equal(0, luma.GetSignificantGroupContext(groupFlags, 1, 0)); - Assert.Equal(0, luma.GetSignificancePattern(groupFlags, 1, 0)); - Assert.Equal(0, luma.GetSignificantCoefficientContext(0, 3)); - Assert.Equal(11, luma.GetSignificantCoefficientContext(1, 3)); - Assert.Equal(14, luma.GetSignificantCoefficientContext(4, 0)); - Assert.Equal(11, chroma.GetSignificantCoefficientContext(4, 0)); - } - - /// - /// Verifies luma and chroma level-context sets track subset position and preceding greater-than-one state. - /// - [Fact] - public void SelectsLevelContextSets() - { - HevcCoefficientCodingParameters luma = CreateParameters(8, 8, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false); - HevcCoefficientCodingParameters chroma = CreateParameters(8, 8, HevcPlane.Cb, HevcCoefficientScanType.Diagonal, false); - - Assert.Equal(0, luma.GetLevelContextSet(0, false)); - Assert.Equal(1, luma.GetLevelContextSet(0, true)); - Assert.Equal(2, luma.GetLevelContextSet(1, false)); - Assert.Equal(3, luma.GetLevelContextSet(1, true)); - Assert.Equal(0, chroma.GetLevelContextSet(0, false)); - Assert.Equal(1, chroma.GetLevelContextSet(3, true)); - } - - /// - /// Verifies a fixed CABAC substream reconstructs one positive DC coefficient without allocating per block. - /// - [Fact] - public void DecodesPositiveDcCoefficientWithReusableScratch() - { - TestMemoryAllocator allocator = new(); - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = new() { MemoryAllocator = allocator }; - using HevcCoefficientDecoder decoder = new(configuration); - int[] coefficients = new int[16]; - HevcCoefficientCodingParameters parameters = CreateParameters(4, 4, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false); - - HevcCabacSyntaxReader firstReader = new([0xEE, 0x48], 22); - int firstNonZeroCount = decoder.Decode(ref firstReader, coefficients, in parameters); - HevcCabacSyntaxReader secondReader = new([0xEE, 0x48], 22); - int secondNonZeroCount = decoder.Decode(ref secondReader, coefficients, in parameters); - - Assert.Equal(1, firstNonZeroCount); - Assert.Equal(1, secondNonZeroCount); - Assert.Equal(1, coefficients[0]); - Assert.All(coefficients[1..], value => Assert.Equal(0, value)); - Assert.Single(allocator.AllocationLog); - } - - /// - /// Verifies the bypass-coded sign is applied to a fixed single-coefficient CABAC substream. - /// - [Fact] - public void DecodesNegativeDcCoefficient() - { - using HevcCoefficientDecoder decoder = new(Configuration.Default); - HevcCabacSyntaxReader reader = new([0xF4, 0x24], 22); - int[] coefficients = new int[16]; - HevcCoefficientCodingParameters parameters = CreateParameters(4, 4, HevcPlane.Y, HevcCoefficientScanType.Diagonal, false); - - int nonZeroCount = decoder.Decode(ref reader, coefficients, in parameters); - - Assert.Equal(1, nonZeroCount); - Assert.Equal(-1, coefficients[0]); - Assert.All(coefficients[1..], value => Assert.Equal(0, value)); - } - - /// - /// Gets the exact raster order for each four-by-four scan direction. - /// - /// The scan direction and expected raster positions. - public static TheoryData GetFourByFourScans() => - new() - { - { - (int)HevcCoefficientScanType.Diagonal, - [0, 4, 1, 8, 5, 2, 12, 9, 6, 3, 13, 10, 7, 14, 11, 15] - }, - { - (int)HevcCoefficientScanType.Horizontal, - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - }, - { - (int)HevcCoefficientScanType.Vertical, - [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15] - }, - }; - - /// - /// Creates explicit coefficient parameters for scan and entropy tests without requiring a parsed parameter set. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component. - /// The coefficient scan. - /// Whether the Range Extensions single significance context applies. - /// The coefficient coding parameters. - private static HevcCoefficientCodingParameters CreateParameters( - int width, - int height, - HevcPlane plane, - HevcCoefficientScanType scanType, - bool singleContext) - => new(width, height, plane, scanType, singleContext, false, false, false, false, 15, plane == HevcPlane.Y ? 0 : 2); -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraPredictorTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraPredictorTests.cs deleted file mode 100644 index 16b42e694..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraPredictorTests.cs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Tests.TestUtilities; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC planar, DC, angular, reference-filter, and SIMD prediction behavior. -/// -[Trait("Format", "Heic")] -public class HevcIntraPredictorTests -{ - /// - /// The hardware configurations required to exercise each SIMD tier and the complete scalar fallback. - /// - private const HwIntrinsics PredictorConfigurations = - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; - - /// - /// Verifies fixed four-by-four prediction results derived from the HEVC intra-prediction equations. - /// - [Fact] - public void PredictsKnownFourByFourBlocks() - { - ushort[] top = [64, 80, 96, 112, 128, 144, 160, 176, 192]; - ushort[] left = [64, 70, 76, 82, 88, 94, 100, 106, 112]; - int[] modes = [0, 1, 2, 9, 18, 30, 34]; - ushort[][] expected = - [ - [86, 101, 117, 132, 90, 103, 115, 128, 94, 104, 114, 123, 98, 105, 112, 119], - [84, 93, 97, 101, 88, 92, 92, 92, 90, 92, 92, 92, 91, 92, 92, 92], - [76, 82, 88, 94, 82, 88, 94, 100, 88, 94, 100, 106, 94, 100, 106, 112], - [70, 71, 71, 72, 76, 77, 77, 78, 82, 83, 83, 84, 88, 89, 89, 90], - [64, 80, 96, 112, 70, 64, 80, 96, 76, 70, 64, 80, 82, 76, 70, 64], - [87, 103, 119, 135, 93, 109, 125, 141, 100, 116, 132, 148, 106, 122, 138, 154], - [96, 112, 128, 144, 112, 128, 144, 160, 128, 144, 160, 176, 144, 160, 176, 192] - ]; - - const int size = 4; - const int stride = 7; - ushort[] destination = new ushort[stride * size]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetScratchLength(2)]; - for (int caseIndex = 0; caseIndex < modes.Length; caseIndex++) - { - destination.AsSpan().Fill(ushort.MaxValue); - int mode = modes[caseIndex]; - HevcIntraPredictor.Predict(top, left, destination, stride, 2, mode, 8, mode == 1, scratch); - - for (int y = 0; y < size; y++) - { - ReadOnlySpan expectedRow = expected[caseIndex].AsSpan(y * size, size); - ReadOnlySpan actualRow = destination.AsSpan(y * stride, size); - Assert.True(expectedRow.SequenceEqual(actualRow), $"Mode {mode}, row {y} did not match the fixed HEVC result."); - } - } - } - - /// - /// Verifies the optional luma boundary filter for the pure horizontal and vertical modes. - /// - [Fact] - public void FiltersPureDirectionPredictionEdges() - { - ushort[] top = [64, 80, 96, 112, 128, 144, 160, 176, 192]; - ushort[] left = [64, 70, 76, 82, 88, 94, 100, 106, 112]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetScratchLength(2)]; - ushort[] horizontal = new ushort[16]; - ushort[] vertical = new ushort[16]; - - HevcIntraPredictor.Predict(top, left, horizontal, 4, 2, 10, 8, true, scratch); - HevcIntraPredictor.Predict(top, left, vertical, 4, 2, 26, 8, true, scratch); - - ushort[] expectedHorizontal = [78, 86, 94, 102, 76, 76, 76, 76, 82, 82, 82, 82, 88, 88, 88, 88]; - ushort[] expectedVertical = [83, 96, 112, 128, 86, 96, 112, 128, 89, 96, 112, 128, 92, 96, 112, 128]; - Assert.True(expectedHorizontal.AsSpan().SequenceEqual(horizontal)); - Assert.True(expectedVertical.AsSpan().SequenceEqual(vertical)); - } - - /// - /// Verifies exact three-tap filtering, including the shared top-left sample. - /// - [Fact] - public void FiltersReferenceSamplesWithThreeTapKernel() - { - ushort[] top = [64, 80, 96, 112, 128, 144, 160, 176, 192]; - ushort[] left = [64, 70, 76, 82, 88, 94, 100, 106, 112]; - ushort[] filteredTop = new ushort[top.Length]; - ushort[] filteredLeft = new ushort[left.Length]; - - HevcIntraPredictor.FilterReferenceSamples(top, left, filteredTop, filteredLeft, 2, 8, true); - - ushort[] expectedTop = [70, 80, 96, 112, 128, 144, 160, 176, 192]; - ushort[] expectedLeft = [70, 70, 76, 82, 88, 94, 100, 106, 112]; - Assert.True(expectedTop.AsSpan().SequenceEqual(filteredTop)); - Assert.True(expectedLeft.AsSpan().SequenceEqual(filteredLeft)); - } - - /// - /// Verifies strong bilinear and normal three-tap reference filtering through every SIMD tier and the scalar fallback. - /// - [Fact] - public void ReferenceFiltersMatchScalarDefinitionsAcrossIntrinsicWidths() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateReferenceFilters, PredictorConfigurations); - - /// - /// Compares every prediction mode and block width with a specification-shaped scalar oracle through every SIMD tier. - /// - [Fact] - public void EveryModeMatchesScalarOracleAcrossIntrinsicWidths() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateEveryMode, PredictorConfigurations); - - /// - /// Verifies strong bilinear and normal three-tap reference filtering under the selected hardware configuration. - /// - private static void ValidateReferenceFilters() - { - const int size = 32; - ushort[] top = new ushort[(size * 2) + 1]; - ushort[] left = new ushort[top.Length]; - ushort[] filteredTop = new ushort[top.Length]; - ushort[] filteredLeft = new ushort[left.Length]; - for (int i = 0; i < top.Length; i++) - { - top[i] = (ushort)(100 + i + (i % 3)); - left[i] = (ushort)(100 + (2 * i) + (i % 5)); - } - - // Strong smoothing is selected from the endpoint/midpoint test, so keep those six values exactly bilinear - // while the remaining samples deliberately differ from the expected straight lines. - top[0] = left[0] = 100; - top[size] = 132; - top[size * 2] = 164; - left[size] = 164; - left[size * 2] = 228; - - HevcIntraPredictor.FilterReferenceSamples(top, left, filteredTop, filteredLeft, 5, 10, true); - - for (int i = 0; i < top.Length; i++) - { - Assert.Equal((ushort)(100 + i), filteredTop[i]); - Assert.Equal((ushort)(100 + (2 * i)), filteredLeft[i]); - } - - HevcIntraPredictor.FilterReferenceSamples(top, left, filteredTop, filteredLeft, 5, 10, false); - - Assert.Equal((ushort)((left[1] + (2 * top[0]) + top[1] + 2) >> 2), filteredTop[0]); - Assert.Equal(filteredTop[0], filteredLeft[0]); - for (int i = 1; i < top.Length - 1; i++) - { - Assert.Equal((ushort)((top[i - 1] + (2 * top[i]) + top[i + 1] + 2) >> 2), filteredTop[i]); - Assert.Equal((ushort)((left[i - 1] + (2 * left[i]) + left[i + 1] + 2) >> 2), filteredLeft[i]); - } - - Assert.Equal(top[^1], filteredTop[^1]); - Assert.Equal(left[^1], filteredLeft[^1]); - } - - /// - /// Compares every prediction mode and block width with the scalar oracle under the selected hardware configuration. - /// - private static void ValidateEveryMode() - { - ReadOnlySpan<(int Log2Size, int BitDepth)> cases = [(2, 8), (3, 10), (4, 12), (5, 12)]; - foreach ((int log2Size, int bitDepth) in cases) - { - int size = 1 << log2Size; - int maximum = (1 << bitDepth) - 1; - int referenceLength = (size * 2) + 1; - ushort[] top = new ushort[referenceLength]; - ushort[] left = new ushort[referenceLength]; - top[0] = left[0] = (ushort)(maximum / 3); - for (int i = 1; i < referenceLength; i++) - { - top[i] = (ushort)((top[0] + (37 * i) + (3 * size)) & maximum); - left[i] = (ushort)((left[0] + (53 * i) + (5 * size)) & maximum); - } - - int stride = size + 3; - ushort[] expected = new ushort[stride * size]; - ushort[] actual = new ushort[stride * size]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetScratchLength(log2Size)]; - for (int mode = 0; mode <= 34; mode++) - { - expected.AsSpan().Clear(); - actual.AsSpan().Clear(); - PredictScalar(top, left, expected, stride, size, mode, bitDepth, true); - HevcIntraPredictor.Predict(top, left, actual, stride, log2Size, mode, bitDepth, true, scratch); - Assert.True(expected.AsSpan().SequenceEqual(actual), $"Mode {mode}, size {size}, and bit depth {bitDepth} did not match the scalar oracle."); - } - } - } - - /// - /// Reconstructs one block directly from the HEVC planar, DC, and angular prediction equations. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The prediction mode. - /// The reconstructed component precision. - /// Whether the luma edge filter applies. - private static void PredictScalar( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode, - int bitDepth, - bool filterPredictionEdges) - { - if (mode == 0) - { - int shift = BitOperations.Log2((uint)size) + 1; - for (int y = 0; y < size; y++) - { - for (int x = 0; x < size; x++) - { - int horizontal = ((size - x - 1) * left[y + 1]) + ((x + 1) * top[size + 1]); - int vertical = ((size - y - 1) * top[x + 1]) + ((y + 1) * left[size + 1]); - destination[(y * destinationStride) + x] = (ushort)((horizontal + vertical + size) >> shift); - } - } - - return; - } - - if (mode == 1) - { - PredictDcScalar(top, left, destination, destinationStride, size, filterPredictionEdges); - return; - } - - if (mode == 10) - { - PredictHorizontalScalar(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); - return; - } - - if (mode == 26) - { - PredictVerticalScalar(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); - return; - } - - PredictAngularScalar(top, left, destination, destinationStride, size, mode); - } - - /// - /// Reconstructs a scalar DC block and its optional boundary filter. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// Whether the luma edge filter applies. - private static void PredictDcScalar( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - bool filterPredictionEdges) - { - int sum = 0; - for (int i = 1; i <= size; i++) - { - sum += top[i] + left[i]; - } - - ushort dc = (ushort)((sum + size) >> (BitOperations.Log2((uint)size) + 1)); - for (int y = 0; y < size; y++) - { - destination.Slice(y * destinationStride, size).Fill(dc); - } - - if (!filterPredictionEdges) - { - return; - } - - destination[0] = (ushort)((top[1] + left[1] + (2 * dc) + 2) >> 2); - for (int i = 1; i < size; i++) - { - destination[i] = (ushort)((top[i + 1] + (3 * dc) + 2) >> 2); - destination[i * destinationStride] = (ushort)((left[i + 1] + (3 * dc) + 2) >> 2); - } - } - - /// - /// Reconstructs scalar horizontal prediction and its optional boundary filter. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The reconstructed component precision. - /// Whether the luma edge filter applies. - private static void PredictHorizontalScalar( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int bitDepth, - bool filterPredictionEdges) - { - for (int y = 0; y < size; y++) - { - destination.Slice(y * destinationStride, size).Fill(left[y + 1]); - } - - if (filterPredictionEdges) - { - int maximum = (1 << bitDepth) - 1; - for (int x = 0; x < size; x++) - { - destination[x] = (ushort)Math.Clamp(destination[x] + ((top[x + 1] - top[0]) >> 1), 0, maximum); - } - } - } - - /// - /// Reconstructs scalar vertical prediction and its optional boundary filter. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The reconstructed component precision. - /// Whether the luma edge filter applies. - private static void PredictVerticalScalar( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int bitDepth, - bool filterPredictionEdges) - { - int maximum = (1 << bitDepth) - 1; - for (int y = 0; y < size; y++) - { - top.Slice(1, size).CopyTo(destination[(y * destinationStride)..]); - if (filterPredictionEdges) - { - int offset = y * destinationStride; - destination[offset] = (ushort)Math.Clamp(destination[offset] + ((left[y + 1] - left[0]) >> 1), 0, maximum); - } - } - } - - /// - /// Reconstructs a scalar angular block, including negative-reference extension and horizontal transposition. - /// - /// The top reference samples. - /// The left reference samples. - /// The destination block origin. - /// The destination row stride. - /// The square block side. - /// The angular prediction mode. - private static void PredictAngularScalar( - ReadOnlySpan top, - ReadOnlySpan left, - Span destination, - int destinationStride, - int size, - int mode) - { - ReadOnlySpan angles = [0, 2, 5, 9, 13, 17, 21, 26, 32]; - ReadOnlySpan inverseAngles = [0, 4096, 1638, 910, 630, 482, 390, 315, 256]; - bool vertical = mode >= 18; - int angleMode = vertical ? mode - 26 : 10 - mode; - int absoluteAngleMode = Math.Abs(angleMode); - int angle = angles[absoluteAngleMode] * Math.Sign(angleMode); - ReadOnlySpan main = vertical ? top : left; - ReadOnlySpan side = vertical ? left : top; - int mainOrigin = size * 2; - int[] extendedMain = new int[(4 * size) + 1]; - for (int i = 0; i < main.Length; i++) - { - extendedMain[mainOrigin + i] = main[i]; - } - - if (angle < 0) - { - int inverseAngleSum = 128; - for (int index = -1; index > ((size * angle) >> 5); index--) - { - inverseAngleSum += inverseAngles[absoluteAngleMode]; - extendedMain[mainOrigin + index] = side[inverseAngleSum >> 8]; - } - } - - ushort[] temporary = new ushort[size * size]; - for (int y = 0, deltaPosition = angle; y < size; y++, deltaPosition += angle) - { - int deltaInteger = deltaPosition >> 5; - int deltaFraction = deltaPosition & 31; - for (int x = 0; x < size; x++) - { - int index = mainOrigin + x + deltaInteger + 1; - temporary[(y * size) + x] = deltaFraction == 0 - ? (ushort)extendedMain[index] - : (ushort)(((extendedMain[index] * (32 - deltaFraction)) + (extendedMain[index + 1] * deltaFraction) + 16) >> 5); - } - } - - for (int y = 0; y < size; y++) - { - for (int x = 0; x < size; x++) - { - int sourceIndex = vertical ? (y * size) + x : (x * size) + y; - destination[(y * destinationStride) + x] = temporary[sourceIndex]; - } - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs deleted file mode 100644 index 9e4c20613..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC intra-reference collection, substitution, and filtering decisions. -/// -[Trait("Format", "Heic")] -public class HevcIntraReferenceSamplesTests -{ - /// - /// Verifies that a fully available reference border is copied directly from the reconstructed plane. - /// - [Fact] - public void PrepareReferenceSamplesCopiesCompleteBorder() - { - const int blockX = 8; - const int blockY = 8; - const int log2Size = 2; - const int unitSize = 2; - const int referenceLength = 9; - using HevcPictureBuffer picture = CreatePicture(32, 32, 10, 1); - bool[] availableUnits = new bool[9]; - availableUnits.AsSpan().Fill(true); - ushort[] top = new ushort[referenceLength]; - ushort[] left = new ushort[referenceLength]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; - - HevcIntraPredictor.PrepareReferenceSamples( - picture, - HevcPlane.Y, - blockX, - blockY, - log2Size, - unitSize, - unitSize, - availableUnits, - top, - left, - scratch); - - for (int i = 0; i < referenceLength; i++) - { - Assert.Equal(GetSample(blockX + i - 1, blockY - 1), top[i]); - Assert.Equal(GetSample(blockX - 1, blockY + i - 1), left[i]); - } - } - - /// - /// Verifies that a block without available neighbors receives the component midpoint. - /// - [Fact] - public void PrepareReferenceSamplesUsesMidpointWithoutNeighbors() - { - const int log2Size = 2; - const int unitSize = 2; - const ushort midpoint = 512; - using HevcPictureBuffer picture = CreatePicture(16, 16, 10, 1); - bool[] availableUnits = new bool[9]; - ushort[] top = new ushort[9]; - ushort[] left = new ushort[9]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; - - HevcIntraPredictor.PrepareReferenceSamples( - picture, - HevcPlane.Y, - 0, - 0, - log2Size, - unitSize, - unitSize, - availableUnits, - top, - left, - scratch); - - Assert.All(top, sample => Assert.Equal(midpoint, sample)); - Assert.All(left, sample => Assert.Equal(midpoint, sample)); - } - - /// - /// Verifies forward substitution across unavailable below-left, left, corner, top, and above-right units. - /// - [Fact] - public void PrepareReferenceSamplesSubstitutesPartialBorderInNormativeOrder() - { - const int log2Size = 2; - const int unitSize = 2; - using HevcPictureBuffer picture = CreatePicture(32, 32, 10, 1); - bool[] availableUnits = [false, true, false, true, false, false, true, false, false]; - ushort[] top = new ushort[9]; - ushort[] left = new ushort[9]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; - - HevcIntraPredictor.PrepareReferenceSamples( - picture, - HevcPlane.Y, - 8, - 8, - log2Size, - unitSize, - unitSize, - availableUnits, - top, - left, - scratch); - - ushort[] expectedTop = [807, 807, 807, 710, 711, 711, 711, 711, 711]; - ushort[] expectedLeft = [807, 807, 907, 1207, 1207, 1207, 1307, 1307, 1307]; - Assert.True(expectedTop.AsSpan().SequenceEqual(top)); - Assert.True(expectedLeft.AsSpan().SequenceEqual(left)); - } - - /// - /// Verifies asymmetric availability units used by horizontally subsampled 4:2:2 chroma planes. - /// - [Fact] - public void PrepareReferenceSamplesSupportsAsymmetricChromaUnits() - { - const int blockX = 16; - const int blockY = 16; - const int log2Size = 3; - const int unitWidth = 2; - const int unitHeight = 4; - const int referenceLength = 17; - using HevcPictureBuffer picture = CreatePicture(48, 48, 10, 2); - bool[] availableUnits = [true, false, false, false, false, true, false, false, false, false, false, false, false]; - ushort[] top = new ushort[referenceLength]; - ushort[] left = new ushort[referenceLength]; - ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitWidth)]; - - HevcIntraPredictor.PrepareReferenceSamples( - picture, - HevcPlane.Cb, - blockX, - blockY, - log2Size, - unitWidth, - unitHeight, - availableUnits, - top, - left, - scratch); - - ushort substitutedLeft = GetSample(blockX - 1, blockY + 12); - Assert.Equal(substitutedLeft, top[0]); - Assert.Equal(GetSample(blockX, blockY - 1), top[1]); - Assert.Equal(GetSample(blockX + 1, blockY - 1), top[2]); - for (int i = 3; i < referenceLength; i++) - { - Assert.Equal(top[2], top[i]); - } - - for (int i = 0; i <= 13; i++) - { - Assert.Equal(substitutedLeft, left[i]); - } - - Assert.Equal(GetSample(blockX - 1, blockY + 13), left[14]); - Assert.Equal(GetSample(blockX - 1, blockY + 14), left[15]); - Assert.Equal(GetSample(blockX - 1, blockY + 15), left[16]); - } - - /// - /// Verifies the mode, size, component, chroma-format, and sequence controls for reference filtering. - /// - [Fact] - public void ShouldFilterReferenceSamplesUsesHevcThresholds() - { - Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 0, 2, 1, false)); - Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 0, 3, 1, false)); - Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 1, 5, 1, false)); - Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 26, 5, 1, false)); - Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 2, 3, 1, false)); - Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Cb, 2, 3, 1, false)); - Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Cb, 2, 3, 3, false)); - Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 2, 3, 1, true)); - } - - /// - /// Creates a picture whose luma and chroma samples encode their source coordinates. - /// - /// The coded luma width. - /// The coded luma height. - /// The component precision. - /// The HEVC chroma-format identifier. - /// The initialized picture. - private static HevcPictureBuffer CreatePicture(int width, int height, int bitDepth, byte chromaFormat) - { - HevcPictureBuffer picture = new(Configuration.Default, width, height, bitDepth, bitDepth, chromaFormat, false); - FillPlane(picture, HevcPlane.Y); - if (chromaFormat != 0) - { - FillPlane(picture, HevcPlane.Cb); - FillPlane(picture, HevcPlane.Cr); - } - - return picture; - } - - /// - /// Fills one picture plane with coordinate-derived samples. - /// - /// The destination picture. - /// The destination plane. - private static void FillPlane(HevcPictureBuffer picture, HevcPlane plane) - { - for (int y = 0; y < picture.GetHeight(plane); y++) - { - Span row = picture.GetRowSpan(plane, y); - for (int x = 0; x < row.Length; x++) - { - row[x] = GetSample(x, y); - } - } - } - - /// - /// Gets the deterministic sample value for one plane coordinate. - /// - /// The sample X coordinate. - /// The sample Y coordinate. - /// The coordinate-derived sample. - private static ushort GetSample(int x, int y) => (ushort)((y * 100) + x); -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseQuantizerTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseQuantizerTests.cs deleted file mode 100644 index 9516a1323..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseQuantizerTests.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC inverse quantization, scaling-list expansion, clipping, and SIMD behavior. -/// -[Trait("Format", "Heic")] -public class HevcInverseQuantizerTests -{ - /// - /// Verifies fixed flat-scale results including signed rounding and both transform-range limits. - /// - [Fact] - public void FlatScaleMatchesFixedResults() - { - int[] quantized = [1, -1, 2, -2, int.MaxValue, int.MinValue, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0]; - int[] actual = new int[quantized.Length]; - HevcScalingList scalingList = new(); - - HevcInverseQuantizer.Dequantize(quantized, actual, 2, 8, 15, 0, false, scalingList, HevcPlane.Y, true, false, false); - - int[] expected = [20, -20, 40, -40, 32767, -32768, 60, -60, 0, 0, 0, 0, 0, 0, 0, 0]; - Assert.True(expected.AsSpan().SequenceEqual(actual)); - } - - /// - /// Verifies fixed nonuniform values from the default eight-by-eight intra-luma scaling matrix. - /// - [Fact] - public void DefaultEightByEightScalingMatrixMatchesFixedResults() - { - int[] quantized = new int[64]; - quantized.AsSpan().Fill(1); - int[] actual = new int[quantized.Length]; - HevcScalingList scalingList = new(); - - HevcInverseQuantizer.Dequantize(quantized, actual, 3, 8, 15, 0, true, scalingList, HevcPlane.Y, true, false, false); - - Assert.Equal(10, actual[0]); - Assert.Equal(11, actual[4]); - Assert.Equal(13, actual[6]); - Assert.Equal(72, actual[63]); - } - - /// - /// Compares SIMD inverse quantization with a scalar oracle across transform sizes, precisions, scaling modes, and shift directions. - /// - /// The base-two logarithm of the tested transform-block side. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The effective component quantization parameter. - /// Whether scaling lists are enabled. - /// Whether the transform is skipped. - /// Whether transform-skip precision is extended. - [Theory] - [InlineData(2, 8, 15, 0, false, false, false)] - [InlineData(2, 8, 15, 27, true, false, false)] - [InlineData(2, 10, 15, 39, true, true, false)] - [InlineData(2, 12, 18, 51, true, true, true)] - [InlineData(3, 10, 15, 45, true, false, false)] - [InlineData(3, 10, 15, 45, true, true, false)] - [InlineData(4, 12, 18, 63, true, false, false)] - [InlineData(5, 12, 18, 75, true, false, false)] - [InlineData(5, 12, 15, 51, false, false, false)] - public void DequantizeMatchesScalarOracle( - int log2Size, - int bitDepth, - int maxTransformDynamicRange, - int quantizationParameter, - bool scalingListEnabled, - bool transformSkip, - bool extendedPrecisionProcessingEnabled) - { - int size = 1 << log2Size; - int[] quantized = new int[size * size]; - for (int i = 0; i < quantized.Length; i++) - { - quantized[i] = (((i * 7919) + (size * 257)) & 131071) - 65536; - } - - HevcScalingList scalingList = new(); - int[] expected = new int[quantized.Length]; - int[] actual = new int[quantized.Length]; - DequantizeScalar( - quantized, - expected, - log2Size, - bitDepth, - maxTransformDynamicRange, - quantizationParameter, - scalingListEnabled, - scalingList, - HevcPlane.Y, - true, - transformSkip, - extendedPrecisionProcessingEnabled); - - HevcInverseQuantizer.Dequantize( - quantized, - actual, - log2Size, - bitDepth, - maxTransformDynamicRange, - quantizationParameter, - scalingListEnabled, - scalingList, - HevcPlane.Y, - true, - transformSkip, - extendedPrecisionProcessingEnabled); - - Assert.True(expected.AsSpan().SequenceEqual(actual), $"The {size}x{size} inverse quantizer did not match the scalar HEVC oracle."); - } - - /// - /// Applies the HEVC inverse-quantization equations directly for one complete transform block. - /// - /// The quantized coefficients. - /// The dequantized coefficients. - /// The base-two logarithm of the transform-block side. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The effective component quantization parameter. - /// Whether the sequence enables scaling matrices. - /// The effective picture scaling matrices. - /// The reconstructed color plane. - /// Whether the transform block belongs to an intra-predicted coding unit. - /// Whether the transform is skipped. - /// Whether transform-skip precision is extended. - private static void DequantizeScalar( - ReadOnlySpan source, - Span destination, - int log2Size, - int bitDepth, - int maxTransformDynamicRange, - int quantizationParameter, - bool scalingListEnabled, - HevcScalingList scalingList, - HevcPlane plane, - bool isIntraPredicted, - bool transformSkip, - bool extendedPrecisionProcessingEnabled) - { - ReadOnlySpan inverseScales = [40, 45, 51, 57, 64, 72]; - int size = 1 << log2Size; - int transformShift = maxTransformDynamicRange - bitDepth - log2Size; - if (transformSkip && extendedPrecisionProcessingEnabled) - { - transformShift = Math.Max(0, transformShift); - } - - int inverseScale = inverseScales[quantizationParameter % 6]; - bool useScalingList = scalingListEnabled && (!transformSkip || log2Size == 2); - int rightShift = 6 - (transformShift + (quantizationParameter / 6)) + (useScalingList ? 4 : 0); - int targetInputBitDepth = Math.Min(maxTransformDynamicRange + 1, 32 + rightShift - (useScalingList ? 15 : 7)); - int inputMinimum = -(1 << (targetInputBitDepth - 1)); - int inputMaximum = (1 << (targetInputBitDepth - 1)) - 1; - int outputMinimum = -(1 << maxTransformDynamicRange); - int outputMaximum = (1 << maxTransformDynamicRange) - 1; - int sizeId = log2Size - 2; - int matrixId = (isIntraPredicted ? 0 : 3) + (int)plane; - ReadOnlySpan matrix = scalingList.GetMatrix(sizeId, matrixId); - int ratio = Math.Max(1, size >> 3); - int matrixSide = Math.Min(size, 8); - byte dcCoefficient = scalingList.GetDcCoefficient(sizeId, matrixId); - - for (int y = 0; y < size; y++) - { - for (int x = 0; x < size; x++) - { - int coefficient = useScalingList - ? ratio > 1 && x == 0 && y == 0 - ? dcCoefficient - : matrix[((y / ratio) * matrixSide) + (x / ratio)] - : 1; - - int value = Math.Clamp(source[(y * size) + x], inputMinimum, inputMaximum) * inverseScale * coefficient; - value = rightShift > 0 ? (value + (1 << (rightShift - 1))) >> rightShift : value << -rightShift; - destination[(y * size) + x] = Math.Clamp(value, outputMinimum, outputMaximum); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseTransformerTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseTransformerTests.cs deleted file mode 100644 index 40265650f..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcInverseTransformerTests.cs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC inverse-DCT, inverse-DST, rectangular-block, dynamic-range, and reconstruction behavior. -/// -[Trait("Format", "Heic")] -public class HevcInverseTransformerTests -{ - /// - /// Verifies the two normative normalization stages with a pure DC coefficient and a strided prediction block. - /// - [Fact] - public void DctDcCoefficientProducesUniformReconstruction() - { - const int size = 4; - const int stride = 7; - int[] coefficients = new int[size * size]; - coefficients[0] = 1024; - ushort[] destination = new ushort[stride * size]; - destination.AsSpan().Fill(100); - int[] scratch = new int[HevcInverseTransformer.GetScratchLength(2, 2)]; - - HevcInverseTransformer.TransformAdd(coefficients, destination, stride, 2, 2, 8, 15, false, scratch); - - for (int y = 0; y < size; y++) - { - for (int x = 0; x < size; x++) - { - Assert.Equal((ushort)108, destination[(y * stride) + x]); - } - } - } - - /// - /// Verifies a hand-derived four-by-four inverse-DST result from a pure lowest-frequency coefficient. - /// - [Fact] - public void DstLowestFrequencyMatchesFixedResult() - { - int[] coefficients = new int[16]; - coefficients[0] = 1024; - int[] actual = new int[16]; - int[] scratch = new int[HevcInverseTransformer.GetScratchLength(2, 2)]; - int[] expected = - [ - 2, 3, 4, 5, - 3, 6, 8, 9, - 4, 8, 11, 12, - 5, 9, 12, 14 - ]; - - HevcInverseTransformer.Transform(coefficients, actual, 2, 2, 8, 15, true, scratch); - - Assert.True(expected.AsSpan().SequenceEqual(actual)); - } - - /// - /// Compares factorized SIMD transforms with a dense specification-shaped oracle across supported dimensions and precisions. - /// - /// The base-two logarithm of the tested block width. - /// The base-two logarithm of the tested block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// Whether the tested four-by-four block uses the inverse DST. - [Theory] - [InlineData(2, 2, 8, 15, false)] - [InlineData(2, 2, 8, 15, true)] - [InlineData(2, 3, 10, 15, false)] - [InlineData(3, 2, 10, 15, false)] - [InlineData(3, 3, 10, 15, false)] - [InlineData(4, 3, 12, 18, false)] - [InlineData(3, 4, 12, 18, false)] - [InlineData(5, 4, 12, 18, false)] - [InlineData(4, 5, 12, 18, false)] - [InlineData(5, 5, 12, 15, false)] - public void TransformMatchesDenseOracle( - int log2Width, - int log2Height, - int bitDepth, - int maxTransformDynamicRange, - bool useDiscreteSineTransform) - { - int width = 1 << log2Width; - int height = 1 << log2Height; - int sampleCount = width * height; - int[] coefficients = new int[sampleCount]; - int coefficientMaximum = (1 << maxTransformDynamicRange) - 1; - for (int i = 0; i < coefficients.Length; i++) - { - // Alternating values across the complete dequantized range exercise both intermediate clipping bounds - // as well as every frequency group used by the factorized transform. - int unit = (((i * 37) + (width * 11) + height) % 127) - 63; - coefficients[i] = (unit * coefficientMaximum) / 63; - } - - int[] expected = new int[sampleCount]; - int[] actual = new int[sampleCount]; - int[] oracleScratch = new int[sampleCount]; - int[] transformScratch = new int[HevcInverseTransformer.GetScratchLength(log2Width, log2Height)]; - TransformDenseOracle( - coefficients, - expected, - oracleScratch, - width, - height, - bitDepth, - maxTransformDynamicRange, - useDiscreteSineTransform); - - HevcInverseTransformer.Transform( - coefficients, - actual, - log2Width, - log2Height, - bitDepth, - maxTransformDynamicRange, - useDiscreteSineTransform, - transformScratch); - - Assert.True(expected.AsSpan().SequenceEqual(actual), $"The {width}x{height} transform at {bitDepth} bits did not match the dense HEVC oracle."); - } - - /// - /// Verifies that residual addition clips both negative and positive reconstruction overflow at the component range. - /// - [Fact] - public void TransformAddClipsToComponentRange() - { - const int size = 4; - int[] positiveCoefficients = new int[size * size]; - int[] negativeCoefficients = new int[size * size]; - positiveCoefficients[0] = 32767; - negativeCoefficients[0] = -32768; - ushort[] positive = new ushort[size * size]; - ushort[] negative = new ushort[size * size]; - positive.AsSpan().Fill(4090); - negative.AsSpan().Fill(5); - int[] scratch = new int[HevcInverseTransformer.GetScratchLength(2, 2)]; - - HevcInverseTransformer.TransformAdd(positiveCoefficients, positive, size, 2, 2, 12, 18, false, scratch); - HevcInverseTransformer.TransformAdd(negativeCoefficients, negative, size, 2, 2, 12, 18, false, scratch); - - Assert.All(positive, value => Assert.Equal((ushort)4095, value)); - Assert.All(negative, value => Assert.Equal((ushort)0, value)); - } - - /// - /// Verifies that SIMD residual reconstruction preserves each predicted lane for an extracted HEVC transform block. - /// - [Fact] - public void AddResidualMatchesExtractedHevcBlock() - { - int[] residual = [-6, 0, 2, 4, -9, -25, -15, -2]; - ushort[] destination = [154, 154, 154, 154, 154, 154, 154, 154]; - ushort[] expected = [148, 154, 156, 158, 145, 129, 139, 152]; - - HevcInverseTransformer.AddResidual(residual, destination, destination.Length, destination.Length, 1, 8); - - Assert.True(expected.AsSpan().SequenceEqual(destination)); - } - - /// - /// Applies both inverse-transform dimensions using direct matrix products and normative rounding points. - /// - /// The dequantized coefficient block. - /// The destination signed residual block. - /// The full-block intermediate buffer. - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// Whether both dimensions use the four-point inverse DST. - private static void TransformDenseOracle( - ReadOnlySpan coefficients, - Span residual, - Span intermediate, - int width, - int height, - int bitDepth, - int maxTransformDynamicRange, - bool useDiscreteSineTransform) - { - int dynamicMinimum = -(1 << maxTransformDynamicRange); - int dynamicMaximum = (1 << maxTransformDynamicRange) - 1; - for (int y = 0; y < height; y++) - { - for (int xFrequency = 0; xFrequency < width; xFrequency++) - { - int sum = 0; - for (int yFrequency = 0; yFrequency < height; yFrequency++) - { - sum += coefficients[(yFrequency * width) + xFrequency] * GetInverseCoefficient(height, yFrequency, y, useDiscreteSineTransform); - } - - intermediate[(y * width) + xFrequency] = Math.Clamp((sum + 64) >> 7, dynamicMinimum, dynamicMaximum); - } - } - - int secondShift = maxTransformDynamicRange + 5 - bitDepth; - int secondRounding = 1 << (secondShift - 1); - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - int sum = 0; - for (int xFrequency = 0; xFrequency < width; xFrequency++) - { - sum += intermediate[(y * width) + xFrequency] * GetInverseCoefficient(width, xFrequency, x, useDiscreteSineTransform); - } - - residual[(y * width) + x] = Math.Clamp((sum + secondRounding) >> secondShift, short.MinValue, short.MaxValue); - } - } - } - - /// - /// Gets one coefficient from the normative HEVC inverse-DCT or four-point inverse-DST matrix. - /// - /// The transform dimension. - /// The frequency-domain coordinate. - /// The spatial-domain coordinate. - /// Whether the four-point inverse DST is selected. - /// The signed matrix coefficient. - private static int GetInverseCoefficient(int size, int frequency, int position, bool useDiscreteSineTransform) - { - if (useDiscreteSineTransform) - { - ReadOnlySpan sine = - [ - 29, 55, 74, 84, - 74, 74, 0, -74, - 84, -29, -74, 55, - 55, -84, 74, -29 - ]; - - return sine[(frequency * 4) + position]; - } - - if (frequency == 0) - { - return 64; - } - - ReadOnlySpan magnitudes = - [ - 90, 90, 90, 90, 89, 88, 87, 85, 83, 82, 80, 78, 75, 73, 70, 67, 64, - 61, 57, 54, 50, 46, 43, 38, 36, 31, 25, 22, 18, 13, 9, 4, 0 - ]; - - int angle = (((2 * position) + 1) * frequency * (32 / size)) & 127; - if (angle > 64) - { - angle = 128 - angle; - } - - return angle > 32 ? -magnitudes[64 - angle] : magnitudes[angle]; - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs deleted file mode 100644 index a8a5f4012..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs +++ /dev/null @@ -1,1799 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers.Binary; -using System.Security.Cryptography; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Heif; -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests.Memory; -using SixLabors.ImageSharp.Tests.TestUtilities; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Validates complete HEVC still-picture reconstruction and decoder ownership against independent results. -/// -[Trait("Format", "Heif")] -public class HevcPictureDecoderTests -{ - /// - /// The hardware configurations required to exercise every SAO vector tier and the scalar fallback. - /// - private const HwIntrinsics LoopFilterConfigurations = - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; - - /// - /// Gets a complete prefix SEI RBSP carrying one unknown message followed by every supported metadata message. - /// The values use the exact field widths and G, B, R ordering read by pinned HM. - /// - private static ReadOnlySpan SupplementalMetadataRbsp => - [ - 0xFF, 0x2D, 0x01, 0x7A, - 0x89, 0x18, - 0x3A, 0x98, 0x75, 0x30, - 0x1D, 0x4C, 0x13, 0x88, - 0x7D, 0x00, 0x3E, 0x80, - 0x3D, 0x13, 0x40, 0x42, - 0x02, 0x03, 0x04, 0x05, - 0x01, 0x02, 0x03, 0x04, - 0x90, 0x04, 0x03, 0xE8, 0x01, 0x90, - 0x93, 0x01, 0x10, - 0x94, 0x08, 0x00, 0x0F, 0x42, 0x40, 0x3D, 0x13, 0x40, 0x42, - 0x95, 0x0D, 0x1C, 0x00, 0x3D, 0x09, 0x00, 0x01, 0x31, 0x2D, 0x00, 0x00, 0xB7, 0x1B, 0x02, - 0x80 - ]; - - /// - /// Gets a display-orientation prefix SEI RBSP that flips horizontally and then rotates a quarter turn - /// anticlockwise. - /// - private static ReadOnlySpan DisplayOrientationRbsp => - [ - 0x2F, 0x03, 0x48, 0x00, 0x08, - 0x80 - ]; - - /// - /// Identifies residual-tool signaling that an official independently decoded picture must exercise. - /// - [Flags] - public enum ResidualTools - { - /// - /// No optional residual tool is signaled. - /// - None = 0, - - /// - /// Coding-unit luma quantization deltas are signaled. - /// - DeltaQuantization = 1, - - /// - /// Non-flat quantization scaling matrices are enabled. - /// - ScalingLists = 2, - - /// - /// Transform skip is enabled. - /// - TransformSkip = 4, - - /// - /// Range-extension transform precision is enabled. - /// - ExtendedPrecision = 8, - - /// - /// Transform and quantization bypass is enabled. - /// - TransquantizationBypass = 16, - - /// - /// A coding-unit chroma quantization-offset list is enabled. - /// - ChromaQuantizationAdjustment = 32, - } - - /// - /// Identifies parallelization syntax that an official independently decoded picture must exercise. - /// - [Flags] - public enum ParallelizationTools - { - /// - /// No parallelization syntax is signaled. - /// - None = 0, - - /// - /// Dependent slice segments are signaled. - /// - DependentSliceSegments = 1, - - /// - /// Tile boundaries are signaled. - /// - Tiles = 2, - - /// - /// Wavefront row entry points are signaled. - /// - Wavefront = 4, - - /// - /// Slice-header extension bytes are signaled. - /// - SliceHeaderExtensions = 8, - - /// - /// One or more entropy entry points are signaled. - /// - EntryPoints = 16, - } - - /// - /// Identifies Range Extensions syntax that an official independently decoded picture must exercise. - /// - [Flags] - public enum RangeExtensionTools - { - /// - /// No additional tool assertion is required beyond the profile, precision, chroma layout, and plane hashes. - /// - None = 0, - - /// - /// Cross-component residual prediction is enabled. - /// - CrossComponentPrediction = 1, - - /// - /// Luma and chroma use different sample precisions. - /// - UnequalBitDepth = 2, - - /// - /// Persistent Golomb-Rice adaptation is enabled. - /// - PersistentRice = 4, - - /// - /// Extended-precision transform processing is enabled. - /// - ExtendedPrecision = 8, - - /// - /// Pulse-code-modulated coding units are enabled. - /// - Pcm = 16, - - /// - /// Transform-skip-specific coefficient contexts are enabled. - /// - TransformSkipContext = 32, - - /// - /// Tile entry points are enabled. - /// - Tiles = 64, - - /// - /// Wavefront row entry points are enabled. - /// - Wavefront = 128, - - /// - /// CABAC bypass bins are byte-aligned. - /// - CabacBypassAlignment = 256, - - /// - /// A coding-unit chroma quantization-offset list is enabled. - /// - ChromaQuantizationAdjustment = 512, - } - - /// - /// Verifies the first independently coded picture from official ITU RExt conformance streams against its - /// published decoded-picture hashes. - /// - /// The official Annex B conformance stream. - /// The signaled component precision. - /// The signaled HEVC chroma-format identifier. - /// The normative luma-plane MD5 digest. - /// The normative blue-difference-plane MD5 digest, when present. - /// The normative red-difference-plane MD5 digest, when present. - [Theory] - [InlineData(TestImages.Heif.General8BitMonochrome, 8, 0, "e5223be3da805fb96440dbf2bd170db0", null, null)] - [InlineData(TestImages.Heif.General8Bit420, 8, 1, "7d66d87736d627193acef745b3b7d014", "cecabae4dd685151d8de966ad01f01a8", "d069d15c457867a0a4fb9c0201eb0585")] - [InlineData(TestImages.Heif.General8Bit444, 8, 3, "2be0bad2e95b42a9f53138cd874db1c9", "f7d50f66757b468f438df47b7e6b4f36", "72d935e42f5e76aa04e0326c64f026dc")] - [InlineData(TestImages.Heif.General10Bit420, 10, 1, "9262fdf6a69587b8f1eed23c9026cb24", "cd81cc4b427565dc8c17761f2bd07c09", "0409bf573e03e2a6dd00b760b997a824")] - [InlineData(TestImages.Heif.General10Bit422, 10, 2, "4c0a0a1bf001ebf1dc440ccd9e0ae3ea", "bd35abc3f86ead4bd59e19403248ee5e", "8ce96a8885e10cda55e67eba25d9ec03")] - [InlineData(TestImages.Heif.General10Bit444, 10, 3, "d6293dfd466b7ed570beb56dee7823e3", "a82bf54ac3b2e996f40db77beff69b03", "d4f38dae50bbaa4087c7c1cf020d30a2")] - [InlineData(TestImages.Heif.General12BitMonochrome, 12, 0, "549ff2b94ede8d83bfdc64a34440817d", null, null)] - [InlineData(TestImages.Heif.General12Bit420, 12, 1, "346f709b5dfe5dd41f2ba1c70d072eb6", "6eee29326b96bb032a4a0ed822e4ba17", "59ef3982a4e0e9597d498a0d035a645a")] - [InlineData(TestImages.Heif.General12Bit422, 12, 2, "be9c8562410e42b2db985444bf8a448e", "c5d616f1ccf8b2e9f56e1bb1d3e23134", "676a9e1ad75cff3193fce58bdb2721cb")] - [InlineData(TestImages.Heif.General12Bit444, 12, 3, "057c9c3dd78c63b2689a159e21da1071", "4d0529c8e5755bb49d0ba0ec9a8e7e89", "9f5e9d559b0cf62440c2e05f141aa5d0")] - public void DecodeOfficialRangeExtensionsPictureMatchesPublishedDigest( - string path, - int bitDepth, - byte chromaFormat, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - byte[] expectedYuv = TestFile.Create($"{path[..^4]}_frame0.yuv").Bytes; - ConvertAnnexBStillPicture(annexB, bitDepth, bitDepth, chromaFormat, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcSliceSegmentHeader sliceHeader = bitstream.SliceSegments[0]; - HevcSequenceParameterSet sequenceParameterSet = sliceHeader.PictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, sliceHeader.PictureParameterSet); - - decoder.Decode(bitstream); - - Assert.True(sequenceParameterSet.SampleAdaptiveOffsetEnabled); - Assert.False(sliceHeader.DeblockingFilterDisabled); - Assert.Equal(bitDepth, decoder.Picture.BitDepthLuma); - Assert.Equal(chromaFormat, decoder.Picture.ChromaFormat); - int expectedLength = decoder.Picture.GetWidth(HevcPlane.Y) * decoder.Picture.GetHeight(HevcPlane.Y) * (bitDepth > 8 ? 2 : 1); - if (chromaFormat != 0) - { - int chromaLength = decoder.Picture.GetWidth(HevcPlane.Cb) * decoder.Picture.GetHeight(HevcPlane.Cb) * (bitDepth > 8 ? 2 : 1); - expectedLength += chromaLength * 2; - } - - Assert.Equal(expectedLength, expectedYuv.Length); - int referenceOffset = 0; - AssertCodedPlaneEqual(decoder.Picture, HevcPlane.Y, expectedYuv, ref referenceOffset); - if (chromaFormat != 0) - { - AssertCodedPlaneEqual(decoder.Picture, HevcPlane.Cb, expectedYuv, ref referenceOffset); - AssertCodedPlaneEqual(decoder.Picture, HevcPlane.Cr, expectedYuv, ref referenceOffset); - } - - Assert.Equal(expectedYuv.Length, referenceOffset); - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - if (chromaFormat != 0) - { - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - } - - /// - /// Verifies the remaining supported Range Extensions profiles and independently coded tools against normative - /// decoded-picture hashes or first pictures from published reference output. - /// - /// The provenance-preserving extracted Annex B picture. - /// The signaled luma precision. - /// The signaled chroma precision. - /// The signaled HEVC chroma-format identifier. - /// The signaled HEVC profile identifier. - /// The Range Extensions syntax that the retained picture must enable. - /// The normative luma-plane MD5 digest. - /// The normative blue-difference-plane MD5 digest. - /// The normative red-difference-plane MD5 digest. - [Theory] - [InlineData(TestImages.Heif.RangeExtensionChromaAngle422, 10, 10, 2, 4, RangeExtensionTools.None, "90085930ec5de68c8e0c95ed59ec0ce4", "a2053b886e4d2b26569472c4e65865ca", "82fc61a1450dc6df0b87d1e17c4d66db")] - [InlineData(TestImages.Heif.RangeExtensionCrossComponent8Bit444, 8, 8, 3, 4, RangeExtensionTools.CrossComponentPrediction, "ab4619c33dc1f67d332ec1945acab23f", "2cc70454f33f32f97c7ecf244d858398", "c54605014b2e9133d66867bf572c3ac6")] - [InlineData(TestImages.Heif.RangeExtensionCrossComponent10Bit444, 10, 10, 3, 4, RangeExtensionTools.CrossComponentPrediction, "daa07e34beaa525d416211025b9ee73d", "7ca36368377b0c02cdf53767ea43c36a", "d2a777f65e1fe6c2e638ae0d4f8ad884")] - [InlineData(TestImages.Heif.RangeExtensionCrossComponent12Bit444, 12, 12, 3, 4, RangeExtensionTools.CrossComponentPrediction, "7bed50ae49c39dd30e1085e7578d3555", "d2a06b27ddfba5fd2f14463f5d88e10d", "1a06a414d88ec6a7819a464021b1e935")] - [InlineData(TestImages.Heif.RangeExtensionLuma12Chroma8, 12, 8, 3, 4, RangeExtensionTools.UnequalBitDepth, "c8a92f6c83830232615157130460aa52", "1a88d067a709f8d4ee66e608edcf1454", "7582e448a0ca0ec91d84e86264048c58")] - [InlineData(TestImages.Heif.RangeExtensionLuma8Chroma12, 8, 12, 3, 4, RangeExtensionTools.UnequalBitDepth, "0f0b9d9ae27e541854f985d2b4f1cb8c", "a1834614ecd2d873cf748af0d182fccd", "cc733d65275087e6ad480f98150139e5")] - [InlineData(TestImages.Heif.ExtendedPrecision8Bit444, 8, 8, 3, 4, RangeExtensionTools.ExtendedPrecision, "abef53fa20dab9ab755b51b6eafac2a8", "eb3fb765be767658b515ce4260a011e9", "e9f741b608b8c2c8efa4607c5fee6664")] - [InlineData(TestImages.Heif.ExtendedPrecision10Bit444, 10, 10, 3, 4, RangeExtensionTools.ExtendedPrecision, "0a6955b11ede15c22279186dcef5f75e", "3de15052d5e92ffc8cc98db58ed3db5c", "bfe0b418ca96bafc86953d9e0065315b")] - [InlineData(TestImages.Heif.ExtendedPrecision12Bit444, 12, 12, 3, 4, RangeExtensionTools.ExtendedPrecision, "c8664d56d8391b236a8347397eb97a08", "08cd345d8798ac2714b2939462ac45e2", "5424bce4562f298e983302da697db849")] - [InlineData(TestImages.Heif.HighThroughputExtendedPrecision8Bit444, 8, 8, 3, 5, RangeExtensionTools.ExtendedPrecision, "ca77f9c29a2d25266d33d77bea0edc66", "473d1bd93cfebffa04f99cfe7e37eb0d", "8ab614af0526c77685ade3c9bbde2510")] - [InlineData(TestImages.Heif.HighThroughputExtendedPrecision10Bit444, 10, 10, 3, 5, RangeExtensionTools.ExtendedPrecision, "635507721877b619dc04593abafcfb1d", "d486efd70d9d3cbc3bcd2110abab6e87", "2b9966cd21c5b1fba41e3c793a40ebcb")] - [InlineData(TestImages.Heif.HighThroughputExtendedPrecision12Bit444, 12, 12, 3, 5, RangeExtensionTools.ExtendedPrecision, "cf547552a75b5e7b819937b82abf7985", "f2b863051a7c8f9af29bddd18401f291", "39a89074bc70d212dfe082ce544a43a0")] - [InlineData(TestImages.Heif.RangeExtensionPcm10Bit422, 10, 10, 2, 4, RangeExtensionTools.Pcm, "32248d41f772b2f69ae921d59b19f878", "678aec54e4b2af3578d06eefd8d6d097", "072613ebc1393dc2a5c17f882d1476f1")] - [InlineData(TestImages.Heif.RangeExtensionPcm12Bit444, 12, 12, 3, 4, RangeExtensionTools.Pcm, "0d91f6c4b691e47f48cf039b6c948d50", "5578d8774855b2a52f26cffda0edf10c", "e7860063b7b05ff55cf8ad981d08ee0f")] - [InlineData(TestImages.Heif.PersistentRice12Bit444, 12, 12, 3, 4, RangeExtensionTools.PersistentRice, "ddea37dedd9b541aaf371351d73e821b", "eb611e94654b76eb14fbcf20295a33ec", "b1655845f49114550fd26ab722ccc335")] - [InlineData(TestImages.Heif.TransformSkipContext8Bit444, 8, 8, 3, 4, RangeExtensionTools.TransformSkipContext, "c148a3f5610b0ec1369b702fc1383445", "82b10517c01f4fe5ca0aa19f5f8f2a0a", "29a841e63b5bf84fd9f8734dcc5df4e7")] - [InlineData(TestImages.Heif.TransformSkipContext10Bit444, 10, 10, 3, 4, RangeExtensionTools.TransformSkipContext, "006f3314efb25b15adc1c734fe7845b0", "99fa977f7603eeaa4981d9ac08b5aec1", "16aa20a082b534aec5e91bc9924060d3")] - [InlineData(TestImages.Heif.TransformSkipContext12Bit444, 12, 12, 3, 4, RangeExtensionTools.TransformSkipContext, "f26cffd119796bcbd2c4730eac6cc0c7", "e12a6a3faccadc8cf77952a7ef443c27", "f96fff6246848c9bd6808d65d1f3337d")] - [InlineData(TestImages.Heif.Main42210A, 10, 10, 2, 4, RangeExtensionTools.ChromaQuantizationAdjustment, "41746faffb59051c7b9002d7c79dc05d", "5180c8020b597bc66d0c1ec3a35656f6", "44140b531120e0737afc57fb25d16207")] - [InlineData(TestImages.Heif.Main42210B, 10, 10, 2, 4, RangeExtensionTools.ChromaQuantizationAdjustment, "845e930817261106c8edb352a1cdc9d6", "ca1e0aa4fd8ea6ba6ebacb3997bc32d1", "4377426b33fee59375d9dedd3b1eee9b")] - [InlineData(TestImages.Heif.HighThroughput10Bit422TilesWavefront, 10, 10, 2, 5, RangeExtensionTools.Tiles | RangeExtensionTools.Wavefront, "efd80653065c89989303827c56eca1ad", "7966084cebcd537109bf49084de31ced", "8030bdf42331b9a1033d0c62ce3ea452")] - [InlineData(TestImages.Heif.HighThroughput8Bit420TilesWavefront, 8, 8, 1, 5, RangeExtensionTools.Tiles | RangeExtensionTools.Wavefront, "6a99581bc96d002befb1bc6ef1c610aa", "79c51aa1ea347d81edcd7a2128e14060", "103dcfa2c88f4d98a24044a0e283cf87")] - [InlineData(TestImages.Heif.HighThroughput8Bit420Wavefront, 8, 8, 1, 5, RangeExtensionTools.Wavefront, "6b7404a197543d0adb7a2711b04273c2", "2f0e9ce1af96ff80ad2c5028d0109685", "a5d0b489441120fe235b7110f4fcaa45")] - [InlineData(TestImages.Heif.HighThroughput8Bit420CabacBypassAlignment, 8, 8, 1, 5, RangeExtensionTools.Tiles | RangeExtensionTools.Wavefront | RangeExtensionTools.CabacBypassAlignment, "6a783152cea8c612766d2173f2a30c5b", "9329df9e4ad589dfc24ac64acc9db1ea", "b01428a538cd555d61a9d3e950faf1cd")] - [InlineData(TestImages.Heif.HighThroughput8Bit420ExtendedPrecision, 8, 8, 1, 5, RangeExtensionTools.Tiles | RangeExtensionTools.Wavefront | RangeExtensionTools.ExtendedPrecision, "2e49cc92405a262f3b0065b764c96c7c", "f2f7073c45814147362ae156eadaaaa9", "b002b9b7a0da39301d112666ce4435d9")] - public void DecodeOfficialRangeExtensionProfilePictureMatchesDecodedPictureHash( - string path, - int bitDepthLuma, - int bitDepthChroma, - byte chromaFormat, - byte profileIdc, - RangeExtensionTools requiredTools, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, bitDepthLuma, bitDepthChroma, chromaFormat, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(profileIdc, sequenceParameterSet.ProfileTierLevel.ProfileIdc); - Assert.Equal(bitDepthLuma, decoder.Picture.BitDepthLuma); - Assert.Equal(bitDepthChroma, decoder.Picture.BitDepthChroma); - Assert.Equal(chromaFormat, decoder.Picture.ChromaFormat); - if ((requiredTools & RangeExtensionTools.CrossComponentPrediction) != 0) - { - Assert.True(pictureParameterSet.CrossComponentPredictionEnabled); - } - - if ((requiredTools & RangeExtensionTools.UnequalBitDepth) != 0) - { - Assert.NotEqual(decoder.Picture.BitDepthLuma, decoder.Picture.BitDepthChroma); - } - - if ((requiredTools & RangeExtensionTools.PersistentRice) != 0) - { - Assert.True(sequenceParameterSet.PersistentRiceAdaptationEnabled); - } - - if ((requiredTools & RangeExtensionTools.ExtendedPrecision) != 0) - { - Assert.True(sequenceParameterSet.ExtendedPrecisionProcessingEnabled); - } - - if ((requiredTools & RangeExtensionTools.Pcm) != 0) - { - Assert.True(sequenceParameterSet.PcmEnabled); - } - - if ((requiredTools & RangeExtensionTools.TransformSkipContext) != 0) - { - Assert.True(pictureParameterSet.TransformSkipEnabled); - Assert.True(sequenceParameterSet.TransformSkipContextEnabled); - } - - if ((requiredTools & RangeExtensionTools.Tiles) != 0) - { - Assert.True(pictureParameterSet.TilesEnabled); - } - - if ((requiredTools & RangeExtensionTools.Wavefront) != 0) - { - Assert.True(pictureParameterSet.EntropyCodingSynchronizationEnabled); - } - - if ((requiredTools & RangeExtensionTools.CabacBypassAlignment) != 0) - { - Assert.True(sequenceParameterSet.CabacBypassAlignmentEnabled); - } - - if ((requiredTools & RangeExtensionTools.ChromaQuantizationAdjustment) != 0) - { - Assert.NotEmpty(pictureParameterSet.ChromaQuantizationParameterOffsetsCb); - Assert.NotEmpty(pictureParameterSet.ChromaQuantizationParameterOffsetsCr); - } - - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies the official Main Still Picture stream containing every luma and chroma intra mode at every - /// conformance block size against its published native planar output. - /// - [Fact] - public void DecodeOfficialIntraPredictionPictureMatchesPublishedReference() - { - byte[] annexB = TestFile.Create(TestImages.Heif.IntraPredictionB).Bytes; - byte[] expectedYuv = TestFile.Create(TestImages.Heif.IntraPredictionBReference).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(3, configuration.GeneralProfileIdc); - Assert.Equal(1920, sequenceParameterSet.DisplayWidth); - Assert.Equal(1080, sequenceParameterSet.DisplayHeight); - Assert.True(sequenceParameterSet.StrongIntraSmoothingEnabled); - Assert.False(sequenceParameterSet.IntraSmoothingDisabled); - Assert.False(pictureParameterSet.ConstrainedIntraPredictionEnabled); - - int expectedLength = sequenceParameterSet.DisplayWidth * sequenceParameterSet.DisplayHeight; - int chromaWidth = GetDisplaySize(sequenceParameterSet.DisplayWidth, decoder.Picture.GetSubsamplingX(HevcPlane.Cb)); - int chromaHeight = GetDisplaySize(sequenceParameterSet.DisplayHeight, decoder.Picture.GetSubsamplingY(HevcPlane.Cb)); - expectedLength += 2 * chromaWidth * chromaHeight; - Assert.Equal(expectedLength, expectedYuv.Length); - - int offset = 0; - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Y, expectedYuv, ref offset); - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Cb, expectedYuv, ref offset); - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Cr, expectedYuv, ref offset); - Assert.Equal(expectedYuv.Length, offset); - } - - /// - /// Verifies the independently coded first picture of the official constrained-intra stream against its - /// decoded-picture hashes while the production PPS path retains the enabled constraint. - /// - [Fact] - public void DecodeOfficialConstrainedIntraPictureMatchesPublishedDigest() - { - byte[] annexB = TestFile.Create(TestImages.Heif.ConstrainedIntraPredictionA).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(1, configuration.GeneralProfileIdc); - Assert.Equal(416, sequenceParameterSet.DisplayWidth); - Assert.Equal(240, sequenceParameterSet.DisplayHeight); - Assert.True(pictureParameterSet.ConstrainedIntraPredictionEnabled); - Assert.Equal("69a20189e6bbb9c088e3adc967244ca1", GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal("26502d354bb123f54c20413f14360ddb", GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal("baafaef47a55ae2e876862b30b3bc720", GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies official independently coded residual-tool pictures against pinned-HM native-plane hashes. - /// - /// The official or provenance-preserving extracted Annex B picture. - /// The signaled component precision. - /// The signaled HEVC chroma-format identifier. - /// The residual tools that the retained picture signals. - /// The pinned-HM luma-plane digest. - /// The pinned-HM blue-difference-plane digest. - /// The pinned-HM red-difference-plane digest. - [Theory] - [InlineData(TestImages.Heif.DeltaQuantizationParameterA, 8, 1, ResidualTools.DeltaQuantization, "2b715c3517e40c00f296260fd0d591c6", "e261d9de5312cba7ac2e355a976ce062", "ca058a402db52ae33aacfcd8c73ae3c6")] - [InlineData(TestImages.Heif.QuantizationMatrixA, 8, 3, ResidualTools.DeltaQuantization | ResidualTools.ScalingLists | ResidualTools.TransformSkip, "6995cec045398044d9cb9668d01fe295", "970394f8a6df16378a39ef2e8fbad354", "1c76949b0ee61b9c1a7d99681a324419")] - [InlineData(TestImages.Heif.ExtendedPrecision12Bit444, 12, 3, ResidualTools.TransformSkip | ResidualTools.ExtendedPrecision, "c8664d56d8391b236a8347397eb97a08", "08cd345d8798ac2714b2939462ac45e2", "5424bce4562f298e983302da697db849")] - [InlineData(TestImages.Heif.ChromaQuantizationAdjustment12Bit444, 12, 3, ResidualTools.TransformSkip | ResidualTools.ChromaQuantizationAdjustment, "0279d9ab84612be260dbd3d4832369b1", "b7eec690a0e5685913ac59d8121ea3e9", "64d7b590e5666f9286204e7e43c6a330")] - [InlineData(TestImages.Heif.LosslessA, 8, 1, ResidualTools.DeltaQuantization | ResidualTools.TransformSkip | ResidualTools.TransquantizationBypass, "6d063ac9bc53ab53e142e300e668c32e", "b69a3e55ff000c0418b79471247ca73f", "1b7449f2f395578ead369f6abde7c4eb")] - public void DecodeOfficialResidualToolsPictureMatchesPinnedHmDigest( - string path, - int bitDepth, - byte chromaFormat, - ResidualTools expectedTools, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, bitDepth, bitDepth, chromaFormat, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(bitDepth, decoder.Picture.BitDepthLuma); - Assert.Equal(chromaFormat, decoder.Picture.ChromaFormat); - Assert.Equal((expectedTools & ResidualTools.DeltaQuantization) != 0, pictureParameterSet.CodingUnitQuantizationParameterDeltaEnabled); - Assert.Equal((expectedTools & ResidualTools.ScalingLists) != 0, sequenceParameterSet.ScalingListEnabled); - Assert.Equal((expectedTools & ResidualTools.TransformSkip) != 0, pictureParameterSet.TransformSkipEnabled); - Assert.Equal((expectedTools & ResidualTools.ExtendedPrecision) != 0, sequenceParameterSet.ExtendedPrecisionProcessingEnabled); - Assert.Equal((expectedTools & ResidualTools.TransquantizationBypass) != 0, pictureParameterSet.TransquantizationBypassEnabled); - Assert.Equal( - (expectedTools & ResidualTools.ChromaQuantizationAdjustment) != 0, - pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count != 0); - - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies deblocking and sample-adaptive-offset conformance streams against native-plane digests produced by - /// the pinned HM decoder from output that matches each archive's published checksum. - /// - /// The complete official Annex B conformance stream. - /// The signaled component precision. - /// The signaled HEVC chroma-format identifier. - /// Whether the sequence enables sample-adaptive offset filtering. - /// The first independently coded picture's displayed width. - /// The first independently coded picture's displayed height. - /// The pinned-HM luma-plane digest. - /// The pinned-HM blue-difference-plane digest. - /// The pinned-HM red-difference-plane digest. - [Theory] - [InlineData(TestImages.Heif.DeblockingA, 8, 1, false, 832, 480, "3ea2c2ef1f973345111480e7658908b3", "0390b32143b1a832a385f78229e7e574", "8388f3a8af827da46f1fb52941ec00ad")] - [InlineData(TestImages.Heif.DeblockingMain10, 10, 1, true, 176, 144, "184a72aab144cb474df3c1a289e8692d", "d30e750dd70cae163d00f441a75896fd", "d253c41f06228df215f4616febbad34f")] - [InlineData(TestImages.Heif.SampleAdaptiveOffsetA, 8, 1, true, 416, 240, "08723eb3fb41af96c87becc4f6973234", "230778eb7df0ebc009ca92e9697ec4d6", "e8e21ed380d2272dc38384ecd6515e53")] - [InlineData(TestImages.Heif.SampleAdaptiveOffsetRangeExtensions, 12, 3, true, 2560, 1600, "fb342158a61b6cb3174b99d2e1167d7d", "9ff5400aac0380474882acb903f51f89", "bb320be3c7905a5a220e0066a5edb991")] - public void DecodeOfficialLoopFilterPictureMatchesPinnedHmDigest( - string path, - int bitDepth, - byte chromaFormat, - bool sampleAdaptiveOffsetEnabled, - int expectedWidth, - int expectedHeight, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - => ValidateOfficialLoopFilterPicture( - path, - bitDepth, - chromaFormat, - sampleAdaptiveOffsetEnabled, - expectedWidth, - expectedHeight, - lumaDigest, - chromaBlueDigest, - chromaRedDigest); - - /// - /// Verifies all official loop-filter pictures through every available SIMD tier and the scalar fallback. - /// - [Fact] - public void DecodeOfficialLoopFilterPicturesMatchPinnedHmDigestsAcrossIntrinsicWidths() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateOfficialLoopFilterPictures, LoopFilterConfigurations); - - /// - /// Verifies sample-adaptive-offset reconstruction with split allocator groups and balanced final disposal. - /// - [Fact] - public void DecodeOfficialLoopFilterPictureWithConstrainedAllocatorMatchesPinnedHmDigest() - { - byte[] annexB = TestFile.Create(TestImages.Heif.SampleAdaptiveOffsetA).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration codecConfiguration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, codecConfiguration); - TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 2_048 }; - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - using (HevcPictureDecoder decoder = new(configuration, bitstream.SliceSegments[0].PictureParameterSet)) - { - decoder.Decode(bitstream); - - Assert.Equal("08723eb3fb41af96c87becc4f6973234", GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal("230778eb7df0ebc009ca92e9697ec4d6", GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal("e8e21ed380d2272dc38384ecd6515e53", GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - Assert.NotEmpty(allocator.AllocationLog); - AssertBalancedAllocations(allocator); - } - - /// - /// Verifies the official deblocking and sample-adaptive-offset pictures in the active intrinsic configuration. - /// - private static void ValidateOfficialLoopFilterPictures() - { - ValidateOfficialLoopFilterPicture( - TestImages.Heif.DeblockingA, - 8, - 1, - false, - 832, - 480, - "3ea2c2ef1f973345111480e7658908b3", - "0390b32143b1a832a385f78229e7e574", - "8388f3a8af827da46f1fb52941ec00ad"); - - ValidateOfficialLoopFilterPicture( - TestImages.Heif.DeblockingMain10, - 10, - 1, - true, - 176, - 144, - "184a72aab144cb474df3c1a289e8692d", - "d30e750dd70cae163d00f441a75896fd", - "d253c41f06228df215f4616febbad34f"); - - ValidateOfficialLoopFilterPicture( - TestImages.Heif.SampleAdaptiveOffsetA, - 8, - 1, - true, - 416, - 240, - "08723eb3fb41af96c87becc4f6973234", - "230778eb7df0ebc009ca92e9697ec4d6", - "e8e21ed380d2272dc38384ecd6515e53"); - - ValidateOfficialLoopFilterPicture( - TestImages.Heif.SampleAdaptiveOffsetRangeExtensions, - 12, - 3, - true, - 2560, - 1600, - "fb342158a61b6cb3174b99d2e1167d7d", - "9ff5400aac0380474882acb903f51f89", - "bb320be3c7905a5a220e0066a5edb991"); - } - - /// - /// Verifies one official loop-filter picture in the active intrinsic configuration. - /// - /// The complete official Annex B conformance stream. - /// The signaled component precision. - /// The signaled HEVC chroma-format identifier. - /// Whether the sequence enables sample-adaptive offset filtering. - /// The first independently coded picture's displayed width. - /// The first independently coded picture's displayed height. - /// The pinned-HM luma-plane digest. - /// The pinned-HM blue-difference-plane digest. - /// The pinned-HM red-difference-plane digest. - private static void ValidateOfficialLoopFilterPicture( - string path, - int bitDepth, - byte chromaFormat, - bool sampleAdaptiveOffsetEnabled, - int expectedWidth, - int expectedHeight, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, bitDepth, bitDepth, chromaFormat, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcSliceSegmentHeader sliceHeader = bitstream.SliceSegments[0]; - HevcSequenceParameterSet sequenceParameterSet = sliceHeader.PictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, sliceHeader.PictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(expectedWidth, decoder.Picture.Width); - Assert.Equal(expectedHeight, decoder.Picture.Height); - Assert.Equal(bitDepth, decoder.Picture.BitDepthLuma); - Assert.Equal(chromaFormat, decoder.Picture.ChromaFormat); - Assert.Equal(sampleAdaptiveOffsetEnabled, sequenceParameterSet.SampleAdaptiveOffsetEnabled); - Assert.False(sliceHeader.DeblockingFilterDisabled); - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies coding-tree and transform-tree conformance streams against native-plane digests produced by the - /// pinned HM decoder from output that matches each archive's published checksum. - /// - /// The official Annex B conformance stream. - /// The expected coding-tree-block size logarithm. - /// The expected minimum coding-block size logarithm. - /// The expected minimum transform-block size logarithm. - /// The expected internal intra transform-depth limit. - /// The reference luma-plane MD5 digest. - /// The reference blue-difference-plane MD5 digest. - /// The reference red-difference-plane MD5 digest. - [Theory] - [InlineData(TestImages.Heif.RqtA, 6, 3, 2, 1, "adf2bfab6de808840c82f48eee31a0a5", "4b29b1d2699b77e42a4e22fb0d70993e", "8ada6d784647329a4f0da232176914f4")] - [InlineData(TestImages.Heif.RqtB, 6, 3, 2, 2, "797f41be9a4d53b640332f9d03b1f304", "ef67e5ceff912f7aeb14f2e00ddd5cbf", "e2af5fa6f3c4bcc96dfa95cb8947a7db")] - [InlineData(TestImages.Heif.RqtC, 6, 3, 2, 3, "5d431346cd0b3f52846fc20fc0afdcc4", "ff9355b8cc72d77edbad6f5bd4938df1", "24f8aae8c00f418af487be29d2a5a126")] - [InlineData(TestImages.Heif.RqtD, 6, 3, 2, 4, "30138fa13664590d16355be8f7362eb2", "6198b3d1e990baadcaa533b4c44a5be2", "45f5427aec28b4f4655240812387607b")] - [InlineData(TestImages.Heif.RqtE, 6, 3, 2, 5, "e9e182380f3209ef877b75199b546c00", "f555fd054855dc5a1cb82cb8f3393b5a", "baed53765a5424fbc38aa541917d0625")] - [InlineData(TestImages.Heif.StructA, 4, 3, 2, 2, "bf47fb8ff96a225c2646c0539744ac93", "c105b60fd0e8740574fb76972bbc3b1a", "f9e2d8327da50734b54771df29b343bb")] - [InlineData(TestImages.Heif.StructB, 5, 4, 2, 2, "61e13729a4e3d6fd96f5002a501d1d60", "fee8312ceaccb1f254ef21449f104e4e", "dbe684d5fffbbc2f5ca90b205018060c")] - [InlineData(TestImages.Heif.TuSizeA, 6, 5, 4, 3, "17a84e6f516dbcb8810c7548bf6e216c", "edd3085f7a152d7ffaabae816f4942ac", "e8b47494064c1a730aca3c6ee23572c8")] - public void DecodeOfficialTraversalPictureMatchesPinnedHmDigest( - string path, - int expectedCodingTreeBlockLog2, - int expectedMinCodingBlockLog2, - int expectedMinTransformBlockLog2, - int expectedMaxTransformHierarchyDepthIntra, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcSequenceParameterSet sequenceParameterSet = bitstream.SliceSegments[0].PictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, bitstream.SliceSegments[0].PictureParameterSet); - - decoder.Decode(bitstream); - - Assert.Equal(8, decoder.Picture.BitDepthLuma); - Assert.Equal(1, decoder.Picture.ChromaFormat); - Assert.Equal(expectedCodingTreeBlockLog2, sequenceParameterSet.CodingTreeBlockLog2); - Assert.Equal(expectedMinCodingBlockLog2, sequenceParameterSet.MinCodingBlockLog2); - Assert.Equal(expectedMinTransformBlockLog2, sequenceParameterSet.MinTransformBlockLog2); - Assert.Equal(expectedMaxTransformHierarchyDepthIntra, sequenceParameterSet.MaxTransformHierarchyDepthIntra); - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies the complete sequential decoder path for HEVC tiles, wavefront entry points, dependent slice - /// segments, and slice-header extensions against native-plane digests from published or pinned-HM output. - /// - /// The official Annex B conformance stream. - /// The parallelization syntax that the retained picture signals. - /// The expected coding-tree-block size logarithm. - /// The expected picture width in coding-tree blocks. - /// The exact tile-column count, or zero when only a multi-tile assertion applies. - /// The exact tile-row count, or zero when only a multi-tile assertion applies. - /// The pinned-HM or published luma-plane MD5 digest. - /// The pinned-HM or published blue-difference-plane MD5 digest. - /// The pinned-HM or published red-difference-plane MD5 digest. - [Theory] - [InlineData(TestImages.Heif.DependentSlicesA, ParallelizationTools.DependentSliceSegments, 6, 30, 1, 1, "00dc01343ab9dc53c078344d7f77dab1", "899538536f2b327d84894c947f87bbc2", "a1bc8421c5a72ce2792b40847e95d438")] - [InlineData(TestImages.Heif.DependentSlicesB, ParallelizationTools.DependentSliceSegments | ParallelizationTools.Wavefront, 6, 30, 1, 1, "d048cfe1b7f0e6a6e3689733914caa19", "d7400a314011173564516b81407c3f42", "7bddfaa6d440f8490ec88b94fdbac706")] - [InlineData(TestImages.Heif.DependentSlicesC, ParallelizationTools.DependentSliceSegments | ParallelizationTools.Tiles, 6, 30, 0, 0, "a8c96c581d9de294a4fe798cede17817", "afb4cdebbbb31edfeab504d64c779895", "e593a80b1f17724f930728068993c06c")] - [InlineData(TestImages.Heif.TilesA, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 5, 5, "6828e4b27ab4fda31fe3b8bcdb3bccef", "1f899aff0a453d133de048232d3ee1c0", "87ca938e4a19cd289bdaa85023c704c5")] - [InlineData(TestImages.Heif.TilesB, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 5, 5, "aa44a1bf0f77f5a78e514eab3621aab2", "53a29305bb7b60dcd3a0082ce5aed9f4", "0e7ad4ea85eedf8fa06ac9b366323175")] - [InlineData(TestImages.Heif.WavefrontA, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 7, 1, 1, "69bd520cd6b017b49144275f1c3b498c", "33bb1c6216561f30fbefb89ce87c0956", "1f88fd804c87d8f5c8c72cee4043d140")] - [InlineData(TestImages.Heif.WavefrontB, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 5, 13, 1, 1, "ff78fcf56cf449c195708626a975e870", "b230844124f07aad4102aa21e2fc0f15", "e10c05f8c4007b14ddc6a7cf858f374e")] - [InlineData(TestImages.Heif.WavefrontC, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 4, 26, 1, 1, "d55877b038bbe2af6a4b35eeff27b26f", "4e1145cc891c295543407b6c62c7ad55", "8fa9c17216a9b02a65582c322206216b")] - [InlineData(TestImages.Heif.WavefrontD, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 1, 1, 1, "ab7c74b80340e5bde0858276f11a37ed", "4fe6b63cfe5656bf88912ccf9caeb85a", "3dbb149d5b90a49cf72d4e2d5fbb0511")] - [InlineData(TestImages.Heif.WavefrontE, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 2, 1, 1, "d2b8cd7d9e7baf4dd3e383ba8fef3c22", "315f19843d4e637dc41f964c4adff626", "07f619c6aedd51a04ba99d2bfc0ecb2c")] - [InlineData(TestImages.Heif.WavefrontF, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 3, 1, 1, "797335a8e293c6ce6dd87fde6f797f07", "77f74454394860093d4b18ae9ae793fe", "e99ff04282a3457a44685378008ff86a")] - [InlineData(TestImages.Heif.EntryPointsA, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 2, 2, "ea26d532556e6b71b369b41def35af93", "295a5552a8152035a1cae2405a690251", "1d81ff340ac8a37d75f7782ca140cae3")] - [InlineData(TestImages.Heif.EntryPointsC, ParallelizationTools.Wavefront | ParallelizationTools.EntryPoints, 6, 30, 1, 1, "81b087fcf7df2626c7592ec5d39ec1fc", "93bd06e1a216a388568c069ead14f98e", "f0446d71e2061a8c3a6281fede767d81")] - public void DecodeOfficialParallelizationPictureMatchesPublishedReference( - string path, - ParallelizationTools expectedTools, - int expectedCodingTreeBlockLog2, - int expectedCodingTreeBlockWidth, - int expectedTileColumns, - int expectedTileRows, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); - - decoder.Decode(bitstream); - - bool hasDependentSliceSegments = false; - bool hasEntryPoints = false; - foreach (HevcSliceSegmentHeader sliceSegment in bitstream.SliceSegments) - { - hasDependentSliceSegments |= sliceSegment.DependentSliceSegment; - hasEntryPoints |= sliceSegment.EntryPointOffsets.Count != 0; - } - - bool expectsTiles = (expectedTools & ParallelizationTools.Tiles) != 0; - bool expectsWavefront = (expectedTools & ParallelizationTools.Wavefront) != 0; - int codingTreeBlockSize = 1 << sequenceParameterSet.CodingTreeBlockLog2; - int codingTreeBlockWidth = (sequenceParameterSet.Width + codingTreeBlockSize - 1) / codingTreeBlockSize; - - Assert.Equal((expectedTools & ParallelizationTools.DependentSliceSegments) != 0, hasDependentSliceSegments); - Assert.Equal(expectsTiles, pictureParameterSet.TilesEnabled); - Assert.Equal(expectsWavefront, pictureParameterSet.EntropyCodingSynchronizationEnabled); - Assert.Equal((expectedTools & ParallelizationTools.SliceHeaderExtensions) != 0, pictureParameterSet.SliceSegmentHeaderExtensionPresent); - Assert.Equal((expectedTools & ParallelizationTools.EntryPoints) != 0, hasEntryPoints); - Assert.Equal(expectedCodingTreeBlockLog2, sequenceParameterSet.CodingTreeBlockLog2); - Assert.Equal(expectedCodingTreeBlockWidth, codingTreeBlockWidth); - - if (expectedTileColumns == 0) - { - Assert.True(pictureParameterSet.TileColumnWidths.Count > 1); - Assert.True(pictureParameterSet.TileRowHeights.Count > 1); - } - else - { - Assert.Equal(expectedTileColumns, pictureParameterSet.TileColumnWidths.Count); - Assert.Equal(expectedTileRows, pictureParameterSet.TileRowHeights.Count); - } - - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - /// - /// Verifies the dependent-slice tile and wavefront combinations through split allocator groups with balanced - /// final disposal. - /// - /// The official Annex B conformance stream. - /// The pinned-HM luma-plane MD5 digest. - /// The pinned-HM blue-difference-plane MD5 digest. - /// The pinned-HM red-difference-plane MD5 digest. - [Theory] - [InlineData(TestImages.Heif.DependentSlicesB, "d048cfe1b7f0e6a6e3689733914caa19", "d7400a314011173564516b81407c3f42", "7bddfaa6d440f8490ec88b94fdbac706")] - [InlineData(TestImages.Heif.DependentSlicesC, "a8c96c581d9de294a4fe798cede17817", "afb4cdebbbb31edfeab504d64c779895", "e593a80b1f17724f930728068993c06c")] - public void DecodeOfficialParallelizationPicturesWithConstrainedAllocatorMatchPinnedHmDigest( - string path, - string lumaDigest, - string chromaBlueDigest, - string chromaRedDigest) - { - byte[] annexB = TestFile.Create(path).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration codecConfiguration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, codecConfiguration); - TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 4_096 }; - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - using (HevcPictureDecoder decoder = new(configuration, bitstream.SliceSegments[0].PictureParameterSet)) - { - decoder.Decode(bitstream); - - Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); - Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); - Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); - } - - Assert.NotEmpty(allocator.AllocationLog); - AssertBalancedAllocations(allocator); - } - - /// - /// Verifies that entry-point byte lengths exclude emulation-prevention bytes from the decoded substream. - /// - [Fact] - public void EntryPointOffsetsExcludeEmulationPreventionBytes() - { - ReadOnlySpan preventionBytePositions = [3, 8, 14]; - - const int DecodedHeaderLength = 4; - int encodedHeaderLength = HevcSliceSegmentHeader.GetEncodedPayloadOffset( - DecodedHeaderLength, - preventionBytePositions); - - const int EncodedSubstreamLength = 11; - int decodedBoundary = HevcSliceSegmentHeader.GetDecodedPayloadOffset( - encodedHeaderLength + EncodedSubstreamLength, - preventionBytePositions); - - Assert.Equal(5, encodedHeaderLength); - Assert.Equal(13, decodedBoundary); - Assert.Equal(9, decodedBoundary - DecodedHeaderLength); - } - - /// - /// Verifies every supported prefix SEI payload against the field ordering and fixed-point units read by pinned HM. - /// - [Fact] - public void SupplementalEnhancementInformationReadsPinnedHmSyntax() - { - HevcSupplementalEnhancementInformation supplementalEnhancementInformation = new(); - supplementalEnhancementInformation.ReadPrefixNalUnit(SupplementalMetadataRbsp); - supplementalEnhancementInformation.ReadPrefixNalUnit(DisplayOrientationRbsp); - - Assert.True(supplementalEnhancementInformation.HasDisplayOrientation); - Assert.True(supplementalEnhancementInformation.HorizontalFlip); - Assert.False(supplementalEnhancementInformation.VerticalFlip); - Assert.Equal((ushort)16384, supplementalEnhancementInformation.AnticlockwiseRotation); - Assert.Equal((byte)CicpTransferCharacteristics.SmpteSt2084, supplementalEnhancementInformation.PreferredTransferCharacteristics); - - HeifContentLightLevel contentLightLevel = supplementalEnhancementInformation.ContentLightLevel.Value; - Assert.Equal((ushort)1000, contentLightLevel.MaximumContentLightLevel); - Assert.Equal((ushort)400, contentLightLevel.MaximumPictureAverageLightLevel); - - HeifMasteringDisplayColorVolume masteringDisplayColorVolume - = supplementalEnhancementInformation.MasteringDisplayColorVolume.Value; - - Assert.Equal(0.64F, masteringDisplayColorVolume.Primaries.R.X, 5); - Assert.Equal(0.32F, masteringDisplayColorVolume.Primaries.R.Y, 5); - Assert.Equal(3375.2069D, masteringDisplayColorVolume.MaximumLuminance, 4); - Assert.Equal(1690.906D, masteringDisplayColorVolume.MinimumLuminance, 4); - - HeifAmbientViewingEnvironment ambientViewingEnvironment - = supplementalEnhancementInformation.AmbientViewingEnvironment.Value; - - Assert.Equal(100D, ambientViewingEnvironment.Illuminance); - Assert.Equal(0.3127F, ambientViewingEnvironment.AmbientLight.X, 5); - Assert.Equal(0.329F, ambientViewingEnvironment.AmbientLight.Y, 5); - - HeifContentColorVolume contentColorVolume = supplementalEnhancementInformation.ContentColorVolume.Value; - Assert.Null(contentColorVolume.Primaries); - Assert.Equal(0.1D, contentColorVolume.MinimumLuminance.Value, 5); - Assert.Equal(0.5D, contentColorVolume.MaximumLuminance.Value, 5); - Assert.Equal(0.3D, contentColorVolume.AverageLuminance.Value, 5); - - ReadOnlySpan cancellationRbsp = - [ - 0x2F, 0x01, 0xC0, - 0x95, 0x01, 0xC0, - 0x80 - ]; - - supplementalEnhancementInformation.ReadPrefixNalUnit(cancellationRbsp); - - Assert.False(supplementalEnhancementInformation.HasDisplayOrientation); - Assert.Null(supplementalEnhancementInformation.ContentColorVolume); - Assert.NotNull(supplementalEnhancementInformation.ContentLightLevel); - } - - /// - /// Verifies malformed SEI framing and payload trailing bits fail at the bounded NAL boundary. - /// - [Fact] - public void SupplementalEnhancementInformationRejectsMalformedPayloads() - { - byte[] emptyRbsp = []; - byte[] truncatedHeader = [0xFF, 0x80]; - byte[] truncatedPayload = [0x90, 0x04, 0x03, 0xE8, 0x80]; - byte[] missingPayloadMarker = [0x2F, 0x03, 0x48, 0x00, 0x00, 0x80]; - - Assert.Throws( - () => new HevcSupplementalEnhancementInformation().ReadPrefixNalUnit(emptyRbsp)); - - Assert.Throws( - () => new HevcSupplementalEnhancementInformation().ReadPrefixNalUnit(truncatedHeader)); - - Assert.Throws( - () => new HevcSupplementalEnhancementInformation().ReadPrefixNalUnit(truncatedPayload)); - - Assert.Throws( - () => new HevcSupplementalEnhancementInformation().ReadPrefixNalUnit(missingPayloadMarker)); - } - - /// - /// Verifies HEVC item metadata and complete HEIF presentation under normal and scalar execution while every - /// constrained allocator group is returned exactly once. - /// - [Fact] - public void DecodeSupplementalPresentationAndMetadataAcrossIntrinsicWidths() - => FeatureTestRunner.RunWithHwIntrinsicsFeature( - ValidateSupplementalPresentationAndMetadata, - HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic); - - /// - /// Verifies no-display and conflicting item-property metadata are rejected by the complete item decoder. - /// - [Fact] - public void DecodeRejectsNonDisplayAndConflictingSupplementalMetadata() - { - byte[] annexB = TestFile.Create(TestImages.Heif.IntraPredictionB).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration codecConfiguration = new(configurationData); - HeifItem item = new(Heif4CharCode.Hvc1, 1) { HevcCodecConfiguration = codecConfiguration }; - HevcHeifItemDecoder itemDecoder = new(); - DecoderOptions options = new(); - ReadOnlySpan noDisplayRbsp = [0x87, 0x00, 0x80]; - - byte[] noDisplayItemData = PrependPrefixSeiNalUnit(itemData, noDisplayRbsp); - Assert.Throws(() => - { - using Image image = itemDecoder.DecodeItemData( - options, - item, - noDisplayItemData, - null, - TestContext.Current.CancellationToken); - }); - - item.ContentLightLevel = new HeifContentLightLevel(1, 2); - byte[] supplementalItemData = PrependPrefixSeiNalUnit(itemData, SupplementalMetadataRbsp); - Assert.Throws(() => - { - using Image image = itemDecoder.DecodeItemData( - options, - item, - supplementalItemData, - null, - TestContext.Current.CancellationToken); - }); - - int prefixNalLength = supplementalItemData.Length - itemData.Length; - byte[] postVclPrefixSeiItemData = new byte[supplementalItemData.Length]; - itemData.CopyTo(postVclPrefixSeiItemData, 0); - supplementalItemData.AsSpan(0, prefixNalLength).CopyTo(postVclPrefixSeiItemData.AsSpan(itemData.Length)); - Assert.Throws( - () => new HevcImageItemBitstream(postVclPrefixSeiItemData, codecConfiguration)); - } - - /// - /// Verifies all reconstructed samples from a real HEIC grid tile against the HM reference decoder. - /// - /// The exact HEVC decoder-configuration record associated with the item. - /// The exact HEVC item payload to decode. - /// The corresponding planar samples produced by HM. - [Theory] - [InlineData(TestImages.Heif.Image1TileHvcConfiguration, TestImages.Heif.Image1Tile1Payload, TestImages.Heif.Image1Tile1ReferenceYuv)] - [InlineData(TestImages.Heif.Image1TileHvcConfiguration, TestImages.Heif.Image1Tile2Payload, TestImages.Heif.Image1Tile2ReferenceYuv)] - [InlineData(TestImages.Heif.Image2TileHvcConfiguration, TestImages.Heif.Image2Tile1Payload, TestImages.Heif.Image2Tile1ReferenceYuv)] - [InlineData(TestImages.Heif.Image2TileHvcConfiguration, TestImages.Heif.Image2Tile7Payload, TestImages.Heif.Image2Tile7ReferenceYuv)] - [InlineData(TestImages.Heif.DwsampleTileHvcConfiguration, TestImages.Heif.DwsampleTilePayload, TestImages.Heif.DwsampleTileReferenceYuv)] - public void DecodeRealHeicTileMatchesHmReference(string configurationPath, string itemPath, string referencePath) - { - byte[] configurationData = TestFile.Create(configurationPath).Bytes; - byte[] itemData = TestFile.Create(itemPath).Bytes; - byte[] expectedYuv = TestFile.Create(referencePath).Bytes; - HevcCodecConfiguration configuration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, configuration); - HevcSequenceParameterSet sequenceParameterSet = bitstream.SliceSegments[0].PictureParameterSet.SequenceParameterSet; - using HevcPictureDecoder decoder = new(Configuration.Default, bitstream.SliceSegments[0].PictureParameterSet); - - decoder.Decode(bitstream); - - int expectedLength = sequenceParameterSet.DisplayWidth * sequenceParameterSet.DisplayHeight; - if (decoder.Picture.ChromaFormat != 0) - { - int chromaWidth = GetDisplaySize(sequenceParameterSet.DisplayWidth, decoder.Picture.GetSubsamplingX(HevcPlane.Cb)); - int chromaHeight = GetDisplaySize(sequenceParameterSet.DisplayHeight, decoder.Picture.GetSubsamplingY(HevcPlane.Cb)); - expectedLength += 2 * chromaWidth * chromaHeight; - } - - Assert.Equal(expectedLength, expectedYuv.Length); - int offset = 0; - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Y, expectedYuv, ref offset); - if (decoder.Picture.ChromaFormat != 0) - { - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Cb, expectedYuv, ref offset); - AssertPlaneEqual(decoder.Picture, sequenceParameterSet, HevcPlane.Cr, expectedYuv, ref offset); - } - - Assert.Equal(expectedYuv.Length, offset); - } - - /// - /// Verifies that every possible allocator failure during decoder construction releases all earlier owners. - /// - [Fact] - public void ConstructorFailureReleasesEveryEarlierAllocation() - { - byte[] configurationData = TestFile.Create(TestImages.Heif.Image1TileHvcConfiguration).Bytes; - byte[] itemData = TestFile.Create(TestImages.Heif.Image1Tile1Payload).Bytes; - HevcCodecConfiguration codecConfiguration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, codecConfiguration); - HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; - - FailingTestMemoryAllocator successfulAllocator = new(int.MaxValue); - Configuration successfulConfiguration = Configuration.Default.Clone(); - successfulConfiguration.MemoryAllocator = successfulAllocator; - using (new HevcPictureDecoder(successfulConfiguration, pictureParameterSet)) - { - } - - int allocationCount = successfulAllocator.AllocationAttemptCount; - Assert.True(allocationCount > 0); - AssertBalancedAllocations(successfulAllocator); - - for (int failureAllocationNumber = 1; failureAllocationNumber <= allocationCount; failureAllocationNumber++) - { - FailingTestMemoryAllocator allocator = new(failureAllocationNumber); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - - Assert.Throws( - () => new HevcPictureDecoder(configuration, pictureParameterSet)); - - Assert.Equal(failureAllocationNumber, allocator.AllocationAttemptCount); - Assert.Equal(failureAllocationNumber - 1, allocator.AllocationLog.Count); - AssertBalancedAllocations(allocator); - } - } - - /// - /// Verifies successful production reconstruction with split allocator groups and balanced final disposal. - /// - [Fact] - public void DecodeWithConstrainedAllocatorReleasesEveryAllocation() - { - byte[] configurationData = TestFile.Create(TestImages.Heif.Image1TileHvcConfiguration).Bytes; - byte[] itemData = TestFile.Create(TestImages.Heif.Image1Tile1Payload).Bytes; - HevcCodecConfiguration codecConfiguration = new(configurationData); - HevcImageItemBitstream bitstream = new(itemData, codecConfiguration); - TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 2_048 }; - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - using (HevcPictureDecoder decoder = new(configuration, bitstream.SliceSegments[0].PictureParameterSet)) - { - decoder.Decode(bitstream); - } - - Assert.NotEmpty(allocator.AllocationLog); - AssertBalancedAllocations(allocator); - } - - /// - /// Compares one decoded component plane with its planar reference samples. - /// - /// The decoded picture containing the component plane. - /// The coded and displayed picture geometry. - /// The component plane to compare. - /// The complete planar YUV reference. - /// The current reference offset, advanced past the compared plane. - private static void AssertPlaneEqual( - HevcPictureBuffer picture, - HevcSequenceParameterSet sequenceParameterSet, - HevcPlane plane, - ReadOnlySpan expected, - ref int offset) - { - int subsamplingX = picture.GetSubsamplingX(plane); - int subsamplingY = picture.GetSubsamplingY(plane); - int sourceX = sequenceParameterSet.ConformanceWindowLeftOffset >> subsamplingX; - int sourceY = sequenceParameterSet.ConformanceWindowTopOffset >> subsamplingY; - int width = GetDisplaySize(sequenceParameterSet.DisplayWidth, subsamplingX); - int height = GetDisplaySize(sequenceParameterSet.DisplayHeight, subsamplingY); - AssertPlaneEqual(picture, plane, sourceX, sourceY, width, height, expected, ref offset); - } - - /// - /// Compares one complete coded component plane with its planar reference samples. - /// - /// The decoded picture containing the component plane. - /// The component plane to compare. - /// The complete planar YUV reference. - /// The current reference offset, advanced past the compared plane. - private static void AssertCodedPlaneEqual(HevcPictureBuffer picture, HevcPlane plane, ReadOnlySpan expected, ref int offset) - => AssertPlaneEqual(picture, plane, 0, 0, picture.GetWidth(plane), picture.GetHeight(plane), expected, ref offset); - - /// - /// Compares one rectangular component region with its planar reference samples. - /// - /// The decoded picture containing the component plane. - /// The component plane to compare. - /// The source-region X coordinate in component samples. - /// The source-region Y coordinate in component samples. - /// The compared width in component samples. - /// The compared height in component samples. - /// The complete planar YUV reference. - /// The current reference offset, advanced past the compared plane. - private static void AssertPlaneEqual( - HevcPictureBuffer picture, - HevcPlane plane, - int sourceX, - int sourceY, - int width, - int height, - ReadOnlySpan expected, - ref int offset) - { - bool usesHighBitDepthSamples = picture.GetBitDepth(plane) > 8; - int mismatchCount = 0; - int maximumDifference = 0; - int firstMismatchX = 0; - int firstMismatchY = 0; - int minimumMismatchX = width; - int minimumMismatchY = height; - int maximumMismatchX = 0; - int maximumMismatchY = 0; - ushort firstActual = 0; - ushort firstExpected = 0; - for (int y = 0; y < height; y++) - { - Span actualRow = picture.GetRowSpan(plane, sourceY + y).Slice(sourceX, width); - for (int x = 0; x < width; x++) - { - ushort expectedSample; - if (usesHighBitDepthSamples) - { - expectedSample = BinaryPrimitives.ReadUInt16LittleEndian(expected[offset..]); - offset += 2; - } - else - { - expectedSample = expected[offset++]; - } - - int difference = Math.Abs(actualRow[x] - expectedSample); - if (difference == 0) - { - continue; - } - - if (mismatchCount == 0) - { - firstMismatchX = x; - firstMismatchY = y; - firstActual = actualRow[x]; - firstExpected = expectedSample; - } - - mismatchCount++; - maximumDifference = Math.Max(maximumDifference, difference); - minimumMismatchX = Math.Min(minimumMismatchX, x); - minimumMismatchY = Math.Min(minimumMismatchY, y); - maximumMismatchX = Math.Max(maximumMismatchX, x); - maximumMismatchY = Math.Max(maximumMismatchY, y); - } - } - - string message = - $"{plane} contained {mismatchCount} differing samples. The maximum difference was {maximumDifference}; " + - $"the mismatches span ({minimumMismatchX}, {minimumMismatchY}) through ({maximumMismatchX}, {maximumMismatchY}), " + - $"and the first mismatch at ({firstMismatchX}, {firstMismatchY}) was {firstActual}, expected {firstExpected}."; - - Assert.True(mismatchCount == 0, message); - } - - /// - /// Converts a luma display extent to the selected component extent. - /// - /// The displayed luma extent. - /// The component subsampling shift. - /// The displayed component extent. - private static int GetDisplaySize(int lumaSize, int subsampling) => (lumaSize + (1 << subsampling) - 1) >> subsampling; - - /// - /// Verifies HEVC item metadata and the complete public HEIF presentation path in the active intrinsic - /// configuration. - /// - private static void ValidateSupplementalPresentationAndMetadata() - { - TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 8_192 }; - allocator.EnableNonThreadSafeLogging(); - Configuration configuration = Configuration.Default.Clone(); - configuration.MemoryAllocator = allocator; - DecoderOptions options = new() { Configuration = configuration }; - - // FeatureTestRunner executes this method outside the originating xUnit context when it disables - // intrinsics, so the remote process cannot obtain the test's cancellation token. - CancellationToken cancellationToken = CancellationToken.None; - byte[] annexB = TestFile.Create(TestImages.Heif.IntraPredictionB).Bytes; - ConvertAnnexBStillPicture(annexB, 8, 8, 1, out byte[] configurationData, out byte[] itemData); - HevcCodecConfiguration codecConfiguration = new(configurationData); - HeifItem item = new(Heif4CharCode.Hvc1, 1) { HevcCodecConfiguration = codecConfiguration }; - byte[] supplementalItemData = PrependPrefixSeiNalUnit(itemData, SupplementalMetadataRbsp); - HevcHeifItemDecoder itemDecoder = new(); - using (Image metadataImage = itemDecoder.DecodeItemData( - options, - item, - supplementalItemData, - null, - cancellationToken)) - { - Assert.Equal(CicpTransferCharacteristics.SmpteSt2084, metadataImage.Metadata.CicpProfile.TransferCharacteristics); - HeifMetadata metadata = metadataImage.Metadata.GetHeifMetadata(); - Assert.Equal((ushort)1000, metadata.ContentLightLevel.Value.MaximumContentLightLevel); - Assert.NotNull(metadata.MasteringDisplayColorVolume); - Assert.NotNull(metadata.ContentColorVolume); - Assert.NotNull(metadata.AmbientViewingEnvironment); - } - - byte[] source = [.. TestFile.Create(TestImages.Heif.Image4).Bytes]; - byte[] orientedContainer = InsertPrimaryItemPrefixSeiNalUnit(source, DisplayOrientationRbsp); - string referencePath = Path.Combine( - TestEnvironment.ReferenceOutputDirectoryFullPath, - "HeifDecoderTests", - "DecodeHevcStillImage_Rgba32_image4.png"); - - using (Image baseline = Image.Load(referencePath)) - using (Image actual = Image.Load(options, orientedContainer)) - { - Assert.Equal(baseline.Height, actual.Width); - Assert.Equal(baseline.Width, actual.Height); - ImageFrame baselineFrame = baseline.Frames.RootFrame; - ImageFrame actualFrame = actual.Frames.RootFrame; - - // A horizontal flip followed by the signaled anticlockwise quarter turn is an exact transpose. Compare - // every RGBA sample directly so both color and auxiliary alpha must share the production transform. - for (int y = 0; y < actual.Height; y++) - { - ReadOnlySpan actualRow = actualFrame.DangerousGetPixelRowMemory(y).Span; - for (int x = 0; x < actual.Width; x++) - { - Assert.Equal(baselineFrame.DangerousGetPixelRowMemory(x).Span[y], actualRow[x]); - } - } - } - - Assert.NotEmpty(allocator.AllocationLog); - AssertBalancedAllocations(allocator); - } - - /// - /// Inserts one prefix SEI NAL unit at the start of the real fixture's primary file-relative extent. - /// - private static byte[] InsertPrimaryItemPrefixSeiNalUnit(byte[] container, ReadOnlySpan rbsp) - { - int metaOffset = FindBoxOffset(container, Heif4CharCode.Meta, 0, container.Length); - Assert.True(metaOffset >= 0); - int metaSize = (int)BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(metaOffset)); - int primaryItemOffset = FindBoxOffset(container, Heif4CharCode.Pitm, metaOffset + 12, metaSize - 12); - int itemLocationOffset = FindBoxOffset(container, Heif4CharCode.Iloc, metaOffset + 12, metaSize - 12); - int mediaDataOffset = FindBoxOffset(container, Heif4CharCode.Mdat, 0, container.Length); - Assert.True(primaryItemOffset >= 0); - Assert.True(itemLocationOffset >= 0); - Assert.True(mediaDataOffset >= 0); - - Assert.Equal(0, container[primaryItemOffset + 8]); - ushort primaryItemId = BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(primaryItemOffset + 12)); - - int position = itemLocationOffset + 8; - Assert.Equal(0, container[position]); - position += 4; - byte fieldSizes = container[position++]; - byte baseOffsetSizes = container[position++]; - Assert.Equal(4, fieldSizes >> 4); - Assert.Equal(4, fieldSizes & 15); - Assert.Equal(0, baseOffsetSizes >> 4); - ushort itemCount = BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(position)); - position += 2; - Assert.True(itemCount > 0); - - ushort firstItemId = BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(position)); - position += 2; - Assert.Equal(primaryItemId, firstItemId); - Assert.Equal(0, BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(position))); - position += 2; - ushort primaryExtentCount = BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(position)); - position += 2; - Assert.Equal(1, primaryExtentCount); - - int primaryOffsetField = position; - int insertionOffset = (int)BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(position)); - position += 4; - int primaryLengthField = position; - uint primaryLength = BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(position)); - position += 4; - - byte[] prefixNalUnit = PrependPrefixSeiNalUnit([], rbsp); - BinaryPrimitives.WriteUInt32BigEndian( - container.AsSpan(primaryLengthField), - checked(primaryLength + (uint)prefixNalUnit.Length)); - - // The fixture stores every extent as an absolute file offset. Inserting into the first extent shifts only - // later extents; its own offset remains the exact start at which the prefix NAL is inserted. - for (int itemIndex = 1; itemIndex < itemCount; itemIndex++) - { - position += 4; - ushort extentCount = BinaryPrimitives.ReadUInt16BigEndian(container.AsSpan(position)); - position += 2; - for (int extentIndex = 0; extentIndex < extentCount; extentIndex++) - { - int extentOffsetField = position; - uint extentOffset = BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(position)); - position += 8; - if (extentOffset > insertionOffset) - { - BinaryPrimitives.WriteUInt32BigEndian( - container.AsSpan(extentOffsetField), - checked(extentOffset + (uint)prefixNalUnit.Length)); - } - } - } - - Assert.Equal( - (uint)insertionOffset, - BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(primaryOffsetField))); - - uint compactMediaDataSize = BinaryPrimitives.ReadUInt32BigEndian(container.AsSpan(mediaDataOffset)); - ulong mediaDataSize = compactMediaDataSize == 1 - ? BinaryPrimitives.ReadUInt64BigEndian(container.AsSpan(mediaDataOffset + 8)) - : compactMediaDataSize; - - if (compactMediaDataSize == 1) - { - BinaryPrimitives.WriteUInt64BigEndian( - container.AsSpan(mediaDataOffset + 8), - checked(mediaDataSize + (uint)prefixNalUnit.Length)); - } - else - { - BinaryPrimitives.WriteUInt32BigEndian( - container.AsSpan(mediaDataOffset), - checked((uint)mediaDataSize + (uint)prefixNalUnit.Length)); - } - - byte[] result = new byte[container.Length + prefixNalUnit.Length]; - container.AsSpan(0, insertionOffset).CopyTo(result); - prefixNalUnit.CopyTo(result.AsSpan(insertionOffset)); - container.AsSpan(insertionOffset).CopyTo(result.AsSpan(insertionOffset + prefixNalUnit.Length)); - return result; - } - - /// - /// Finds a bounded ISO BMFF child box, including boxes that use a 64-bit extended size. - /// - private static int FindBoxOffset(ReadOnlySpan data, Heif4CharCode type, int offset, int length) - { - int endOffset = offset + length; - while (offset < endOffset) - { - uint compactSize = BinaryPrimitives.ReadUInt32BigEndian(data[offset..]); - ulong boxSize = compactSize == 1 - ? BinaryPrimitives.ReadUInt64BigEndian(data[(offset + 8)..]) - : compactSize; - - Assert.InRange(boxSize, 8UL, (ulong)(endOffset - offset)); - Heif4CharCode boxType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(data[(offset + 4)..]); - if (boxType == type) - { - return offset; - } - - offset += (int)boxSize; - } - - return -1; - } - - /// - /// Prepends one valid prefix SEI NAL unit to a length-delimited HEVC item payload. - /// - private static byte[] PrependPrefixSeiNalUnit(ReadOnlySpan itemData, ReadOnlySpan rbsp) - { - int preventionByteCount = 0; - int consecutiveZeroBytes = 0; - foreach (byte value in rbsp) - { - if (consecutiveZeroBytes == 2 && value <= 3) - { - preventionByteCount++; - consecutiveZeroBytes = 0; - } - - consecutiveZeroBytes = value == 0 ? consecutiveZeroBytes + 1 : 0; - } - - const int lengthFieldLength = 4; - const int nalHeaderLength = 2; - int nalLength = nalHeaderLength + rbsp.Length + preventionByteCount; - byte[] result = new byte[lengthFieldLength + nalLength + itemData.Length]; - BinaryPrimitives.WriteUInt32BigEndian(result, (uint)nalLength); - result[lengthFieldLength] = 0x4E; - result[lengthFieldLength + 1] = 0x01; - int destinationOffset = lengthFieldLength + nalHeaderLength; - consecutiveZeroBytes = 0; - foreach (byte value in rbsp) - { - if (consecutiveZeroBytes == 2 && value <= 3) - { - result[destinationOffset++] = 3; - consecutiveZeroBytes = 0; - } - - result[destinationOffset++] = value; - consecutiveZeroBytes = value == 0 ? consecutiveZeroBytes + 1 : 0; - } - - itemData.CopyTo(result.AsSpan(destinationOffset)); - return result; - } - - /// - /// Adapts the first independently coded Annex B picture to the bounded hvc1 item contract used by the - /// production decoder. - /// - /// The complete official conformance stream. - /// The stream's published luma precision. - /// The stream's published chroma precision. - /// The stream's published chroma-format identifier. - /// The generated item-local HEVC decoder configuration. - /// The generated length-delimited payload containing only the first picture. - private static void ConvertAnnexBStillPicture( - ReadOnlySpan annexB, - int bitDepthLuma, - int bitDepthChroma, - byte chromaFormat, - out byte[] configurationData, - out byte[] itemData) - { - const int VideoParameterSetNalUnitType = 32; - const int SequenceParameterSetNalUnitType = 33; - const int PictureParameterSetNalUnitType = 34; - const int HighestVideoCodingLayerNalUnitType = 31; - const int NalUnitHeaderLength = 2; - const int SpsConfigurationPrefixLength = 13; - const int ProfileTierLevelLength = 12; - const int ConfigurationHeaderLength = 23; - const int ParameterSetArrayHeaderLength = 5; - const int ParameterSetCount = 3; - const int ItemNalUnitLengthFieldLength = 4; - - (int Offset, int Length) videoParameterSet = default; - (int Offset, int Length) sequenceParameterSet = default; - (int Offset, int Length) pictureParameterSet = default; - List<(int Offset, int Length)> pictureNalUnits = []; - int offset = 0; - bool foundPicture = false; - while (TryReadAnnexBNalUnit(annexB, ref offset, out int nalOffset, out int nalLength)) - { - // HEVC stores nal_unit_type in the six bits following forbidden_zero_bit. - int nalUnitType = (annexB[nalOffset] >> 1) & 0x3F; - if (!foundPicture) - { - switch (nalUnitType) - { - case VideoParameterSetNalUnitType: - videoParameterSet = (nalOffset, nalLength); - break; - case SequenceParameterSetNalUnitType: - sequenceParameterSet = (nalOffset, nalLength); - break; - case PictureParameterSetNalUnitType: - pictureParameterSet = (nalOffset, nalLength); - break; - } - } - - if (nalUnitType > HighestVideoCodingLayerNalUnitType) - { - continue; - } - - // The first RBSP bit after the two-byte NAL header is first_slice_segment_in_pic_flag. No emulation byte - // can precede that first bit, so it can terminate the extracted picture without parsing later sequences. - bool firstSliceSegment = (annexB[nalOffset + NalUnitHeaderLength] & 0x80) != 0; - if (foundPicture && firstSliceSegment) - { - break; - } - - foundPicture = true; - pictureNalUnits.Add((nalOffset, nalLength)); - } - - Assert.True(videoParameterSet.Length > 0, "The conformance stream does not contain a VPS before its first picture."); - Assert.True(sequenceParameterSet.Length > 0, "The conformance stream does not contain an SPS before its first picture."); - Assert.True(pictureParameterSet.Length > 0, "The conformance stream does not contain a PPS before its first picture."); - Assert.NotEmpty(pictureNalUnits); - - ReadOnlySpan sps = annexB.Slice(sequenceParameterSet.Offset, sequenceParameterSet.Length); - Span spsRbspPrefix = stackalloc byte[SpsConfigurationPrefixLength]; - CopyRbspPrefix(sps[NalUnitHeaderLength..], spsRbspPrefix); - - int configurationLength = ConfigurationHeaderLength - + (ParameterSetCount * ParameterSetArrayHeaderLength) - + videoParameterSet.Length - + sequenceParameterSet.Length - + pictureParameterSet.Length; - - configurationData = new byte[configurationLength]; - - // ISO/IEC 14496-15 defines a fixed 23-byte HEVCDecoderConfigurationRecord header. Copying - // profile_tier_level directly from the published SPS avoids synthesizing codec capability claims. - configurationData[0] = 1; // configurationVersion - spsRbspPrefix.Slice(1, ProfileTierLevelLength).CopyTo(configurationData.AsSpan(1, ProfileTierLevelLength)); - configurationData[13] = 0xF0; // reserved and min_spatial_segmentation_idc = 0 - configurationData[15] = 0xFC; // reserved and parallelismType = 0 - configurationData[16] = (byte)(0xFC | chromaFormat); // reserved and chromaFormat - configurationData[17] = (byte)(0xF8 | (bitDepthLuma - 8)); // reserved and bitDepthLumaMinus8 - configurationData[18] = (byte)(0xF8 | (chromaFormat == 0 ? 0 : bitDepthChroma - 8)); // reserved and bitDepthChromaMinus8 - - int maxSubLayers = ((spsRbspPrefix[0] >> 1) & 7) + 1; - int temporalIdNesting = spsRbspPrefix[0] & 1; - configurationData[21] = (byte)((maxSubLayers << 3) | (temporalIdNesting << 2) | 3); // lengthSizeMinusOne = 3 - configurationData[22] = ParameterSetCount; - int configurationOffset = ConfigurationHeaderLength; - WriteParameterSetArray(configurationData, ref configurationOffset, VideoParameterSetNalUnitType, annexB.Slice(videoParameterSet.Offset, videoParameterSet.Length)); - WriteParameterSetArray(configurationData, ref configurationOffset, SequenceParameterSetNalUnitType, sps); - WriteParameterSetArray(configurationData, ref configurationOffset, PictureParameterSetNalUnitType, annexB.Slice(pictureParameterSet.Offset, pictureParameterSet.Length)); - Assert.Equal(configurationData.Length, configurationOffset); - - int itemLength = 0; - foreach ((int _, int nalLength) in pictureNalUnits) - { - itemLength += ItemNalUnitLengthFieldLength + nalLength; - } - - itemData = new byte[itemLength]; - int itemOffset = 0; - foreach ((int nalOffset, int nalLength) in pictureNalUnits) - { - BinaryPrimitives.WriteUInt32BigEndian(itemData.AsSpan(itemOffset), (uint)nalLength); - itemOffset += ItemNalUnitLengthFieldLength; - annexB.Slice(nalOffset, nalLength).CopyTo(itemData.AsSpan(itemOffset)); - itemOffset += nalLength; - } - } - - /// - /// Reads the next NAL-unit payload from an Annex B byte stream. - /// - /// The complete Annex B byte stream. - /// The current search offset, advanced to the next start code. - /// The returned NAL-unit payload offset. - /// The returned NAL-unit payload length. - /// when another complete NAL unit was found. - private static bool TryReadAnnexBNalUnit(ReadOnlySpan source, ref int offset, out int nalOffset, out int nalLength) - { - int startCodeOffset = FindAnnexBStartCode(source, offset, out int startCodeLength); - if (startCodeOffset < 0) - { - nalOffset = 0; - nalLength = 0; - return false; - } - - nalOffset = startCodeOffset + startCodeLength; - int nextStartCodeOffset = FindAnnexBStartCode(source, nalOffset, out _); - int nalEnd = nextStartCodeOffset < 0 ? source.Length : nextStartCodeOffset; - - // Annex B permits trailing_zero_8bits between a NAL unit and the next start-code prefix. They are byte-stream - // framing and must not enter the length-delimited item payload. - while (nalEnd > nalOffset && source[nalEnd - 1] == 0) - { - nalEnd--; - } - - offset = nextStartCodeOffset < 0 ? source.Length : nextStartCodeOffset; - nalLength = nalEnd - nalOffset; - return nalLength >= 2; - } - - /// - /// Locates the next three- or four-byte Annex B start code. - /// - /// The complete Annex B byte stream. - /// The first byte to inspect. - /// The returned start-code length. - /// The start-code offset, or negative one when no code remains. - private static int FindAnnexBStartCode(ReadOnlySpan source, int offset, out int length) - { - for (int index = offset; index <= source.Length - 3; index++) - { - if (source[index] != 0 || source[index + 1] != 0) - { - continue; - } - - if (source[index + 2] == 1) - { - length = 3; - return index; - } - - if (index <= source.Length - 4 && source[index + 2] == 0 && source[index + 3] == 1) - { - length = 4; - return index; - } - } - - length = 0; - return -1; - } - - /// - /// Copies the fixed SPS prefix through general_level_idc while removing emulation-prevention bytes. - /// - /// The SPS bytes following the NAL-unit header. - /// The fixed 13-byte SPS prefix destination. - private static void CopyRbspPrefix(ReadOnlySpan escapedRbsp, Span destination) - { - const int EscapeZeroCount = 2; - const byte EmulationPreventionByte = 3; - - int sourceOffset = 0; - int destinationOffset = 0; - int consecutiveZeroes = 0; - while (destinationOffset < destination.Length) - { - byte value = escapedRbsp[sourceOffset++]; - if (consecutiveZeroes == EscapeZeroCount && value == EmulationPreventionByte) - { - consecutiveZeroes = 0; - continue; - } - - destination[destinationOffset++] = value; - consecutiveZeroes = value == 0 ? consecutiveZeroes + 1 : 0; - } - } - - /// - /// Writes one complete parameter-set array to an HEVC decoder-configuration record. - /// - /// The complete configuration destination. - /// The current destination offset, advanced past the array. - /// The parameter-set NAL-unit type. - /// The complete NAL unit without Annex B framing. - private static void WriteParameterSetArray(Span configuration, ref int offset, byte nalUnitType, ReadOnlySpan nalUnit) - { - // Each complete array contains exactly one parameter set from the source stream. ISO/IEC 14496-15 stores - // array_completeness in the high bit and the six-bit HEVC NAL-unit type in the low bits. - configuration[offset++] = (byte)(0x80 | nalUnitType); - BinaryPrimitives.WriteUInt16BigEndian(configuration[offset..], 1); - offset += 2; - BinaryPrimitives.WriteUInt16BigEndian(configuration[offset..], (ushort)nalUnit.Length); - offset += 2; - nalUnit.CopyTo(configuration[offset..]); - offset += nalUnit.Length; - } - - /// - /// Calculates the HEVC decoded-picture MD5 digest for one reconstructed component plane. - /// - /// The reconstructed picture. - /// The component plane to hash. - /// The lowercase hexadecimal decoded-picture digest. - private static string GetPlaneDigest(HevcPictureBuffer picture, HevcPlane plane) - { - int width = picture.GetWidth(plane); - int height = picture.GetHeight(plane); - int bytesPerSample = picture.GetBitDepth(plane) > 8 ? 2 : 1; - byte[] rowBytes = new byte[width * bytesPerSample]; - using IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.MD5); - for (int y = 0; y < height; y++) - { - Span samples = picture.GetRowSpan(plane, y)[..width]; - if (bytesPerSample == 1) - { - for (int x = 0; x < width; x++) - { - rowBytes[x] = (byte)samples[x]; - } - } - else - { - for (int x = 0; x < width; x++) - { - BinaryPrimitives.WriteUInt16LittleEndian(rowBytes.AsSpan(x * 2), samples[x]); - } - } - - hash.AppendData(rowBytes); - } - - return Convert.ToHexString(hash.GetHashAndReset()).ToLowerInvariant(); - } - - /// - /// Verifies that every tracked allocation was returned exactly once. - /// - /// The allocator whose ownership log is complete. - private static void AssertBalancedAllocations(TestMemoryAllocator allocator) - { - Assert.Equal(allocator.AllocationLog.Count, allocator.ReturnLog.Count); - foreach (TestMemoryAllocator.AllocationRequest allocation in allocator.AllocationLog) - { - Assert.Single( - allocator.ReturnLog, - returned => returned.AllocationId == allocation.AllocationId); - } - } - - /// - /// Provides tracked owners until the configured allocation attempt fails. - /// - private sealed class FailingTestMemoryAllocator : TestMemoryAllocator - { - private readonly int failureAllocationNumber; - private int allocationAttemptCount; - - /// - /// Initializes a new instance of the class. - /// - /// The one-based allocation attempt that must fail. - public FailingTestMemoryAllocator(int failureAllocationNumber) - { - this.failureAllocationNumber = failureAllocationNumber; - this.EnableNonThreadSafeLogging(); - } - - /// - /// Gets the number of backing-owner allocation attempts. - /// - public int AllocationAttemptCount => this.allocationAttemptCount; - - /// - protected override AllocationTrackedMemoryManager AllocateCore( - int length, - AllocationOptions options = AllocationOptions.None) - { - this.allocationAttemptCount++; - if (this.allocationAttemptCount == this.failureAllocationNumber) - { - // Fail before delegation so the failed attempt never creates an owner that needs rollback. - throw new InvalidMemoryOperationException("The configured HEVC allocation failed."); - } - - return base.AllocateCore(length, options); - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs deleted file mode 100644 index 409271cff..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC luma and chroma quantization-parameter derivation. -/// -[Trait("Format", "Heic")] -public class HevcQuantizationParametersTests -{ - /// - /// Verifies that luma quantization parameters include the precision-derived offset. - /// - /// The luma quantization parameter before the bit-depth offset. - /// The reconstructed luma precision. - /// The expected effective luma quantization parameter. - [Theory] - [InlineData(22, 8, 22)] - [InlineData(-12, 10, 0)] - [InlineData(-24, 12, 0)] - [InlineData(51, 12, 75)] - public void LumaIncludesBitDepthOffset(int quantizationParameter, int bitDepth, int expected) - { - HevcQuantizationParameters parameters = new(quantizationParameter, bitDepth, bitDepth, 1, 0, 0); - - Assert.Equal(expected, parameters.Luma); - Assert.Equal(expected, parameters.Get(HevcPlane.Y)); - } - - /// - /// Verifies the 4:2:0 chroma mapping plateaus and the upper mapped value. - /// - /// The luma quantization parameter before component offsets. - /// The expected effective eight-bit chroma quantization parameter. - [Theory] - [InlineData(29, 29)] - [InlineData(30, 29)] - [InlineData(35, 33)] - [InlineData(37, 34)] - [InlineData(43, 37)] - [InlineData(51, 45)] - public void Chroma420UsesNormativeMapping(int quantizationParameter, int expected) - { - HevcQuantizationParameters parameters = new(quantizationParameter, 8, 8, 1, 0, 0); - - Assert.Equal(expected, parameters.Cb); - Assert.Equal(expected, parameters.Cr); - } - - /// - /// Verifies that 4:2:2 and 4:4:4 chroma quantization parameters are linear through 51 and saturate above it. - /// - /// The tested sequence chroma-format identifier. - [Theory] - [InlineData((byte)2)] - [InlineData((byte)3)] - public void FullResolutionMappingsSaturateAboveFiftyOne(byte chromaFormat) - { - HevcQuantizationParameters parameters = new(51, 8, 8, chromaFormat, 6, 6); - - Assert.Equal(51, parameters.Cb); - Assert.Equal(51, parameters.Cr); - } - - /// - /// Verifies negative chroma values and independent combined component offsets at higher precision. - /// - [Fact] - public void ChromaAppliesCombinedOffsetsAndBitDepthOffset() - { - HevcQuantizationParameters parameters = new(-8, 10, 12, 1, -8, 20); - - Assert.Equal(4, parameters.Luma); - Assert.Equal(8, parameters.Cb); - Assert.Equal(36, parameters.Cr); - Assert.Equal(parameters.Cb, parameters.Get(HevcPlane.Cb)); - Assert.Equal(parameters.Cr, parameters.Get(HevcPlane.Cr)); - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcResidualReconstructorTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcResidualReconstructorTests.cs deleted file mode 100644 index 713b77888..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcResidualReconstructorTests.cs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC transform-skip, transquant-bypass, rotation, and residual differential reconstruction. -/// -[Trait("Format", "Heic")] -public class HevcResidualReconstructorTests -{ - /// - /// Verifies that lossless transquant bypass preserves or completely reverses coefficient order. - /// - /// Whether the coefficient order is reversed. - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CopyBypassedPreservesOrRotatesCoefficientOrder(bool rotate) - { - int[] coefficients = new int[1024]; - int[] actual = new int[coefficients.Length]; - int[] expected = new int[coefficients.Length]; - for (int i = 0; i < coefficients.Length; i++) - { - coefficients[i] = (i * 17) - 8000; - } - - for (int i = 0; i < coefficients.Length; i++) - { - expected[i] = rotate ? coefficients[coefficients.Length - 1 - i] : coefficients[i]; - } - - HevcResidualReconstructor.CopyBypassed(coefficients, actual, rotate); - - int mismatch = expected.AsSpan().SequenceEqual(actual) ? -1 : FindFirstMismatch(expected, actual); - Assert.True(mismatch < 0, mismatch < 0 ? string.Empty : $"Mismatch at {mismatch}: expected {expected[mismatch]}, actual {actual[mismatch]}."); - } - - /// - /// Compares SIMD transform-skip reconstruction with a scalar oracle across transform sizes and signed shift directions. - /// - /// The transform-block width. - /// The transform-block height. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The base-two logarithm of the equivalent square transform size. - /// Whether extended transform-skip precision applies. - /// Whether the complete coefficient order is reversed. - [Theory] - [InlineData(4, 4, 8, 15, 2, false, true)] - [InlineData(4, 8, 8, 15, 3, false, false)] - [InlineData(8, 4, 10, 15, 2, false, false)] - [InlineData(8, 8, 10, 15, 3, false, false)] - [InlineData(16, 16, 12, 15, 4, false, false)] - [InlineData(16, 16, 12, 15, 4, true, false)] - [InlineData(32, 32, 12, 18, 5, false, false)] - public void TransformSkipMatchesScalarOracle( - int width, - int height, - int bitDepth, - int maxTransformDynamicRange, - int equivalentLog2TransformSize, - bool extendedPrecisionProcessingEnabled, - bool rotate) - { - int coefficientCount = width * height; - int[] coefficients = new int[coefficientCount]; - int[] actual = new int[coefficientCount]; - int[] expected = new int[coefficientCount]; - for (int i = 0; i < coefficientCount; i++) - { - coefficients[i] = (((i * 7919) + (width * 257)) & 65535) - 32768; - } - - ApplyTransformSkipScalar( - coefficients, - expected, - bitDepth, - maxTransformDynamicRange, - equivalentLog2TransformSize, - extendedPrecisionProcessingEnabled, - rotate); - - HevcResidualReconstructor.ApplyTransformSkip( - coefficients, - actual, - width, - height, - bitDepth, - maxTransformDynamicRange, - equivalentLog2TransformSize, - extendedPrecisionProcessingEnabled, - rotate); - - Assert.True(expected.AsSpan().SequenceEqual(actual)); - } - - /// - /// Compares SIMD residual differential reconstruction with the sequential normative recurrence. - /// - /// The square residual-block side. - /// The numeric differential accumulation direction. - [Theory] - [InlineData(4, 1)] - [InlineData(4, 2)] - [InlineData(8, 1)] - [InlineData(8, 2)] - [InlineData(16, 1)] - [InlineData(16, 2)] - [InlineData(32, 1)] - [InlineData(32, 2)] - public void ResidualDpcmMatchesScalarOracle(int size, int modeValue) - { - HevcResidualDpcmMode mode = (HevcResidualDpcmMode)modeValue; - int[] actual = new int[size * size]; - for (int i = 0; i < actual.Length; i++) - { - actual[i] = (((i * 104729) + (size * 4099)) & 8191) - 4096; - } - - int[] expected = (int[])actual.Clone(); - ApplyResidualDpcmScalar(expected, size, size, mode); - - HevcResidualReconstructor.ApplyResidualDpcm(actual, size, size, mode); - - Assert.True(expected.AsSpan().SequenceEqual(actual)); - } - - /// - /// Verifies signed residual clipping without clipping the thirty-two-bit recurrence accumulator. - /// - /// The numeric differential accumulation direction. - [Theory] - [InlineData(1)] - [InlineData(2)] - public void ResidualDpcmClipsStoredSamples(int modeValue) - { - HevcResidualDpcmMode mode = (HevcResidualDpcmMode)modeValue; - int[] actual = new int[32 * 32]; - actual.AsSpan().Fill(3000); - int[] expected = (int[])actual.Clone(); - ApplyResidualDpcmScalar(expected, 32, 32, mode); - - HevcResidualReconstructor.ApplyResidualDpcm(actual, 32, 32, mode); - - Assert.True(expected.AsSpan().SequenceEqual(actual)); - Assert.Contains(short.MaxValue, actual); - } - - /// - /// Verifies the Range Extensions rotation constraint for non-transformed intra blocks. - /// - [Fact] - public void RotationRequiresEnabledFourWideIntraBlock() - { - Assert.True(HevcResidualReconstructor.IsNonTransformedResidualRotated(true, true, 4)); - Assert.False(HevcResidualReconstructor.IsNonTransformedResidualRotated(false, true, 4)); - Assert.False(HevcResidualReconstructor.IsNonTransformedResidualRotated(true, false, 4)); - Assert.False(HevcResidualReconstructor.IsNonTransformedResidualRotated(true, true, 8)); - } - - /// - /// Verifies implicit residual differential mode selection, including 4:2:2 chroma angle remapping. - /// - [Fact] - public void ImplicitResidualDpcmFollowsPredictionDirection() - { - Assert.Equal(HevcResidualDpcmMode.Horizontal, HevcResidualReconstructor.GetImplicitResidualDpcmMode(10, false)); - Assert.Equal(HevcResidualDpcmMode.Vertical, HevcResidualReconstructor.GetImplicitResidualDpcmMode(26, false)); - Assert.Equal(HevcResidualDpcmMode.None, HevcResidualReconstructor.GetImplicitResidualDpcmMode(18, false)); - Assert.Equal(HevcResidualDpcmMode.Horizontal, HevcResidualReconstructor.GetImplicitResidualDpcmMode(10, true)); - Assert.Equal(HevcResidualDpcmMode.Vertical, HevcResidualReconstructor.GetImplicitResidualDpcmMode(26, true)); - } - - /// - /// Compares cross-component residual prediction with the scalar signed-precision oracle across SIMD widths and a tail. - /// - /// The luma precision minus the chroma precision. - [Theory] - [InlineData(-2)] - [InlineData(0)] - [InlineData(2)] - public void CrossComponentPredictionMatchesScalarOracle(int bitDepthDifference) - { - const int sampleCount = 257; - const int alpha = -8; - int[] luma = new int[sampleCount]; - int[] actual = new int[sampleCount]; - int[] expected = new int[sampleCount]; - for (int index = 0; index < sampleCount; index++) - { - luma[index] = (((index * 7919) + 1229) & 65535) - 32768; - actual[index] = (((index * 4099) + 811) & 65535) - 32768; - expected[index] = actual[index]; - } - - for (int index = 0; index < sampleCount; index++) - { - int adjustedLuma = bitDepthDifference >= 0 ? luma[index] >> bitDepthDifference : luma[index] << -bitDepthDifference; - expected[index] = Math.Clamp(expected[index] + ((alpha * adjustedLuma) >> 3), short.MinValue, short.MaxValue); - } - - HevcResidualReconstructor.ApplyCrossComponentPrediction(luma, actual, sampleCount, alpha, bitDepthDifference); - - Assert.True(expected.AsSpan().SequenceEqual(actual)); - } - - /// - /// Applies the normative transform-skip normalization as a scalar test oracle. - /// - /// The dequantized coefficients. - /// The destination residual block. - /// The reconstructed component precision. - /// The transform dynamic range excluding its sign bit. - /// The base-two logarithm of the equivalent square transform size. - /// Whether extended transform-skip precision applies. - /// Whether the complete coefficient order is reversed. - private static void ApplyTransformSkipScalar( - ReadOnlySpan coefficients, - Span residual, - int bitDepth, - int maxTransformDynamicRange, - int equivalentLog2TransformSize, - bool extendedPrecisionProcessingEnabled, - bool rotate) - { - int shift = maxTransformDynamicRange - bitDepth - equivalentLog2TransformSize; - if (extendedPrecisionProcessingEnabled) - { - shift = Math.Max(0, shift); - } - - for (int i = 0; i < coefficients.Length; i++) - { - int value = coefficients[rotate ? coefficients.Length - 1 - i : i]; - residual[i] = shift > 0 - ? (value + (1 << (shift - 1))) >> shift - : value << -shift; - } - } - - /// - /// Applies the normative inverse residual differential recurrence as a scalar test oracle. - /// - /// The residual block in packed raster order. - /// The residual-block width. - /// The residual-block height. - /// The differential accumulation direction. - private static void ApplyResidualDpcmScalar(Span residual, int width, int height, HevcResidualDpcmMode mode) - { - if (mode == HevcResidualDpcmMode.Vertical) - { - for (int x = 0; x < width; x++) - { - int accumulator = residual[x]; - for (int y = 1; y < height; y++) - { - int index = (y * width) + x; - accumulator += residual[index]; - residual[index] = Math.Clamp(accumulator, short.MinValue, short.MaxValue); - } - } - } - else if (mode == HevcResidualDpcmMode.Horizontal) - { - for (int y = 0; y < height; y++) - { - int rowOffset = y * width; - int accumulator = residual[rowOffset]; - for (int x = 1; x < width; x++) - { - int index = rowOffset + x; - accumulator += residual[index]; - residual[index] = Math.Clamp(accumulator, short.MinValue, short.MaxValue); - } - } - } - } - - /// - /// Finds the first unequal element in two equally sized test buffers. - /// - /// The expected values. - /// The actual values. - /// The first unequal index. - private static int FindFirstMismatch(ReadOnlySpan expected, ReadOnlySpan actual) - { - for (int i = 0; i < expected.Length; i++) - { - if (expected[i] != actual[i]) - { - return i; - } - } - - return -1; - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTileLayoutTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTileLayoutTests.cs deleted file mode 100644 index 4fbcb9ce0..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTileLayoutTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC coding-tree-block mappings for unequal tile dimensions. -/// -[Trait("Format", "Heic")] -public class HevcTileLayoutTests -{ - /// - /// Verifies the normative tile-row, tile-column, and in-tile raster traversal order. - /// - [Fact] - public void MapsEveryAddressBetweenRasterAndTileScanOrder() - { - HevcTileLayout layout = new(new[] { 2, 1 }, new[] { 1, 2 }); - - ReadOnlySpan expectedRasterAddresses = [0, 1, 2, 3, 4, 6, 7, 5, 8]; - for (int tileScanAddress = 0; tileScanAddress < expectedRasterAddresses.Length; tileScanAddress++) - { - int rasterAddress = expectedRasterAddresses[tileScanAddress]; - Assert.Equal(rasterAddress, layout.GetRasterAddress(tileScanAddress)); - Assert.Equal(tileScanAddress, layout.GetTileScanAddress(rasterAddress)); - } - } - - /// - /// Verifies tile identity, local coordinates, and dimensions on both sides of each tile boundary. - /// - [Theory] - [InlineData(0, 0, 0, 0, 2, 1)] - [InlineData(2, 1, 0, 0, 1, 1)] - [InlineData(3, 2, 0, 0, 2, 2)] - [InlineData(7, 2, 1, 1, 2, 2)] - [InlineData(5, 3, 0, 0, 1, 2)] - [InlineData(8, 3, 0, 1, 1, 2)] - public void ResolvesTileLocalPosition( - int rasterAddress, - int expectedTileIndex, - int expectedColumn, - int expectedRow, - int expectedWidth, - int expectedHeight) - { - HevcTileLayout layout = new(new[] { 2, 1 }, new[] { 1, 2 }); - - layout.GetTilePosition( - rasterAddress, - out int tileIndex, - out int column, - out int row, - out int width, - out int height); - - Assert.Equal(expectedTileIndex, tileIndex); - Assert.Equal(expectedColumn, column); - Assert.Equal(expectedRow, row); - Assert.Equal(expectedWidth, width); - Assert.Equal(expectedHeight, height); - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTransformUnitGeometryTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTransformUnitGeometryTests.cs deleted file mode 100644 index 4722be9ca..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcTransformUnitGeometryTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies HEVC transform-tree component geometry across chroma sampling layouts. -/// -[Trait("Format", "Heic")] -public class HevcTransformUnitGeometryTests -{ - /// - /// Verifies that a sub-minimum 4:2:0 chroma block is retained and processed with the final luma quadrant. - /// - [Fact] - public void Chroma420RetainsMinimumBlockAtFinalLumaQuadrant() - { - HevcTransformUnitGeometry root = HevcTransformUnitGeometry.CreateRoot(16, 24, 3, 1, false, 0); - - for (int childIndex = 0; childIndex < 3; childIndex++) - { - HevcTransformUnitGeometry child = root.CreateChild(childIndex); - Assert.False(child.ChromaBlue.Process); - } - - HevcTransformComponentGeometry chroma = root.CreateChild(3).ChromaBlue; - Assert.True(chroma.Process); - Assert.False(chroma.ProcessesAllQuadrants); - Assert.Equal(8, chroma.X); - Assert.Equal(12, chroma.Y); - Assert.Equal(4, chroma.Width); - Assert.Equal(4, chroma.Height); - } - - /// - /// Verifies that a 4:2:2 rectangular chroma transform is retained for two vertical four-by-four coefficient blocks. - /// - [Fact] - public void Chroma422RetainsVerticalSubTransformsAtFinalLumaQuadrant() - { - HevcTransformUnitGeometry root = HevcTransformUnitGeometry.CreateRoot(16, 24, 3, 2, false, 0); - - for (int childIndex = 0; childIndex < 3; childIndex++) - { - Assert.False(root.CreateChild(childIndex).ChromaBlue.Process); - } - - HevcTransformComponentGeometry chroma = root.CreateChild(3).ChromaBlue; - Assert.True(chroma.Process); - Assert.False(chroma.ProcessesAllQuadrants); - Assert.Equal(8, chroma.X); - Assert.Equal(24, chroma.Y); - Assert.Equal(4, chroma.Width); - Assert.Equal(8, chroma.Height); - } - - /// - /// Verifies that separate 4:4:4 planes retain full-resolution primary geometry and omit combined chroma syntax. - /// - [Fact] - public void SeparateColorPlaneUsesFullResolutionPrimaryGeometry() - { - HevcTransformUnitGeometry root = HevcTransformUnitGeometry.CreateRoot(16, 24, 5, 3, true, 2); - - Assert.Equal(HevcPlane.Cr, root.PrimaryPlane); - Assert.Equal(16, root.Primary.X); - Assert.Equal(24, root.Primary.Y); - Assert.Equal(32, root.Primary.Width); - Assert.Equal(32, root.Primary.Height); - Assert.False(root.HasCombinedChroma); - } -} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcYuvConverterTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcYuvConverterTests.cs deleted file mode 100644 index b6ea5b42c..000000000 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcYuvConverterTests.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Heif.Hevc; -using SixLabors.ImageSharp.Formats.Heif.Hevc.Color; -using SixLabors.ImageSharp.Metadata.Profiles.Cicp; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; - -/// -/// Verifies the shared SIMD-first HEVC component conversion pipeline in both directions. -/// -[Trait("Format", "Heic")] -public class HevcYuvConverterTests -{ - /// - /// Verifies that the identity matrix preserves every eight-bit RGB component through the HEVC GBR plane order. - /// - [Fact] - public void IdentityRoundTripIsExact() - { - const int width = 19; - const int height = 7; - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.Identity, true); - using Image source = new(width, height); - for (int y = 0; y < height; y++) - { - Span row = source.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); - for (int x = 0; x < width; x++) - { - row[x] = new Rgba32((byte)((x * 17) + y), (byte)((y * 31) + x), (byte)((x * 7) + (y * 13))); - } - } - - using HevcPictureBuffer picture = new(Configuration.Default, width, height, 8, 8, 3, false); - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - source.Frames.RootFrame, - picture, - profile, - HevcChromaSampleLocation.Left); - - using Image destination = new(width, height); - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - picture, - destination.Frames.RootFrame, - profile, - HevcChromaSampleLocation.Left); - - for (int y = 0; y < height; y++) - { - ReadOnlySpan expected = source.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); - ReadOnlySpan actual = destination.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); - Assert.True(expected.SequenceEqual(actual)); - } - } - - /// - /// Verifies that every exposed precision and plane layout executes both conversion directions on an odd-sized image. - /// - /// The encoded component precision. - /// The HEVC chroma-format identifier. - /// Whether components use their complete numeric range. - [Theory] - [InlineData(8, 0, true)] - [InlineData(8, 1, false)] - [InlineData(10, 2, true)] - [InlineData(12, 3, false)] - public void ConstantColorRoundTripsEveryPrecisionAndPlaneLayout(int bitDepth, byte chromaFormat, bool fullRange) - { - const int width = 17; - const int height = 9; - Rgba64 expected = new(38550, 25700, 51400, ushort.MaxValue); - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.ItuRBt709_6, fullRange); - using Image source = new(width, height, expected); - using HevcPictureBuffer picture = new(Configuration.Default, width, height, bitDepth, bitDepth, chromaFormat, false); - - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - source.Frames.RootFrame, - picture, - profile, - HevcChromaSampleLocation.Left); - - using Image destination = new(width, height); - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - picture, - destination.Frames.RootFrame, - profile, - HevcChromaSampleLocation.Left); - - int tolerance = bitDepth switch - { - 8 => 800, - 10 => 240, - _ => 80, - }; - - foreach (Rgba64 actual in destination.Frames.RootFrame.PixelBuffer.DangerousGetSingleSpan()) - { - if (chromaFormat == 0) - { - // A monochrome picture retains only luma. The decoded channels must therefore agree, while - // comparing them with the chromatic source would incorrectly require discarded color to survive. - Assert.InRange(Math.Abs(actual.R - actual.G), 0, tolerance); - Assert.InRange(Math.Abs(actual.R - actual.B), 0, tolerance); - Assert.Equal(ushort.MaxValue, actual.A); - continue; - } - - Assert.InRange(Math.Abs(actual.R - expected.R), 0, tolerance); - Assert.InRange(Math.Abs(actual.G - expected.G), 0, tolerance); - Assert.InRange(Math.Abs(actual.B - expected.B), 0, tolerance); - Assert.Equal(ushort.MaxValue, actual.A); - } - } - - /// - /// Verifies the six HEVC 4:2:0 sample locations in both conversion directions. - /// - /// The signaled sample-location code point. - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(4)] - [InlineData(5)] - public void ConvertsEveryChromaSampleLocation(int chromaSampleLocationValue) - { - const int width = 17; - const int height = 9; - Rgba64 expected = new(46260, 20560, 33410, ushort.MaxValue); - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.ItuRBt709_6, true); - HevcChromaSampleLocation chromaSampleLocation = (HevcChromaSampleLocation)chromaSampleLocationValue; - using Image source = new(width, height, expected); - using HevcPictureBuffer picture = new(Configuration.Default, width, height, 10, 10, 1, false); - - HevcYuvConverter.ConvertFromRgb(Configuration.Default, source.Frames.RootFrame, picture, profile, chromaSampleLocation); - - using Image destination = new(width, height); - HevcYuvConverter.ConvertToRgb(Configuration.Default, picture, destination.Frames.RootFrame, profile, chromaSampleLocation); - - Rgba64 actual = destination[width - 1, height - 1]; - Assert.InRange(Math.Abs(actual.R - expected.R), 0, 240); - Assert.InRange(Math.Abs(actual.G - expected.G), 0, 240); - Assert.InRange(Math.Abs(actual.B - expected.B), 0, 240); - } - - /// - /// Verifies that encoding selects the horizontal and vertical sample coordinates defined by all six location code points. - /// - [Fact] - public void ChromaSampleLocationsSelectExpectedSourceCoordinatesWhenEncoding() - { - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.YCgCo, true); - using Image source = new(2, 2); - source[0, 0] = new Rgba32(0, 0, 0); - source[1, 0] = new Rgba32(0, byte.MaxValue, 0); - source[0, 1] = new Rgba32(byte.MaxValue, 0, 0); - source[1, 1] = new Rgba32(0, 0, byte.MaxValue); - - // YCgCo maps the four source pixels to neutral, positive Cg, positive Co, and negative Co. Applying the - // H.273 full-range scale after the location-specific point or average yields these exact encoded samples. - ReadOnlySpan expectedChromaBlue = [96, 128, 128, 192, 64, 64]; - ReadOnlySpan expectedChromaRed = [192, 128, 128, 128, 255, 128]; - for (int location = 0; location < expectedChromaBlue.Length; location++) - { - using HevcPictureBuffer picture = new(Configuration.Default, 2, 2, 8, 8, 1, false); - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - source.Frames.RootFrame, - picture, - profile, - (HevcChromaSampleLocation)location); - - Assert.Equal(expectedChromaBlue[location], picture.GetRowSpan(HevcPlane.Cb, 0)[0]); - Assert.Equal(expectedChromaRed[location], picture.GetRowSpan(HevcPlane.Cr, 0)[0]); - } - } - - /// - /// Verifies that decoding reconstructs chroma at the coordinates defined by all six location code points. - /// - [Fact] - public void ChromaSampleLocationsInterpolateExpectedCoordinatesWhenDecoding() - { - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.YCgCo, true); - - // At luma coordinate (1, 1), the six locations exercise centered and co-sited horizontal reconstruction - // together with the quarter-sample weights for centered, top, and bottom vertical positions. - ReadOnlySpan expectedRed = [96, 128, 128, 160, 64, 96]; - ReadOnlySpan expectedGreen = [160, 128, 128, 96, 192, 160]; - for (int location = 0; location < expectedRed.Length; location++) - { - using HevcPictureBuffer picture = new(Configuration.Default, 4, 4, 8, 8, 1, false); - picture.Luma.DangerousGetSingleSpan().Fill(128); - picture.ChromaRed!.DangerousGetSingleSpan().Fill(128); - Span topChromaBlue = picture.GetRowSpan(HevcPlane.Cb, 0); - Span bottomChromaBlue = picture.GetRowSpan(HevcPlane.Cb, 1); - topChromaBlue[0] = 128; - topChromaBlue[1] = 255; - bottomChromaBlue[0] = 0; - bottomChromaBlue[1] = 128; - - using Image destination = new(4, 4); - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - picture, - destination.Frames.RootFrame, - profile, - (HevcChromaSampleLocation)location); - - Rgba32 actual = destination[1, 1]; - Assert.InRange(Math.Abs(actual.R - expectedRed[location]), 0, 1); - Assert.InRange(Math.Abs(actual.G - expectedGreen[location]), 0, 1); - Assert.InRange(Math.Abs(actual.B - expectedRed[location]), 0, 1); - } - } - - /// - /// Verifies that independent luma and chroma precisions use their own H.273 code ranges. - /// - [Fact] - public void SupportsIndependentLumaAndChromaBitDepths() - { - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.ItuRBt709_6, false); - using Image source = new(17, 5, new Rgba64(40000, 24000, 48000, ushort.MaxValue)); - using HevcPictureBuffer picture = new(Configuration.Default, 17, 5, 10, 12, 3, false); - - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - source.Frames.RootFrame, - picture, - profile, - HevcChromaSampleLocation.Left); - - foreach (ushort sample in picture.Luma.DangerousGetSingleSpan()) - { - Assert.InRange(sample, (ushort)0, (ushort)1023); - } - - foreach (ushort sample in picture.ChromaBlue!.DangerousGetSingleSpan()) - { - Assert.InRange(sample, (ushort)0, (ushort)4095); - } - - foreach (ushort sample in picture.ChromaRed!.DangerousGetSingleSpan()) - { - Assert.InRange(sample, (ushort)0, (ushort)4095); - } - - using Image destination = new(17, 5); - HevcYuvConverter.ConvertToRgb( - Configuration.Default, - picture, - destination.Frames.RootFrame, - profile, - HevcChromaSampleLocation.Left); - } - - /// - /// Verifies the exact limited-range endpoints written for ten-bit luma and chroma samples. - /// - [Fact] - public void LimitedRangeUsesExactTenBitEndpoints() - { - CicpProfile profile = CreateProfile(CicpMatrixCoefficients.ItuRBt709_6, false); - using Image source = new(2, 1); - source[0, 0] = new Rgba64(0, 0, 0, ushort.MaxValue); - source[1, 0] = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); - using HevcPictureBuffer picture = new(Configuration.Default, 2, 1, 10, 10, 3, false); - - HevcYuvConverter.ConvertFromRgb( - Configuration.Default, - source.Frames.RootFrame, - picture, - profile, - HevcChromaSampleLocation.Left); - - Assert.Equal((ushort)64, picture.GetRowSpan(HevcPlane.Y, 0)[0]); - Assert.Equal((ushort)940, picture.GetRowSpan(HevcPlane.Y, 0)[1]); - Assert.Equal((ushort)512, picture.GetRowSpan(HevcPlane.Cb, 0)[0]); - Assert.Equal((ushort)512, picture.GetRowSpan(HevcPlane.Cb, 0)[1]); - Assert.Equal((ushort)512, picture.GetRowSpan(HevcPlane.Cr, 0)[0]); - Assert.Equal((ushort)512, picture.GetRowSpan(HevcPlane.Cr, 0)[1]); - } - - /// - /// Creates a complete H.273 profile for a conversion test. - /// - /// The matrix-coefficient code point. - /// Whether components use their complete numeric range. - /// The configured profile. - private static CicpProfile CreateProfile(CicpMatrixCoefficients matrixCoefficients, bool fullRange) - => new( - (byte)CicpColorPrimaries.ItuRBt709_6, - (byte)CicpTransferCharacteristics.ItuRBt709_6, - (byte)matrixCoefficients, - fullRange); -} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ba4d6a84d..825d1a289 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1279,132 +1279,6 @@ public static class TestImages public static class Heif { - // The tile payloads are exact item extents from the named HEIC fixtures. Their planar references were decoded - // with the HEVC Test Model 18.0 decoder so codec conformance is checked independently of color conversion. - public const string Image1TileHvcConfiguration = "Heif/Hevc/image1-tile1.hvcc"; - public const string Image1Tile1Payload = "Heif/Hevc/image1-tile1.hvc1"; - public const string Image1Tile1ReferenceYuv = "Heif/Hevc/image1-tile1-hm.yuv"; - public const string Image1Tile2Payload = "Heif/Hevc/image1-tile2.hvc1"; - public const string Image1Tile2ReferenceYuv = "Heif/Hevc/image1-tile2-hm.yuv"; - public const string Image2TileHvcConfiguration = "Heif/Hevc/image2-tile.hvcc"; - public const string Image2Tile1Payload = "Heif/Hevc/image2-tile1.hvc1"; - public const string Image2Tile1ReferenceYuv = "Heif/Hevc/image2-tile1-hm.yuv"; - public const string Image2Tile7Payload = "Heif/Hevc/image2-tile7.hvc1"; - public const string Image2Tile7ReferenceYuv = "Heif/Hevc/image2-tile7-hm.yuv"; - public const string DwsampleTileHvcConfiguration = "Heif/Hevc/dwsample-tile.hvcc"; - public const string DwsampleTilePayload = "Heif/Hevc/dwsample-tile.hvc1"; - public const string DwsampleTileReferenceYuv = "Heif/Hevc/dwsample-tile-hm.yuv"; - public const string General8BitMonochrome = "Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.bit"; - public const string General8Bit420 = "Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.bit"; - public const string General8Bit444 = "Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.bit"; - public const string General10Bit420 = "Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.bit"; - public const string General10Bit422 = "Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.bit"; - public const string General10Bit444 = "Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.bit"; - public const string General12BitMonochrome = "Heif/Hevc/Conformance/GENERAL_12b_400_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 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 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 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 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 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 HighThroughputExtendedPrecision10Bit444 = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit"; - public const string HighThroughputExtendedPrecision12Bit444 = "Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit"; - public const string RangeExtensionPcm10Bit422 = "Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.bit"; - public const string RangeExtensionPcm12Bit444 = "Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.bit"; - public const string PersistentRice12Bit444 = "Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit"; - public const string TransformSkipContext8Bit444 = "Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.bit"; - public const string TransformSkipContext10Bit444 = "Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.bit"; - public const string TransformSkipContext12Bit444 = "Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.bit"; - public const string Main42210A = "Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.bit"; - public const string Main42210B = "Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.bit"; - public const string HighThroughput10Bit422TilesWavefront = "Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit"; - public const string HighThroughput8Bit420TilesWavefront = "Heif/Hevc/Conformance/WPP_AND_TILE_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 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 IntraPredictionBReference = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv"; - 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 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 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 DeblockingA = "Heif/Hevc/Conformance/DBLK_A_SONY_3.bit"; - public const string DeblockingMain10 = "Heif/Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit"; - public const string SampleAdaptiveOffsetA = "Heif/Hevc/Conformance/SAO_A_MediaTek_4.bit"; - public const string SampleAdaptiveOffsetRangeExtensions = "Heif/Hevc/Conformance/SAO_A_RExt_MediaTek_1.bit"; - public const string RqtA = "Heif/Hevc/Conformance/RQT_A_HHI_4.bit"; - public const string RqtB = "Heif/Hevc/Conformance/RQT_B_HHI_4.bit"; - public const string RqtC = "Heif/Hevc/Conformance/RQT_C_HHI_4.bit"; - public const string RqtD = "Heif/Hevc/Conformance/RQT_D_HHI_4.bit"; - public const string RqtE = "Heif/Hevc/Conformance/RQT_E_HHI_4.bit"; - public const string StructA = "Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit"; - public const string StructB = "Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit"; - public const string TuSizeA = "Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit"; - public const string DependentSlicesA = "Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit"; - public const string DependentSlicesB = "Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit"; - public const string DependentSlicesC = "Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit"; - public const string TilesA = "Heif/Hevc/Conformance/TILES_A_Cisco_2.bit"; - public const string TilesB = "Heif/Hevc/Conformance/TILES_B_Cisco_1.bit"; - public const string WavefrontA = "Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit"; - public const string WavefrontB = "Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit"; - public const string WavefrontC = "Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit"; - public const string WavefrontD = "Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit"; - public const string WavefrontE = "Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit"; - public const string WavefrontF = "Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit"; - public const string EntryPointsA = "Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit"; - public const string EntryPointsC = "Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit"; - public const string Image1 = "Heif/image1.heic"; - public const string Image2 = "Heif/image2.heic"; - public const string Image3 = "Heif/image3.heic"; - public const string Image4 = "Heif/image4.heic"; - public const string Sample640x427 = "Heif/dwsample-heic-640.heic"; - - // Downloaded from: https://github.com/draktable-org/darktable/issues/14473 - public const string FujiFilmHif = "Heif/IMG-20230508-0053.hif"; - // Downloaded from: https://github.com/AOMediaCodec/av1-avif/blob/master/testFiles/Microsoft/Irvine_CA.avif public const string IrvineAvif = "Heif/Irvine_CA.avif"; diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png deleted file mode 100644 index a4e906042..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighBitDepthPresentationPreservesPrecisionAcrossIntrinsicWidths_Rgba64_GENERAL_12b_444_RExt_Sony_2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a -size 445507 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png deleted file mode 100644 index b130f64d8..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42c898b491383da95bf01676a8e8f7aa221a171f4b211f08ce5fa6a1c313bfb1 -size 254922 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png deleted file mode 100644 index 340348343..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5752cc38c38054018850157c1b11805cae28048a58f422389de7a37240af3593 -size 259024 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png deleted file mode 100644 index c256c779d..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb558d383a95d9dcbcf39637e7295f69881aa75b99134c84e98b02405849f956 -size 2270060 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png deleted file mode 100644 index 4d27ce953..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:988480de65a19b98f7df3d3298bd432beb324b7e7fd4a67370d37bba13526a53 -size 599400 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png deleted file mode 100644 index a734b6970..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:704acdf5dabe6d6550373d9f32216fdd0ff17549a6bafba870fab353a0138150 -size 597546 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png deleted file mode 100644 index 9fdb2ad64..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcHighThroughputStillImageMatchesPinnedPresentation_Rgba32_WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82e48bd7d67c6d6bf724f1dd4a07d4ba5ba3532ebab387bd152aca2e54354fa7 -size 596229 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png deleted file mode 100644 index e0a8a0297..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeHighBitDepthPreservesSourcePrecision_Rgba64_GENERAL_12b_400_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137 -size 263001 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png deleted file mode 100644 index e0a8a0297..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_12b_400_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137 -size 263001 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png deleted file mode 100644 index def52f84b..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcMonochromeStillImageMatchesPinnedLibheif_Rgba32_GENERAL_8b_400_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:490756396b21f4aeacc40634483f40c0c42f77d8f70005449e09682729f6e52a -size 83380 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png deleted file mode 100644 index 0369d8ad6..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5a974be508d2f5122ea7e7182da0c4b5313049b5229e1cd4fec77bb57c2b759f -size 249788 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png deleted file mode 100644 index 8a37e56da..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionPresentationMatchesAcrossIntrinsicWidths_Rgba32_GENERAL_8b_420_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9b4e6e260ffe16dc728d0064cfa4852730426f14ed6ee16ae074272deee3cff9 -size 157543 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png deleted file mode 100644 index c1c3ef9c8..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4b38aee5d910a934868a8ddba5756363ac2f6155c756912f310d2e1a7ef2ccf5 -size 4683537 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png deleted file mode 100644 index 378f39e65..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_10bit_RExt_QCOM_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5af1901f1bbd30d2dd98f437cad37b458f2b2defad5b0948959952daf7a48511 -size 2422557 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png deleted file mode 100644 index 5ba5e57df..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_12bit_RExt_QCOM_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c77bdce5adbf111756bd6f07d5e8550384cb9327e5326dca6a704085a662757 -size 5152549 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png deleted file mode 100644 index d6dec42a6..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_CCP_8bit_RExt_QCOM_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ccb446aec97dc88379861345dd6066b121341c63d0f5fb2ef9a4b5211266e795 -size 952927 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png deleted file mode 100644 index 4adc2572f..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8fbc99537fa7e121f41a217a196875f95053662ef3ff21c453b8c203e64eb2ba -size 255982 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png deleted file mode 100644 index 1a154e992..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2b2eaee6ec269a2658d6a7e7e3be47d2cba8b2d158478606b40cd266efbecfd3 -size 250386 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png deleted file mode 100644 index 144666160..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b5d91da29e9ad97cfd64d0b32023e770f7035b51ca37da1825294928d3a2030 -size 221441 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png deleted file mode 100644 index bef135aaf..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_420_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f93b25daf35a51c5d06e13260406d6206cc7fda532812a43c7c2c7fcdcee2ee8 -size 337393 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png deleted file mode 100644 index eb94e6bfd..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_422_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60beaf4ceb7ee35e356225783e04734b8b5e364f93448f716f8ade10f92e2ac4 -size 339585 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png deleted file mode 100644 index 21a418a46..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_10b_444_RExt_Sony_2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9f72db003768c465aae57bd4414cf0a7ca844ac0989a7065ba1f423e131b8a8 -size 327991 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png deleted file mode 100644 index bdea9013a..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_420_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52d44eea9a312d6dfc38ff0f52172e6ccd2aa7ee11ec4427c8dc22a223a12830 -size 455775 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png deleted file mode 100644 index 74b92c41d..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_422_RExt_Sony_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3951b586abcbcc5522f574cbeca8aeb4ab96e6da848b8b2a46823bb6fb51f43e -size 476713 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png deleted file mode 100644 index a4e906042..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_12b_444_RExt_Sony_2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a -size 445507 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png deleted file mode 100644 index 52b26aa22..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_GENERAL_8b_444_RExt_Sony_2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a06102b72c546060d1693244e5a9f6de9f5924a634e27450dbde189ca46da4f -size 159244 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png deleted file mode 100644 index 7886b689e..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_A_RExt_NEC_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e57731b800d6a7c0e34d3b07ab7ba5690f2b66141c2865c64dc3fdc5afecbcb7 -size 247858 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png deleted file mode 100644 index d51e0ab22..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_IPCM_B_RExt_NEC_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b8b3953335f607c9691c626452dabdf68595017a7eeb94e9bee96c340bf9b0a -size 313388 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png deleted file mode 100644 index d778ee4c2..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_A_RExt_Sony_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:21614e3259ce3ff3565d729600e8aa268691f3450bc91eb2bfb0900864576cf3 -size 5872698 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png deleted file mode 100644 index f26a32f38..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_Main_422_10_B_RExt_Sony_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:007694be6991762d3b7637fa2b6215191a470888695d9fbc098b21058f66a949 -size 10716965 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png deleted file mode 100644 index 49289abb0..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_PERSIST_RPARAM_A_RExt_Sony_3_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7d1d7749d15635514d824d7f52aeb92ac7933feaa98d2c59db9eaebbfd17b183 -size 246199 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png deleted file mode 100644 index ee6e06eb5..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_10bit_I_RExt_SHARP_1_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b525da1d3cbc0fede549913eed0f721baf85a4a2c2bc56a1a6d42e326b2ef9ef -size 6251753 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png deleted file mode 100644 index 9924fb149..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_12bit_I_RExt_SHARP_1_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a96601049c2e6dc5808d30297a5c1fcc7060a3c0bae1ce0c13f7d4d8ae7c2b9e -size 10856528 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png deleted file mode 100644 index 1a17e9fdd..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_TSCTX_8bit_I_RExt_SHARP_1_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:de182fc4852448839fc9090ace49fe375401408aed131c632dcd9b28f42b3dcd -size 3486224 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png deleted file mode 100644 index e52b5b0fd..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcRangeExtensionStillImageMatchesPinnedPresentation_Rgba32_WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19a83def25a4deea548a1328c69fd8f1c771d32d5bf86c9571c5368cafad80c1 -size 552593 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_dwsample-heic-640.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_dwsample-heic-640.png deleted file mode 100644 index 786e143c6..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_dwsample-heic-640.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c580c529e6371d050f04cf8ac3f1cd758eccedb2bcbaca37f78120c14035525 -size 464501 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image1.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image1.png deleted file mode 100644 index 9aba8c0b6..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eef9b60aae394fb2043ce3f6f51c0ead683de3f613b99ee6761c91afb7913c99 -size 17932847 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image2.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image2.png deleted file mode 100644 index 99268f29c..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:33d45d4efdbc3e1b4bc2c6dcbd6953a423350b9d7b39be00ed5b54a351dcff59 -size 11579818 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image3.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image3.png deleted file mode 100644 index bfe08d750..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image3.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f7bb5e5e7774177f2065bf752523cb0b053e4a18898278d58e1de35b9d9ee34c -size 10528077 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image4.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image4.png deleted file mode 100644 index d988609b8..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/DecodeHevcStillImage_Rgba32_image4.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86832e24985e7ffa41387a5a4993db9fee76b7fe8ebc829c8fe891b02638f8ff -size 195726 diff --git a/tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png b/tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png deleted file mode 100644 index 1dbb286d2..000000000 --- a/tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b21c55369e0cf2a15398a6538335c3caab59da83321d095c74913de9d25532d2 -size 3736336 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit deleted file mode 100644 index 4755dd43c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56924b6d5c19e5147649973d13a5ddd83af402fd3dca41b2698d03515b4bb2d7 -size 77368 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic deleted file mode 100644 index c6fe8aec2..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b36e994059522739e53eedbacae7ccca5edfe67934fe841fbb400388b926abc -size 77732 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit deleted file mode 100644 index 4e46a40af..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19206fe90232d5cc99cadd70a663a303ec0a27f64539bbf155012461e8c14b8d -size 26723 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.heic deleted file mode 100644 index 3dc03cedd..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_A_RExt_Sony_1_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:83a143499278c1950f191ffe84d04d6aaf6cfb41077f52a78dd686de0e4a9cd2 -size 27087 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit deleted file mode 100644 index 08bfd07ce..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e8bb3d6ad34078bd5319aebc52c4f64a3999cf6fb529808dc7e9a3d51a69c4b -size 26751 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.heic deleted file mode 100644 index 38871a3b8..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Bitdepth_B_RExt_Sony_1_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1dd4464942006d50126b6a79db8e7e1ab6c53833cf1802eca047803086f4bc4b -size 27115 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit deleted file mode 100644 index 744fc3720..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e92952c1da523290592c98b766656964299bac330a357851980f71331b920ee4 -size 44331 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.heic deleted file mode 100644 index b130c7884..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_10bit_RExt_QCOM_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f3c9f9adce22f14fa9f9fe6cf566a0cb3336e0ce71dc98d763b49f268c19b425 -size 44695 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit deleted file mode 100644 index 83f89c0c0..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:33c20dd58a4ae2e16e412bb00ec8def0d071ab6959a8821ea9410405c732ea4e -size 365428 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.heic deleted file mode 100644 index b9f6ab636..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_12bit_RExt_QCOM_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d42fc1b58c55a4d1ab7f385a4479a7dd029c0f3a4fd4244eab11f3147a90270a -size 365792 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit deleted file mode 100644 index 8ac51c3f6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2bb969c41f2eb6664ba29bcbd71a094c76add86df6a2c6c49e90d1f3265952f2 -size 28905 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.heic deleted file mode 100644 index 4783651f2..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CCP_8bit_RExt_QCOM_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d4b6ba7ec0edc57e4ae3a50369d83a53382801dd413412c5224896c8f71cafd6 -size 29269 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit b/tests/Images/Input/Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit deleted file mode 100644 index 72a805dc1..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19f6a710e8b2c66f393392af24c11e5fbba10a5c9f6f28c8b26949942f384c1c -size 11957 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit deleted file mode 100644 index 0619c68a1..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_MAIN10_VIXS_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a9089207bedddb0c8db28b410441e37d4cff5dd8301cb8ba7d2f75036e2d50c -size 100467 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_SONY_3.bit b/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_SONY_3.bit deleted file mode 100644 index 2642388fa..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DBLK_A_SONY_3.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d61a13e04a1678816c0f16cf7a47f5bb918de95ecf1b6fab305e2390c3dd7de9 -size 279115 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit deleted file mode 100644 index b26e7149c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4dc49a2aae1cce20d5cd190a1342faf4c3b2bd06d1a59824542b9b22f53c581b -size 230658 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit deleted file mode 100644 index a33e54c7b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8398fb23c814a197bba497ad2c6103f81ca8003434fa40ec347d4d0a07c9468a -size 373328 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit deleted file mode 100644 index 977d1d42e..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db582f1181f3b8ed3beeb32246a1a99123700dbdc3d3be4e1797032fcd46c486 -size 379617 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit deleted file mode 100644 index 386f2d8d8..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f162007f604aa0e48229c4153f940fd5f24db955ad158d11e2bd46c89c49ca76 -size 373275 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit deleted file mode 100644 index 8179733ae..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1dc1dfb16f676f59cebde3a4732209ce5ce0d0f49ef697970987c0c3e0e087b1 -size 2623324 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit deleted file mode 100644 index e94ebf87f..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:993cd68f0e749cd1d9ce7cc3a368b4df26dccf07ded91048100217b9a0c56d28 -size 218579 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit deleted file mode 100644 index 2a16e0a55..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3572e1c350bd60ba6b0edd71bae1388ccd4d57a1c18c7d62439fa1661e33980c -size 149305 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic deleted file mode 100644 index c309b49b5..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f9b828da68543a5a8724bf317cec4b7c63289322338a98e8164c3a51668a749b -size 149669 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit deleted file mode 100644 index eaa304262..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:15026126bbaa266f5551077bf1a29018ef11d5aa274ae3aadfd282184c1837b0 -size 206758 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.heic deleted file mode 100644 index 93b061e31..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b50903b1cd8bef1ca9a7128a09c11fe29a0cca4bfa76d9fd5360a81d3093f216 -size 207122 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit deleted file mode 100644 index 1867c6c63..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5584b06fbf9affc87d3f1bffa56ecfadb892a80ed6468401a2acf46622444d66 -size 93552 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic deleted file mode 100644 index 5ffc00ff6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ff37e993f514becbf9ccc055863fb105c0e506f99caeb2a0f576c27f4a9cf67 -size 93916 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit deleted file mode 100644 index 660d620c6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:28f0fa43f8c9a5c80c2158b087ed030d8e3e8deac6819239ba84890302a8f30a -size 148045 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic deleted file mode 100644 index 06154d230..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e05c498eccf1c6fa5f273a8a61441b703df82eb6267ba6add646f22a328ee8b1 -size 148409 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit deleted file mode 100644 index fc59086ae..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4107d5bb5df4f95185e7a8a5eef8f35970a4769c76901ff590f38f7bc36b268b -size 205277 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic deleted file mode 100644 index 17c8c614d..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:63bb00619cb2deb4986df6d42ec805ba0c1895e7ab0534e4a81ca43ada140861 -size 205642 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit deleted file mode 100644 index 819e7ea7c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:abace12c9947acaaf91bf8e4cb8bf50cd5ff5b2a7ab5a14d9ee25fe2cdf6b746 -size 92709 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic deleted file mode 100644 index 85b394556..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:72e84f04f278d7a3bd02e215a2fd7d0bbe5dfc6d1ac2048dca57ec4ef36b6a73 -size 93073 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.bit deleted file mode 100644 index 180d8704c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:de0f153bde98066c6d7368a51e0907cc9a829b34a6b998672999ce0b283cb212 -size 30436 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.heic deleted file mode 100644 index 92d10ac81..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:091a1dc8aaf44706b153d61c35f4eb93e27c458704a41f3f2e645b81e5e7d845 -size 30801 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 0658c18a4..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_420_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d9b784954f44b512caef67c967c873ed469643f0fe03b62b67fa00abc8c74fa3 -size 460800 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.bit deleted file mode 100644 index 8caa27c30..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6c012634574009542d4046b24a2c29fbabaa7351a62b19300df5eec3336e810 -size 71890 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.heic deleted file mode 100644 index 2c993558e..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18644ca5cfe89c4a50145d4a9ce5e5cd9ef240b6bea134f13020e2194889b538 -size 36130 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 54665f629..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_422_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:210797850418d6544948c375f65d34e8a58000d52a868c24088701691f6f2573 -size 614400 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.bit deleted file mode 100644 index 5becd373e..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ab9c7d7c0554e6342836fa0212e6334ecc894141f1cb90cd4512c108dabcb936 -size 510412 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.heic deleted file mode 100644 index 97881385b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7070b0a6368ca02fe33310dcf54b7f5da61a4413444308cf99b1a8e49dab43b4 -size 43247 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2_frame0.yuv deleted file mode 100644 index d00cde11b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_10b_444_RExt_Sony_2_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e2af46b75d97462b7fd8ab67238705747fe74571efcb564ff13468c42ae1158 -size 921600 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.bit deleted file mode 100644 index f00d1b802..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42ba06081165645c28b1f38ca1addd62eaf48925bb64e3439b404f32a2b50952 -size 38232 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.heic deleted file mode 100644 index 61fc4f88b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:af534c6238e9fb7c2b4f2a97f053b232d073eb31bd175b4d32e83e2128bb4d67 -size 38595 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 40387f55d..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_400_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01880b5616174d54d5030ce8726374d00242158bba7b3b312141eb48be2625e4 -size 307200 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit deleted file mode 100644 index ce3120197..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3b1f3e683bc1c6b6abcaa26fe540d6ec26a3734255010b27ae650b44b52654e9 -size 57176 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.heic deleted file mode 100644 index 392b3934c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:814433b3109b2fa1f2408f424f6aa38e17f92b4e50f4996d2ca59a2baa84a30a -size 57541 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 1c0c6f70c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c394390723d177e5421eac9428efcb2ead2d94af967aa1937572b689095ac850 -size 460800 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit deleted file mode 100644 index 62b683a49..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a3f534275c0c3d317a3dafc30a3e0eb89d3e7d6aa2f2c0df0e9d27712354700d -size 139893 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.heic deleted file mode 100644 index 70bf1a7b9..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d88d5f0225061eef053bfc193d83334624ec80a6653a8e15792bc320973d25f5 -size 69910 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1_frame0.yuv deleted file mode 100644 index b54899758..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b5930a097a0acd815b1b6575d7cbbf9c9921b49cb3cfb12f46026174fc6a173 -size 614400 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit deleted file mode 100644 index d47c45005..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:197023ccc20fd340c108646fd06b08b92fc29a613609714f6983e15d3770d669 -size 1185165 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.heic deleted file mode 100644 index f9db83f87..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da264470c99586d99280fb861273c69b982a2ac6d962bd7408751c8bfe186332 -size 85534 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_frame0.yuv deleted file mode 100644 index 84ab716c1..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:751dd2deb76632024b523289d945e3b3fb32d27d1eac22bf1de0b8c2ff9ebbc9 -size 921600 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit deleted file mode 100644 index db90f260c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:981d97f0adc228f2e3a739d7a68161f84cd60309745397d5cf24d2f81c009dbc -size 86390 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.bit deleted file mode 100644 index 775b131c5..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6151ab4b8fdc2a8643d169f98d9878e8221f9d23d97c71447bd4d7de5e79bd4b -size 10349 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.heic deleted file mode 100644 index 3108015af..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0dfce3b603a81c253d0a70827d1edea45554adc405068a4a277eea2286af8304 -size 10712 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 6272d9842..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_400_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5ca4e944cc21944edbf1507e2a1a178eb878b31f0f05eeb65a1d3a81aae3dbf -size 153600 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.bit deleted file mode 100644 index a7eb7b2fc..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:44b43ba732bae1b05c217661f7ea9049a52288bd5e4a966252b44406122b6ea4 -size 14092 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.heic deleted file mode 100644 index ed17c27f2..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:68bcd1fa5e8ffcfa7e9fb4a7c9b091b9096db4e7be27b8bd6c336439df217f1d -size 14457 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1_frame0.yuv deleted file mode 100644 index 2822f7f52..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_420_RExt_Sony_1_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3503c56768f9de92a7dd6debbd959ed4636ea1c9e35b767d5f881dee42a572a6 -size 230400 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.bit deleted file mode 100644 index 85c7483ce..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef4e0d887ad0a02e1f8218e3fbf93258c1b1e3923d079296df8df69fac56e538 -size 258456 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.heic b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.heic deleted file mode 100644 index 720c25036..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cacde3604855b57280977daa9ecc4073f4ac30973aa530a30727b88d3395763f -size 19050 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2_frame0.yuv b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2_frame0.yuv deleted file mode 100644 index 5424e1a83..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_8b_444_RExt_Sony_2_frame0.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6974f93431618d623c8e9168ea259b5280e37dd22fd059eb5b2e25318de61ff7 -size 460800 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.bit deleted file mode 100644 index bdf499e96..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:26ae7aefa4ded2ccdc917b99b5ecf2352bd9c7100ce0cfd9a9bdc918c5d242b6 -size 4273 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.heic deleted file mode 100644 index 29b2629cc..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_A_RExt_NEC_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6575bd13224e8c48db8ea3b2ffea9cfdb4b2444c84193f43a1eabef2c6f76342 -size 4637 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.bit deleted file mode 100644 index 3f71b427f..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39f97b25079ce5f12340cce43c9dbd5821c5e23f44da58dd6f1942e135bc93b5 -size 4973 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.heic deleted file mode 100644 index 786bd07e1..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPCM_B_RExt_NEC_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:20b2814c29ae04acd57718ab75fd8fc32b49975a08af9ace98918af1dff814fe -size 5337 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit b/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit deleted file mode 100644 index fc511331b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:456608ae5c14fea0496d770ae9614cd3d7c6ae98c14b22d64bbd96b37f49d7a0 -size 29168 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv b/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv deleted file mode 100644 index 27e440244..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:69626c83c9905fe2e3796b5425ccf2719b05b76b42cfeb398997a17207982a52 -size 3110400 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit deleted file mode 100644 index e77bd3ef6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14251238f005a4576437429a327b34d216b346aaac2a7a05c132a3430174b11b -size 593001 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.bit deleted file mode 100644 index 204075e6c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d6dd0506e85bfd17cfd8d676107927c2860d38d1e8b7e5486647279e6b33876 -size 632639 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.heic deleted file mode 100644 index 2b849e969..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_A_RExt_Sony_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5ee8360a7400db49f7d9ca790e73eed7947b3a267e115714d2581bdea628f04e -size 633003 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.bit deleted file mode 100644 index c3fc445cf..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:951acc9c846dcb9f8ccbdc0b6971a947144baed2d9021ae4be77bcce106bbe21 -size 1113811 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.heic deleted file mode 100644 index c345a56ad..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/Main_422_10_B_RExt_Sony_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:efeff0451cce627bbd73fec725cc38c759cf3de07010050642e6debcc3f57448 -size 1114175 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit deleted file mode 100644 index 868082e00..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6a86ff4122512fda96ec6c4c7a143079eee5371760433c05428749140036cec -size 329271 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic deleted file mode 100644 index da42f2f7d..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/PERSIST_RPARAM_A_RExt_Sony_3_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9fc9c32108dd3ee8b37f138428f39caf6a1701f161778c1258100c754ed9378b -size 329635 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit deleted file mode 100644 index baba05904..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:74847e07f3298923da0e9b68b1f861930d227bd5ad83a834a8280cf042a7e78f -size 236915 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/README.md b/tests/Images/Input/Heif/Hevc/Conformance/README.md deleted file mode 100644 index 0e4dc3950..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# HEVC conformance fixtures - -These fixtures come from the electronic attachment to [ITU-T H.265.1 (10/2018)](https://www.itu.int/rec/T-REC-H.265.1-201810-I/en), the conformance specification aligned with ISO/IEC 23008-8. Table 4 identifies the Sony `GENERAL_*_RExt` streams and their required profiles. - -The `.bit` files are the original Annex B conformance streams. The Samsung archives name their payloads `.bin`; the repository stores those unchanged bytes with the common `.bit` extension. Each `GENERAL_*_frame0.yuv` file contains the first decoded picture extracted byte-for-byte from the corresponding published planar reference output; it was not generated by ImageSharp. - -`HevcPictureDecoderTests.DecodeOfficialRangeExtensionsPictureMatchesPublishedDigest` limits each stream to its first independently coded picture, adapts that picture to the bounded `hvc1` item contract, compares every reconstructed sample with `_frame0.yuv`, and verifies the published decoded-picture MD5 value for every present plane. - -The retained matrix covers the ImageSharp 8, 10, and 12-bit still-image precision boundary across monochrome, 4:2:0, 4:2:2, and 4:4:4. The tool-specific matrix below completes independently coded coverage for the exposed Range Extensions profiles and tools. Explicit residual DPCM remains with dependent-picture inter decoding because the normative syntax requires an inter prediction unit. - -The Main-profile traversal matrix comes from the official `RQT_A_HHI_4` through `RQT_E_HHI_4`, `STRUCT_A_Samsung_7`, `STRUCT_B_Samsung_7`, and `TUSIZE_A_Samsung_1` archives. Their published descriptions target residual-quadtree intra hierarchy depths zero through four, coding-tree and minimum-coding-unit structure, and a 64x64 coding-tree block with 32x32 minimum coding units and 16x16 minimum luma transforms, respectively. - -`HevcPictureDecoderTests.DecodeOfficialTraversalPictureMatchesPinnedHmDigest` decodes each first independently coded picture through the production still-item path and compares all three native planes with fixed MD5 digests. The RQT values are the streams' decoded-picture-hash SEI values. For STRUCT and TUSIZE, the plane values were calculated from the first picture written by the [HM reference decoder at commit `9c1f298659ab0cee9dc13d23d0304221575410b9`](https://vcgit.hhi.fraunhofer.de/jvet/HM/-/commit/9c1f298659ab0cee9dc13d23d0304221575410b9); the complete HM output also matched each archive's published file MD5: - -- `STRUCT_A_Samsung_7`: `02f02cc9a67222004ae18ee6bebd475a` -- `STRUCT_B_Samsung_7`: `1f472e014aff120681e8482c8a04ef93` -- `TUSIZE_A_Samsung_1`: `5e6c198c852e92cdcca0e137fc2610c4` - -The intra-prediction fixtures come from the same official HEVC v1 attachment: - -- `IPRED_B_Nokia_3` is the one-picture Main Still Picture stream identified by H.265.1 Table 1. Its published description states that it uses all 35 modes at each of the luma 32x32, 16x16, 8x8, and 4x4 sizes and the chroma 16x16, 8x8, and 4x4 sizes, for 245 mode-and-size combinations. The archive SHA-256 is `738E35AF4CED173E8CCB558464DC6A1B8B791FB1BB957C073633AA6037288CD0`; the published bitstream MD5 is `1be12a94d9da6b84af78cd6cfd5e4407`; and the retained published YUV has MD5 `ec3bcad4f9174404e52d132206b6617b`. -- `CIP_A_Panasonic_3` signals constrained intra prediction and contains one I picture followed by one B picture. The bounded still decoder test extracts only the independently coded I picture. The archive SHA-256 is `5C55B3594E5C951C9F5312913961750283EB2DADD15844FE35F9BEB94A9A548C`, and the published bitstream MD5 is `e1e00592fc8a158da9109b4dc05d03be`. - -HM at commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces the published `IPRED_B_Nokia_3` YUV MD5 exactly and reports decoded-picture hashes `200ef9f7d2efde44ab872a78a40ece42`, `bfe9de3bef5ce5596a71bbfdbfb823fc`, and `5b14acdd5c5bf8c6887160817ee1ad11`. The same pinned decoder reproduces `CIP_A_Panasonic_3`'s published complete two-picture YUV MD5 `4cbf601ba98d63f642defab5eaa12c8d`; its independently coded first picture reports plane hashes `69a20189e6bbb9c088e3adc967244ca1`, `26502d354bb123f54c20413f14360ddb`, and `baafaef47a55ae2e876862b30b3bc720`. - -The residual-reconstruction fixtures come from the official HEVC v1 and Range Extensions attachments. Pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` decodes every retained independently coded picture and produces the plane hashes asserted by the tests. - -- `DELTAQP_A_BRCM_4_frame0.bit` is the exact first independently coded picture extracted from `DELTAQP_A_BRCM_4.bit`. The official archive has SHA-256 `63ABBBAF43D334D7952EAB7BBC5B3E34B4AD0259EDDCD63CDB164DBCF9A36CA6`, the original bitstream has SHA-256 `1274E3E308DFE684B38A88F8B3A2845B84F57492B0B02AAECBE5DC29CA8ED532`, and the retained picture has SHA-256 `4DC49A2AAE1CCE20D5CD190A1342FAF4C3B2BD06D1A59824542B9B22F53C581B`. The official description randomizes `diff_cu_qp_delta_depth` from zero through three and delta QP from -26 through 25; the retained picture contains 384 delta-QP CABAC decisions. -- `QMATRIX_A_RExt_Sony_1.bit` has SHA-256 `74847E07F3298923DA0E9B68B1F861930D227BD5AD83A834A8280CF042A7E78F`. Its official description covers scaling-list data with and without transform skip at every transform size over different QPs; the retained first picture signals scaling lists, coding-unit QP deltas, transform skip, transform-skip contexts and rotation, and implicit residual DPCM. -- `EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit` is the second of the official stream's two concatenated one-picture sequences, retained with its own VPS, SPS, and PPS. The official archive has SHA-256 `66C8E24866210B94A63A29C93F8C1490046CC17B26484C5BD81F805D7BFD18D1`, the original bitstream has SHA-256 `5F0164DCCC333296FB2753068CAFE4985B758DDB7E282358DBB8FAF74986817D`, and the retained sequence has SHA-256 `4107D5BB5DF4F95185E7A8A5EEF8F35970A4769C76901FF590F38F7BC36B268B`. This 12-bit GBR sequence is the one that signals extended-precision processing and transform skip. Its reference plane hashes were produced by the pinned HM commit built with its `HIGH_BITDEPTH` option, which is required by HM for this sequence. -- `GENERAL_12b_444_RExt_Sony_2_idr7.bit` is the seventh independently coded picture extracted with its active VPS, SPS, and PPS from the already retained official GENERAL stream. It has SHA-256 `981D97F0ADC228F2E3A739D7A68161F84CD60309745397D5CF24D2F81C009DBC` and contains 39 coding-unit chroma-QP adjustment decisions across 8-, 16-, and 32-sample chroma transforms. -- `LS_A_Orange_2.bit` has SHA-256 `14251238F005A4576437429A327B34D216B346AAAC2A7A05C132A3430174B11B` and published MD5 `9118d01cf6b3671038d6f99342c0895e`. The official lossless description states that every coding unit uses transform, quantization, and filtering bypass; the retained first picture exercises the bypass-flag path and its decoded-picture hash reports OK. - -The remaining Range Extensions fixtures come from the official H.265.1 RExt attachment. Each retained file contains the active VPS, SPS, and PPS followed by exactly one independently coded picture. For the extended-precision streams, the retained picture is the second concatenated one-picture sequence because that sequence enables extended-precision processing. `HevcPictureDecoderTests.DecodeOfficialRangeExtensionProfilePictureMatchesDecodedPictureHash` checks profile identifier, luma and chroma precision, chroma layout, required tool flags, and every native-plane MD5 through the production still-item decoder. - -The expected plane hashes come directly from each selected picture's decoded-picture-hash SEI message when present. The CCP 8/10/12-bit, IPCM A/B, and Main 4:2:2 10 B streams omit that per-picture message. Their first reference pictures were extracted only after FFmpeg 9.0.1 reproduced the archives' published complete-output MD5 values `54231c6f121ca65d8b94a747b9504b8b`, `363f55fc6097a3bb7525392b87a6498a`, `f3e914fccdb820eac85f46642ea0e168`, `8d4eb18812338f9f475035b8da27e74a`, `eea32e6e1f88b7782515e53f8122be7d`, and `fd163bcbdc24792a23781ee7aace74eb`, respectively. - -## Genuine HEIC presentation matrix - -The 35 `.heic` files pair every retained official Range Extensions picture above with a real ISO BMFF image item. GPAC `MP4Box` 26.07 (`26.07-rev0-ga07cbfff-master`) wrote the containers. FFmpeg 9.0.1 first copied one complete picture, including its active VPS, SPS, and PPS, from each supported Annex B stream so the `hvcC` configuration and item payload cannot select parameter sets from different concatenated pictures. The two unequal-precision streams are already bounded pictures and were passed directly to GPAC because FFmpeg rejects unequal luma and chroma precision. - -The generation templates were: - -```text -ffmpeg -threads 1 -i input.bit -map 0:v:0 -frames:v 1 -c copy frame0.hevc -MP4Box -add-image frame0.hevc:primary -new output.heic -``` - -The monochrome files add `image-pixi=8` or `image-pixi=12` to the `-add-image` argument so their `pixi` property contains exactly one channel. No `colr` property is added; the decoder must retain the HEVC VUI color description. - -Presentation references under `tests/Images/External/ReferenceOutput/HeifDecoderTests` come from pinned libheif 1.23.1 when its HEVC backend reproduces the native reference planes. Profiles that libheif rejects or reconstructs incorrectly instead use native samples from pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9`, built with `HIGH_BITDEPTH=ON`, followed by libheif's independently reproduced color-conversion operation. That operation retains libheif's literal float32 Rec.601 fallback coefficients, limited-range factors, nearest native chroma sample, separate multiply/add order, half-unit rounding, source-precision clipping, and 16-bit RGB storage for high-bit-depth input. - -FFmpeg 9.0.1 raw output is a reconstruction cross-check only where every Y, Cb, and Cr byte matches pinned HM. Its PNG color-conversion rounding is not a presentation oracle. Every HM plane used to generate a reference matches the selected picture's official decoded-picture-hash values or the independently verified complete-output digest described above. - -The two unequal-precision HEIC files have no PNG reference because FFmpeg and libheif reject that HEVC profile combination. Their public tests require successful bounded HEIC presentation and correct metadata, while the companion native tests compare all three planes with the published digests. - -`HeifDecoderTests` covers all 35 genuine HEIC files. The 33 independently presentable cases use `DebugSave` and `CompareToReferenceOutput(ImageComparer.Exact, provider)`; no presentation tolerance is permitted. Exact native-plane tests remain the reconstruction oracle. Representative 8/10/12-bit, monochrome, 4:2:0, 4:2:2, 4:4:4, and high-throughput files also run with 4 KiB allocator groups and exactly-once returns. Eight-bit subsampled, 12-bit full-resolution, and 12-bit `Rgba64` presentation repeat through AVX-512, AVX, narrower vector, and scalar `FeatureTestRunner` configurations. - -The SHA-256 columns below identify the downloaded archive, its original Annex B payload, and the retained bounded picture: - -| Retained picture | Archive SHA-256 | Original payload SHA-256 | Retained SHA-256 | -| --- | --- | --- | --- | -| `ADJUST_IPRED_ANGLE_A_RExt_Mitsubishi_2_frame0.bit` | `8C915B0554265CCD6FB1347B41788006DF9E7C66349FDEAA142D953340B12997` | `54B72F9B2F5BD7C25049A63FD4289AB19735AF5B30C2105DEDD7AA5AE90A72E8` | `56924B6D5C19E5147649973D13A5DDD83AF402FD3DCA41B2698D03515B4BB2D7` | -| `Bitdepth_A_RExt_Sony_1_frame0.bit` | `34967E2047D59B0F3E96E7501712A056C01F369501D812B170C717CE1D417368` | `0519F266D11FAD0A25878F29D5DAC153BA00629AACEF34ECD78F5F5AD0FB2174` | `19206FE90232D5CC99CADD70A663A303EC0A27F64539BBF155012461E8C14B8D` | -| `Bitdepth_B_RExt_Sony_1_frame0.bit` | `6F5F16377E9287CED1AD06A9493249ED1D61E393D779B06E777A92E14B6C6EB6` | `C8B03A11E8453E430E41BC499F686BE9E75CEF269F80CCAB85C971D048A91EA8` | `0E8BB3D6AD34078BD5319AEBC52C4F64A3999CF6FB529808DC7E9A3D51A69C4B` | -| `CCP_8bit_RExt_QCOM_frame0.bit` | `E5A0216C23F3816107441366B7743160BD0E6A39CE15B14A9FE1A3FFEA38BF88` | `C5E0D9B5E0062C2F4BFB1EC9E8A6465FB715E3AF498306D7D0E4BEC0BC9BAE5C` | `2BB969C41F2EB6664BA29BCBD71A094C76ADD86DF6A2C6C49E90D1F3265952F2` | -| `CCP_10bit_RExt_QCOM_frame0.bit` | `190AB9B4A9AADEE32ECFB639223156A3DEE721DBBA064C28574E176CE07FF032` | `6E40E49BA5BEF61D951C4088B0234DB20E1F264804C59A007B9F32A117A45603` | `E92952C1DA523290592C98B766656964299BAC330A357851980F71331B920EE4` | -| `CCP_12bit_RExt_QCOM_frame0.bit` | `E54417A143D59BA112D90355BE88E535761A22EC44C88BDA5C4913ED0B3CE7F3` | `17FD17F2449B4C44D4AFDADDAC9FE1A61B572E1BD4BF1FBDAC609E108A7F2BD3` | `33C20DD58A4AE2E16E412BB00EC8DEF0D071AB6959A8821EA9410405C732EA4E` | -| `EXTPREC_MAIN_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit` | `7EBC6DB0E9BFBCEEEF1F168843E824CDD2B3A672C9574ECEEA9ADA8F41A37A37` | `52DEF2115F3FF5D2B4E1BCDD702B893DF280B83CD6D0DDED940735E6899556A8` | `ABACE12C9947ACAAF91BF8E4CB8BF50CD5FF5B2A7AB5A14D9EE25FE2CDF6B746` | -| `EXTPREC_MAIN_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit` | `1120A6B1CC6D1E8AF5CF2D9980A5D4AC2C8C83217E570900D5C45BC6CD92403A` | `8760ABA893522FE302C14C8472677FED3C3A29B241918A49067F12107F6B5FDE` | `28F0FA43F8C9A5C80C2158B087ED030D8E3E8DEAC6819239BA84890302A8F30A` | -| `EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_8BIT_RExt_Sony_1_extended.bit` | `B7D3993DC6AA87E86D09C245665FB13F6F970E14DFE75AB25F7EE689C3BB78A0` | `07C7A14717FFFB5828D9B3472D1DB1DE554ACB9A126D34CA4E73EF1ED6F68734` | `5584B06FBF9AFFC87D3F1BFFA56ECFADB892A80ED6468401A2ACF46622444D66` | -| `EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_10BIT_RExt_Sony_1_extended.bit` | `445D8EF653C1C37495B62C6AFBE11006218131540289CDE5B35DFAFBA4ACF975` | `5FE487203641FDC5920C2FBC85F1A0E329A01462E18862850B42890F90F148AE` | `3572E1C350BD60BA6B0EDD71BAE1388CCD4D57A1C18C7D62439FA1661E33980C` | -| `EXTPREC_HIGHTHROUGHPUT_444_16_INTRA_12BIT_RExt_Sony_1_extended.bit` | `78DF34AC5602D59A67730E231CA86A6F1E41D4B9E6967C6FBB31AFD276B2D4F8` | `BEBB8D008469343017680082D847309A0122F3133B9A8296D5BEA8F402D3F445` | `15026126BBAA266F5551077BF1A29018EF11D5AA274AE3AADFD282184C1837B0` | -| `IPCM_A_RExt_NEC_2_frame0.bit` | `CC78DF0AA70ED8536B002B48EA7FEACF72AB703BBA7E5286C290D02D1EB51308` | `26AE7AEFA4DED2CCDC917B99B5ECF2352BD9C7100CE0CFD9A9BDC918C5D242B6` | `26AE7AEFA4DED2CCDC917B99B5ECF2352BD9C7100CE0CFD9A9BDC918C5D242B6` | -| `IPCM_B_RExt_NEC_frame0.bit` | `B248E35A32F76F3384DDFB3EA7727B7D088AE977B3C649E152CCA06149F546CE` | `39F97B25079CE5F12340CCE43C9DBD5821C5E23F44DA58DD6F1942E135BC93B5` | `39F97B25079CE5F12340CCE43C9DBD5821C5E23F44DA58DD6F1942E135BC93B5` | -| `Main_422_10_A_RExt_Sony_2_frame0.bit` | `7618CCC9C836EEC9A981910F4E0E46DC36936D620755857005CF6F85BBD5CC84` | `4004FE9C86D10F36ACC6E111B8E6A473351CEECEB6B0649F1D3EFB442D27A231` | `5D6DD0506E85BFD17CFD8D676107927C2860D38D1E8B7E5486647279E6B33876` | -| `Main_422_10_B_RExt_Sony_2_frame0.bit` | `9A8CB40F0AC87635ADA427EFE80DEAAD7FE45C8ABD2E72BEF258D5EC0914C086` | `951ACC9C846DCB9F8CCBDC0B6971A947144BAED2D9021AE4BE77BCCE106BBE21` | `951ACC9C846DCB9F8CCBDC0B6971A947144BAED2D9021AE4BE77BCCE106BBE21` | -| `PERSIST_RPARAM_A_RExt_Sony_3_frame0.bit` | `8237DFE09F09E9AD390C04A30FA57D66F26AB75B9DA90C6B482BA1330DCDBEBB` | `5E1A69D29278E3E4CBD7695C7D3C229AC63C1E5E2B7AF3B1C8F8062586EA1468` | `B6A86FF4122512FDA96EC6C4C7A143079EEE5371760433C05428749140036CEC` | -| `TSCTX_8bit_I_RExt_SHARP_1_frame0.bit` | `E15D63CF239A2F4297EA6EE1E404CB02F19862929270448D0668BD01A5A3D352` | `FCB7B066FF751FE3BA72C0583D289C73B61BE3DB59691B4423ED10B9F00A5F25` | `2849C0B451F6DF2945903B5274E59C87D30E439744FAC6B5E9ECB7014EF23488` | -| `TSCTX_10bit_I_RExt_SHARP_1_frame0.bit` | `813F1951C6EB2C1F469098A05D72FBB23BE0FD1C9D15FCB93C54DA74C0FE9169` | `4B62369D263F475DD560D9F129760100C3EE429F14A515DA23D5A7A622B9368A` | `C27C76EA271243EF36D372F86551012E9E2380D92DEAED84F0E2218108810696` | -| `TSCTX_12bit_I_RExt_SHARP_1_frame0.bit` | `96CC6851668F4968EB439FC4E4823E73394415C4D26B9032AB5EC401E753F0FC` | `7A9608103F06AA750F0A07ABE42693F87B77E38EE7BBF2E47D1D0AF9A1CD958D` | `A5A766D2CEB72DEFF1006107A5A5E65290434F2CCA9EBAAA1696E9EF19898067` | -| `WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit` | `C813326651E42576644264EA0F82D03DD79081F6E3163C1C0F283EE6AC03F4C2` | `25DBD378D19E10B020F413213A3CA183FF4C5132500DDECF84D7D9D22218F301` | `DA432C41E52F4DB66B8D2878D8A14A95A9320F4252851EC3242DD3800034377F` | -| `WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit` | `E0703E36A275DB2715D2051C72F0B4FC392C3CBA9421C069FD58EE037823C8DD` | `00075C57F3C1E17619904B60C5352C52814EAFF495012DF453763FE7A5DCB9F2` | `B5EAC077AA30EE4D228F5D89DA9EFD1588BF046B8F521D55C4E53A9455B99217` | -| `WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit` | `FDFD79B04A4FAEC48E7536E4FFCF692B84B2C5945253A9E8EDFA382816B27335` | `2B7110F868BFB62DA8282A0A6300408D7ABAD70A5A940A6A0BA1FBC69536F870` | `741854FDF832F4BD144FB9F68E49119FE4094C5305EEC00472129F00C1690261` | -| `WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit` | `A852B1501950E9CE50827FD7E65043511115FFC6ACA0E5E68A1B6B25932172B9` | `BDC2868A887A56A58AAEF4046F4341525C320616834189C9E2D2F582E08340C7` | `25FD77A0BB9E41A7AD145CBBAB044F981508C506FD25B8F8F6155EB71EC082C2` | -| `WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit` | `4D8135BD94FE6EEFFD58E162999241F6068CCCB67C377C18A18C25B33EF34A1D` | `BE5BE06A8DD2C873E8B386B4E4EAB2F45713277F1D6F2E6BDE5C96E6D7426AF4` | `A6DAE7D643DCB2918465D82347877E6BB70F8BE30C3F9D90C9515DC4DF55A468` | - -The loop-filter fixtures are the unchanged Annex B payloads from the official HEVC v1 and Range Extensions archives. `HevcPictureDecoderTests.DecodeOfficialLoopFilterPictureMatchesPinnedHmDigest` retains each first independently coded picture, verifies its signaled precision and chroma layout, and compares every native plane with output from the pinned HIGH_BITDEPTH HM decoder. The feature-runner companion repeats the same production decoder path through every available SIMD tier and the scalar fallback. - -- `DBLK_A_SONY_3.bit` is the 8-bit 4:2:0 deblocking stream. The archive SHA-256 is `CE18CD42375359B9FF293D12A95793CC6B56EB75D899AD012C68E25DFA54DFBF`, and the bitstream SHA-256 is `D61A13E04A1678816C0F16CF7A47F5BB918DE95ECF1B6FAB305E2390C3DD7DE9`. Pinned HM reproduces the published complete-output MD5 `42486a48ea12d5ab6cd98ed2e2807cfe`; the retained first picture has Y, Cb, and Cr MD5 values `3ea2c2ef1f973345111480e7658908b3`, `0390b32143b1a832a385f78229e7e574`, and `8388f3a8af827da46f1fb52941ec00ad`. -- `DBLK_A_MAIN10_VIXS_4.bit` is the Main 10 negative-QP deblocking stream. The archive SHA-256 is `D86EC87F98FCCBCEC3758954A7CF430E8AF8CF021D347DFBBED2F1D5DBB00F25`, and the bitstream SHA-256 is `3A9089207BEDDDB0C8DB28B410441E37D4CFF5DD8301CB8BA7D2F75036E2D50C`. Pinned HM reproduces the published complete-output MD5 `c4594956bb9e8303f1662f9eb1bcdf50`; the retained 10-bit 4:2:0 first picture has plane MD5 values `184a72aab144cb474df3c1a289e8692d`, `d30e750dd70cae163d00f441a75896fd`, and `d253c41f06228df215f4616febbad34f`. -- `SAO_A_MediaTek_4.bit` is the 8-bit 4:2:0 sample-adaptive-offset stream. The archive SHA-256 is `E0B5C8F4AB4FB592971F06469DB630D9E7EF72B4D37D729604E2FDAA9DF706C9`, and the bitstream SHA-256 is `88F693AC4AEC4DEA03CB4BC0CB9D8C98A4023A015905369BB62688064FB58944`. Pinned HM reproduces the published complete-output MD5 `272e694a2262a2b34f6248f787a4431d`; the retained first picture has plane MD5 values `08723eb3fb41af96c87becc4f6973234`, `230778eb7df0ebc009ca92e9697ec4d6`, and `e8e21ed380d2272dc38384ecd6515e53`. -- `SAO_A_RExt_MediaTek_1.bit` is the 12-bit 4:4:4 Range Extensions stream for PPS bit-shift scaling of SAO offsets. The archive SHA-256 is `D0B5646150B35FB8E4C1D51F3D451AEDDF964581132C9C49B253E34556BEEA4D`, and the bitstream SHA-256 is `6B0D45B5CA4D4919EAEC5A98DAF0EB4CEBCC6D8B0A12B30B748386CC9358DF13`. Pinned HM reproduces the published complete GBR-output MD5 `126cd42a185b327d86640a9269044bc8`; the retained first picture has G, B, and R plane MD5 values `fb342158a61b6cb3174b99d2e1167d7d`, `9ff5400aac0380474882acb903f51f89`, and `bb320be3c7905a5a220e0066a5edb991`. - -The parallelization fixtures are unchanged Annex B payloads from the official HEVC v1 attachment. Archives that name their payload `.bin` are retained byte-for-byte with the repository's common `.bit` extension. `HevcPictureDecoderTests.DecodeOfficialParallelizationPictureMatchesPublishedReference` adapts every complete first IDR picture to one bounded `hvc1` item, asserts its tile, wavefront, dependent-slice, entry-point, extension, and CTB geometry, and compares all three native planes exactly. - -Pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces the published complete-output MD5 for every dependent-slice, tile, and entry-point archive below. The WPP first-picture plane hashes come directly from the published YUV files included in their archives. The two combined dependent-slice cases also pass through 4 KiB allocator groups with balanced exactly-once disposal. - -- `DSLICE_A_HHI_5.bit`: archive SHA-256 `C41C1A1E1E700AF201EAF4CB0D11D4361B41BA0828E80C78D1835D1DE187F80C`; bitstream SHA-256 `8398FB23C814A197BBA497AD2C6103F81CA8003434FA40EC347D4D0A07C9468A`; published output MD5 `c7caf3164b0a316549ac7244f66f1294`. -- `DSLICE_B_HHI_5.bit`: archive SHA-256 `993B933AC6801899B6F04AE5DD2C20FBC2498FD9710A924611E66DE2F690716B`; bitstream SHA-256 `DB582F1181F3B8ED3BEEB32246A1A99123700DBDC3D3BE4E1797032FCD46C486`; published output MD5 `2e072130f116a4d0f7fc69fa76cfcdfb`. -- `DSLICE_C_HHI_5.bit`: archive SHA-256 `6836566F0A4465775B4757B84D7A89F453B635EBA2B941AE07132F7C7268C116`; bitstream SHA-256 `F162007F604AA0E48229C4153F940FD5F24DB955AD158D11E2BD46C89C49CA76`; published output MD5 `505a47c5ce4bc66ef5661aeb778b0f1f`. -- `TILES_A_Cisco_2.bit`: archive SHA-256 `BD63079966105A3BDAC764979CB82B708F586CC7C34859A1D466E18CC6A5C345`; bitstream SHA-256 `EFF78A401ECCCC21D995345988F1BE60EE76604CF10FA39D421C3E00668A94D6`; published output MD5 `946f0771fa87d49f684f96871c9c3c88`. -- `TILES_B_Cisco_1.bit`: archive SHA-256 `75B3C9B2981CDA59D497750322CDE7CA23D6CEF39FA9A8E11F36926758B7F83B`; bitstream SHA-256 `D2F65167917E42030DB804C60CA7775452445A61930CCEE3FA2AD2CD3830A57A`; published output MD5 `3382291f2b19ee2d760647d4cc756e95`. -- `WPP_A_ericsson_MAIN_2.bit`: archive SHA-256 `81C601E9301BA5F562EBF9918E73D1C7153E0A6FE8FA4509F61035A212188216`; bitstream SHA-256 `54D896D9FBDFA0AAE15629001105C6EE132C8459E152ABB06EFC62CEAD4324AE`; published output MD5 `cd7e815eb47e8138fec2185d4de84304`. -- `WPP_B_ericsson_MAIN_2.bit`: archive SHA-256 `57BFA7DC2417976825187B43829F1E107D87EBE661CFA4A02087EFAE3BD1148E`; bitstream SHA-256 `F1D18F737A9380F6CE58B6AEAE158AF458C5B557E06DD24977B7A5C6E095B9B2`; published output MD5 `e37c7e561a1226640a7bf98e81df78b1`. -- `WPP_C_ericsson_MAIN_2.bit`: archive SHA-256 `4D1F3E7FC1B05E22F2C53FABC670A3DB9A642BB511004C4D785B49582890BDF2`; bitstream SHA-256 `21CF0A7C5F6FBA5A76C7132A2714313D120D1443A0130400FB55AB1E454D5BDA`; published output MD5 `e067aa3a6a12cd5743849ded793c8d3f`. -- `WPP_D_ericsson_MAIN_2.bit`: archive SHA-256 `04C166DF6E6D68584910FCE77055F6C16428FC5C907BB1BEFD10BBF40C16B939`; bitstream SHA-256 `30EEC63F2324AA982FB91BD4C1C551C833C253BA711EE131AA9CC4D322398CAF`; published output MD5 `f710612103f386c415be3e6300693451`. -- `WPP_E_ericsson_MAIN_2.bit`: archive SHA-256 `10F4A50870F763D7CCBD975ADCE20176022A1B9712664244A94C069322534FAF`; bitstream SHA-256 `C8FE49762A13E1CC2B033308BB22AEB35DA0C96F94CA8EF7D87D95BDADAEAA2C`; published output MD5 `485798dbf95ad61232075df2f294aa3f`. -- `WPP_F_ericsson_MAIN_2.bit`: archive SHA-256 `39F5FFAA4C273C800B4E05715E2D4002E31B3BBE39505EA59B08C5FDABE6AF21`; bitstream SHA-256 `E8566E0E48509592DFAF7D314B7E292F51CEDE045559E3C32B447A9822A8B949`; published output MD5 `2aaf16274fe8e799d72fa08a4963850d`. -- `ENTP_A_Qualcomm_1.bit`: archive SHA-256 `66ABDF60455ECE468ACF160274316C51DCA3858D93A8FFCE93646CBB5568FE43`; bitstream SHA-256 `1DC1DFB16F676F59CEBDE3A4732209CE5CE0D0F49EF697970987C0C3E0E087B1`; published output MD5 `25f3c7facf9e7b75e81247206b4fc60d`. -- `ENTP_C_Qualcomm_1.bit`: archive SHA-256 `6CFDFE2F6BA8E1A860909550E5A486E7BF712FED00674869F45C1D82D05833F5`; bitstream SHA-256 `993CD68F0E749CD1D9CE7CC3A368B4DF26DCCF07DED91048100217B9A0C56D28`; published output MD5 `0b607e6e7946e6e8d0f92a1a294d135d`. - -The retained `ENTP_A_Qualcomm_1` first picture verifies its uniform 2x2 tile entry points. The archive description places its special entry-point emulation-prevention case at POC 4, which is outside the retained IDR picture. `HevcPictureDecoderTests.EntryPointOffsetsExcludeEmulationPreventionBytes` therefore verifies the same production boundary-conversion methods directly with prevention bytes in both the encoded header and the bounded non-final entropy substream; it does not claim that the later non-IDR picture itself is decoded. diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit deleted file mode 100644 index 8466c1c00..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c07a976943200d97ba9eb0a16a858d2c77fb3fe37410c6c6f956e5960b2f4621 -size 66244 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit deleted file mode 100644 index 6a9a096ac..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:49a8037bbbed4efa8de285c522d215c3c5a117dd58d17822db0ffb9808161e29 -size 66149 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit deleted file mode 100644 index bc5295c2c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7b157215595b68ddbb70d952fb786923826c26ecc448bbfe36f4df47f9abc5c -size 66094 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit deleted file mode 100644 index 052db04bb..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b37468d7e4acf7d1fb97bf0062e0124785d3bda1284e1f698a1e8eb6441d76b -size 66078 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit deleted file mode 100644 index 111213496..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d792e442c19d9ffc45f43a8842c7a6ad57d0a51d62364c43ef97810689c2eea8 -size 84820 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_MediaTek_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_MediaTek_4.bit deleted file mode 100644 index d0726f980..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_MediaTek_4.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:88f693ac4aec4dea03cb4bc0cb9d8c98a4023a015905369bb62688064fb58944 -size 52606 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_RExt_MediaTek_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_RExt_MediaTek_1.bit deleted file mode 100644 index 62febb8cb..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/SAO_A_RExt_MediaTek_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b0d45b5ca4d4919eaec5a98daf0eb4cebcc6d8b0a12b30b748386cc9358df13 -size 1466031 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit deleted file mode 100644 index 144dcaa04..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e511007d79d66f6988654a7f89a934cd68329be6492143cda9570e3741f5a551 -size 319053 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit deleted file mode 100644 index 654dd6ea6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e58b04f1434449721957857ca38adab3865b71cb93ae9705a4361b7cb4400d8e -size 324322 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit deleted file mode 100644 index 23b755d90..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eff78a401ecccc21d995345988f1be60ee76604cf10fa39d421c3e00668a94d6 -size 484767 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit deleted file mode 100644 index 024a8c1a4..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d2f65167917e42030db804c60ca7775452445a61930ccee3fa2ad2cd3830a57a -size 533127 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.bit deleted file mode 100644 index e4bf33b4e..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c27c76ea271243ef36d372f86551012e9e2380d92deaed84f0e2218108810696 -size 165113 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.heic deleted file mode 100644 index 52687367e..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_10bit_I_RExt_SHARP_1_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ff99931c1da27fb150f17b67dc1bf8f167439e78ef5611f51af5ecddfba5b720 -size 165477 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.bit deleted file mode 100644 index 7a894a09a..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5a766d2ceb72deff1006107a5a5e65290434f2cca9ebaaa1696e9ef19898067 -size 382534 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.heic deleted file mode 100644 index 8696a5f9a..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_12bit_I_RExt_SHARP_1_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b3031636e37f8534f28fa54304287208784476ae709f52f64c54cbf5e55a3773 -size 382898 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.bit deleted file mode 100644 index deba4e447..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2849c0b451f6df2945903b5274e59c87d30e439744fac6b5e9ecb7014ef23488 -size 165586 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.heic deleted file mode 100644 index 1f55945f7..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TSCTX_8bit_I_RExt_SHARP_1_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4a1653681d5dcde1e0b9f35c7ba6e6d546cf237f4c8d724123da631e2c57148 -size 165950 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit deleted file mode 100644 index 7475185e8..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:23c9959a0dd09d9a0b9c64b980794ca18214d7a3cb0fd091ca87f98dda75acf4 -size 1238476 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit deleted file mode 100644 index ab4f9f6cf..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da432c41e52f4db66b8d2878d8a14a95a9320f4252851ec3242dd3800034377f -size 37152 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic deleted file mode 100644 index b169e63b3..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_10Bit422Test_HIGH_TP_444_10BIT_RExt_Apple_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e7c9278743e5cf8b964953fb15ac0590a9fb476959c995a8089e730d0d531d29 -size 37516 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit deleted file mode 100644 index c6c7164d9..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:25fd77a0bb9e41a7ad145cbbab044f981508c506fd25b8f8f6155eb71ec082c2 -size 42150 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic deleted file mode 100644 index 3d44ff999..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_BYPASS_ALIGN_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:be215d5a7ab4a91b99080ed3e80bf48c93f921003a6642c2d4c92c779b8211df -size 42514 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit deleted file mode 100644 index 9ad5d1dad..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6dae7d643dcb2918465d82347877e6bb70f8be30c3f9d90c9515dc4df55a468 -size 41800 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic deleted file mode 100644 index 55ca09470..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_AND_CABAC_EXT_PREC_1_HIGH_TP_444_14BIT_RExt_Apple_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a685b86dd0b6a7eefdf268f8d908e61a0fb6154aaaa86bb640b04b3e7d6fd14 -size 42164 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit deleted file mode 100644 index 25ba27fdd..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b5eac077aa30ee4d228f5d89da9efd1588bf046b8f521d55c4e53a9455b99217 -size 41776 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic deleted file mode 100644 index 1fb7de8d3..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_AND_TILE_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:099e54d9b44702a852b9c6c37f70bd2eb31d3a0bd9ba16767e09e6ce2904fc33 -size 42140 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit deleted file mode 100644 index c22e03f93..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54d896d9fbdfa0aae15629001105c6ee132c8459e152abb06efc62cead4324ae -size 67554 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit deleted file mode 100644 index 790f5d5b7..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1d18f737a9380f6ce58b6aeae158af458c5b557e06dd24977b7a5c6e095b9b2 -size 68895 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit deleted file mode 100644 index 77b1994d3..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:21cf0a7c5f6fba5a76c7132a2714313d120d1443a0130400fb55ab1e454d5bda -size 71856 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit deleted file mode 100644 index 8aa7b24e6..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:30eec63f2324aa982fb91bd4c1c551c833c253ba711ee131aa9cc4d322398caf -size 22474 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit deleted file mode 100644 index 522863c1b..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c8fe49762a13e1cc2b033308bb22aeb35da0c96f94ca8ef7d87d95bdadaeaa2c -size 29642 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit deleted file mode 100644 index 350f57cfc..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8566e0e48509592dfaf7d314b7e292f51cede045559e3c32b447a9822a8b949 -size 31461 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit deleted file mode 100644 index 18068e86c..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.bit +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:741854fdf832f4bd144fb9f68e49119fe4094c5305eec00472129f00c1690261 -size 41569 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic b/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic deleted file mode 100644 index df945ac32..000000000 --- a/tests/Images/Input/Heif/Hevc/Conformance/WPP_HIGH_TP_444_8BIT_RExt_Apple_2_frame0.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:000c9e50da7654fc76374a42a8ea3ead12890b02e26fedd103e42fd57d28aae5 -size 41933 diff --git a/tests/Images/Input/Heif/Hevc/dwsample-tile-hm.yuv b/tests/Images/Input/Heif/Hevc/dwsample-tile-hm.yuv deleted file mode 100644 index e6900655f..000000000 --- a/tests/Images/Input/Heif/Hevc/dwsample-tile-hm.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:655e8839c763574402cc02a1671204bc6b0faf22df7168eca3cbd917cc5a8890 -size 821760 diff --git a/tests/Images/Input/Heif/Hevc/dwsample-tile.hvc1 b/tests/Images/Input/Heif/Hevc/dwsample-tile.hvc1 deleted file mode 100644 index e70be49aa..000000000 --- a/tests/Images/Input/Heif/Hevc/dwsample-tile.hvc1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c06484dc3196ed6c7f5e46ac7e10aaca1703541261e59107734b281537fe4f8 -size 191419 diff --git a/tests/Images/Input/Heif/Hevc/dwsample-tile.hvcc b/tests/Images/Input/Heif/Hevc/dwsample-tile.hvcc deleted file mode 100644 index 97708f07e..000000000 --- a/tests/Images/Input/Heif/Hevc/dwsample-tile.hvcc +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d093eebbfacf797903c222c3b85512b25e741f11af1e660be9ec40b03a9cd4ef -size 113 diff --git a/tests/Images/Input/Heif/Hevc/image1-tile1-hm.yuv b/tests/Images/Input/Heif/Hevc/image1-tile1-hm.yuv deleted file mode 100644 index 771b5e405..000000000 --- a/tests/Images/Input/Heif/Hevc/image1-tile1-hm.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d7a5c74c14d91ffb39cd4846f0b57e8a88fabd31935ab065e2c6f90817ed54db -size 393216 diff --git a/tests/Images/Input/Heif/Hevc/image1-tile1.hvc1 b/tests/Images/Input/Heif/Hevc/image1-tile1.hvc1 deleted file mode 100644 index a91511999..000000000 --- a/tests/Images/Input/Heif/Hevc/image1-tile1.hvc1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd8846579cc77df772687ebb89dbbda9ff192ad4ffdfaa080d8de7970fecd397 -size 106458 diff --git a/tests/Images/Input/Heif/Hevc/image1-tile1.hvcc b/tests/Images/Input/Heif/Hevc/image1-tile1.hvcc deleted file mode 100644 index 58e61e05a..000000000 --- a/tests/Images/Input/Heif/Hevc/image1-tile1.hvcc +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd729786f48693c2584175658291cffbae94cda18502735a383eeb61853b8f0d -size 102 diff --git a/tests/Images/Input/Heif/Hevc/image1-tile2-hm.yuv b/tests/Images/Input/Heif/Hevc/image1-tile2-hm.yuv deleted file mode 100644 index 0f15687f4..000000000 --- a/tests/Images/Input/Heif/Hevc/image1-tile2-hm.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4aa91b707f756ce52d6675283f77bd555352e6738ca0137dfe8526e593c0abb0 -size 393216 diff --git a/tests/Images/Input/Heif/Hevc/image1-tile2.hvc1 b/tests/Images/Input/Heif/Hevc/image1-tile2.hvc1 deleted file mode 100644 index 691e7e4eb..000000000 --- a/tests/Images/Input/Heif/Hevc/image1-tile2.hvc1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:976b53167fd8881b71b04b3e3e7fb9f9792c05f9e49ca682bac60a8d096bbafa -size 105758 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile.hvcc b/tests/Images/Input/Heif/Hevc/image2-tile.hvcc deleted file mode 100644 index 1e0a32339..000000000 --- a/tests/Images/Input/Heif/Hevc/image2-tile.hvcc +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91b49d1a8ff5e91ea2fafe74df88538a85b76c7dba8086c94799c4a178cf46c2 -size 101 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile1-hm.yuv b/tests/Images/Input/Heif/Hevc/image2-tile1-hm.yuv deleted file mode 100644 index 893c67e2c..000000000 --- a/tests/Images/Input/Heif/Hevc/image2-tile1-hm.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:69b8abe9e4faac13f1b7c212dae7da63716a87aa3cd8d7e9c70f00457b1c3fb2 -size 393216 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile1.hvc1 b/tests/Images/Input/Heif/Hevc/image2-tile1.hvc1 deleted file mode 100644 index 111292e5e..000000000 --- a/tests/Images/Input/Heif/Hevc/image2-tile1.hvc1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c6eecfa72cb4fc895d9421f35f0fb916b0b54704618c66a135a48a7bfb7f1fd5 -size 21099 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv b/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv deleted file mode 100644 index 15e68c75a..000000000 --- a/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d85526927c19fd642b7173215767d2e822e4d0eda58eaae3bb5e3a498659ea4f -size 393216 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 b/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 deleted file mode 100644 index 0ca9b368e..000000000 --- a/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6bd5ebc70cea2c8359a3afdd81eed0cdb9e86616516a7b974a7f8017139bb939 -size 7945 diff --git a/tests/Images/Input/Heif/IMG-20230508-0053.hif b/tests/Images/Input/Heif/IMG-20230508-0053.hif deleted file mode 100644 index 0d0c65727..000000000 --- a/tests/Images/Input/Heif/IMG-20230508-0053.hif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ff1be6d42e8dbfc4d072bb73f4450d2afabad176b7aece108b51f8429f400edc -size 12367360 diff --git a/tests/Images/Input/Heif/dwsample-heic-640.heic b/tests/Images/Input/Heif/dwsample-heic-640.heic deleted file mode 100644 index 5b40f44e0..000000000 --- a/tests/Images/Input/Heif/dwsample-heic-640.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9249ef2c23113ee8a5de5e99f3eff353652abbb081bc26a66772769ee03c51b4 -size 191883 diff --git a/tests/Images/Input/Heif/image1.heic b/tests/Images/Input/Heif/image1.heic deleted file mode 100644 index 6266a4bc1..000000000 --- a/tests/Images/Input/Heif/image1.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:335699d7ba7b4b4581aed0d995aefa57ef1e6771b66595719768cd313fa2430c -size 2994394 diff --git a/tests/Images/Input/Heif/image2.heic b/tests/Images/Input/Heif/image2.heic deleted file mode 100644 index 5b26cf33c..000000000 --- a/tests/Images/Input/Heif/image2.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ca296eaa15c4dd912a6e0dd76ce3bc9cbe80a61e570043b00ecfaba4d70d6a6 -size 2540019 diff --git a/tests/Images/Input/Heif/image3.heic b/tests/Images/Input/Heif/image3.heic deleted file mode 100644 index 00867b04e..000000000 --- a/tests/Images/Input/Heif/image3.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:85b3098fb5b0f364958c0e18a5ec13ddde5369826db936021dfa909a3a85b46a -size 1148790 diff --git a/tests/Images/Input/Heif/image4.heic b/tests/Images/Input/Heif/image4.heic deleted file mode 100644 index 3ec147e66..000000000 --- a/tests/Images/Input/Heif/image4.heic +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:676b0a76dcaa7fe9ffc41110b7791bef6cf0bfdb32455b03e149b0f8bfdb0856 -size 41465