From 93f48a480d87796fd1860708750ef514c90dde3c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 8 Sep 2026 10:41:39 +1000 Subject: [PATCH] Restore shared decoder, ICC, and Point APIs to upstream main Restore ImageDecoderCore, DecoderOptions, IccProfile, Point, and PointTests. Keep HEIF box-integrity policy inside HeifDecoderCore, use the existing ICC serialization API, and replace AV1 Point shifts with coordinate shifts. Include the PixelOperations formatting correction. The current implementations passed Release net11.0 compilation, 55 Point tests, 70 public HEIF encoder/grid tests, and 38 sequence tests. This commit stages the shared API restorations and their caller changes only; the region and component-domain ICC refactor remains in progress and is not claimed as a verified codec checkpoint. --- src/ImageSharp/Formats/DecoderOptions.cs | 2 +- .../Formats/Heif/Av1/Av1CodecConfiguration.cs | 4 +- .../Av1IntraSuperblockEncoder.ModeDecision.cs | 12 +-- ...SuperblockEncoder.ReferenceModeDecision.cs | 2 +- .../Av1/Pipeline/Av1IntraSuperblockEncoder.cs | 2 +- .../Formats/Heif/Av1/Tiling/Av1TileWriter.cs | 6 +- .../Formats/Heif/HeifDecoderCore.cs | 60 +++++++++++--- .../Formats/Heif/HeifEncoderCore.Sequence.cs | 2 +- src/ImageSharp/Formats/Heif/HeifItem.cs | 2 +- .../Formats/Heif/HeifSequenceParser.cs | 12 +-- src/ImageSharp/Formats/ImageDecoderCore.cs | 80 ++++--------------- .../Metadata/Profiles/ICC/IccProfile.cs | 16 ---- .../PixelFormats/PixelOperations{TPixel}.cs | 1 + src/ImageSharp/Primitives/Point.cs | 36 --------- .../Av1/Av1IntraSuperblockEncoderTests.cs | 2 +- .../ImageSharp.Tests/Primitives/PointTests.cs | 23 +----- 16 files changed, 90 insertions(+), 172 deletions(-) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index f1925913ce..916888af33 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -78,7 +78,7 @@ public sealed class DecoderOptions return false; } - if (this.ColorProfileHandling != ColorProfileHandling.Convert) + if (this.ColorProfileHandling == ColorProfileHandling.Preserve) { return false; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs b/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs index 5e39a3f5d7..9ec06fc916 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs @@ -382,7 +382,7 @@ internal sealed class Av1CodecConfiguration contentLightLevel = dataObuContentLightLevel ?? this.configContentLightLevel; masteringDisplayColorVolume = dataObuMasteringDisplayColorVolume ?? this.configMasteringDisplayColorVolume; } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(options, ex)) { // Conflicting optional OBU metadata is discarded without weakening OBU framing or sequence-header checks. } @@ -563,7 +563,7 @@ internal sealed class Av1CodecConfiguration masteringDisplayColorVolume = obuMasteringDisplayColorVolume; } } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(options, ex)) { // The OBU payload remains bounded by the image-data scan; only its invalid optional metadata is discarded. } diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs index 324d1866bb..5cdd79ecf0 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs @@ -88,7 +88,7 @@ internal static partial class Av1IntraSuperblockEncoder ref int partitionIndex) { Av1EncoderCommon common = picture.Parent.Common; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); if (modeInfoPosition.Y >= common.ModeInfoRowCount || modeInfoPosition.X >= common.ModeInfoColumnCount) { return; @@ -219,7 +219,7 @@ internal static partial class Av1IntraSuperblockEncoder return preparedPartition; } - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); bool hasRows = modeInfoPosition.Y + blockSize.Get4x4HighCount() <= this.picture.Parent.Common.ModeInfoRowCount; @@ -479,7 +479,7 @@ internal static partial class Av1IntraSuperblockEncoder private bool IsBlockOriginInsideFrame(Point blockOrigin) { - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); return modeInfoPosition.Y < this.picture.Parent.Common.ModeInfoRowCount && modeInfoPosition.X < this.picture.Parent.Common.ModeInfoColumnCount; } @@ -596,7 +596,7 @@ internal static partial class Av1IntraSuperblockEncoder }; modeInfo.CdefStrength = 0; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); block.HasChroma = !this.source.IsMonochrome && Av1TileReader.HasChroma(this.picture.Sequence.SequenceHeader, modeInfoPosition, blockSize); @@ -768,7 +768,7 @@ internal static partial class Av1IntraSuperblockEncoder { // Trial leaves must use the same reconstruction order as final leaves of this partition. this.SetBlockGeometry(blockOrigin, blockSize, partitionType); - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); Av1TileWriter.SetModeInfoRowAndColumn( this.picture, macroBlock, @@ -813,7 +813,7 @@ internal static partial class Av1IntraSuperblockEncoder Av1BlockSize blockSize, Av1PartitionType partitionType) { - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); ref Av1MacroBlockModeInfo modeInfo = ref this.picture.GetMacroBlockModeInfo(modeInfoPosition); modeInfo.Block = new Av1EncoderBlockModeInfo { diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ReferenceModeDecision.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ReferenceModeDecision.cs index 717d3c19fe..a5223e8918 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ReferenceModeDecision.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ReferenceModeDecision.cs @@ -480,7 +480,7 @@ internal static partial class Av1IntraSuperblockEncoder this.blockWorkspace.GetInterPredictionWorkspace(); ObuFrameHeader frameHeader = this.picture.Parent.FrameHeader; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); ref Av1ReferenceMotionVectors referenceMotionVectors = ref this.blockWorkspace.ReferenceMotionVectors; referenceMotionVectors.Build( this.picture, diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.cs index e815a89ce5..5375f9681c 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.cs @@ -187,7 +187,7 @@ internal static partial class Av1IntraSuperblockEncoder public void EncodePartitionTree(Point blockOrigin, Av1BlockSize blockSize) { Av1EncoderCommon common = this.picture.Parent.Common; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); if (modeInfoPosition.Y >= common.ModeInfoRowCount || modeInfoPosition.X >= common.ModeInfoColumnCount) { return; diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs index e1cebda557..2233f58794 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs @@ -282,7 +282,7 @@ internal partial class Av1TileWriter (childIndex & 1) * halfBlockSize, (childIndex >> 1) * halfBlockSize); - Point childModeInfoPosition = childOrigin >> Av1Constants.ModeInfoSizeLog2; + Point childModeInfoPosition = new(childOrigin.X >> Av1Constants.ModeInfoSizeLog2, childOrigin.Y >> Av1Constants.ModeInfoSizeLog2); if (childModeInfoPosition.Y >= common.ModeInfoRowCount || childModeInfoPosition.X >= common.ModeInfoColumnCount) { @@ -791,7 +791,7 @@ internal partial class Av1TileWriter out bool hasColumns) { int halfBlockModeInfoCount = blockSize.Get4x4WideCount() >> 1; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); hasRows = modeInfoPosition.Y + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoRowCount; hasColumns = modeInfoPosition.X + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoColumnCount; int leftIndex = partitionContexts.GetLeftIndex(blockOrigin); @@ -2024,7 +2024,7 @@ internal partial class Av1TileWriter Av1NeighborArrayUnit luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx]; - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); ref Av1MacroBlockModeInfo mbmi = ref pcs.GetMacroBlockModeInfo(modeInfoPosition); bool skip_coeff = mbmi.Block.Skip; diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 0c054b84d5..80f622af43 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -1289,12 +1289,12 @@ internal sealed class HeifDecoderCore : ImageDecoderCore this.itemLinks.Add(link); } - catch (Exception ex) when (linkType == Heif4CharCode.Cdsc && ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (linkType == Heif4CharCode.Cdsc && HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // A malformed descriptive link cannot change reconstructed pixels, so non-strict modes omit it. bytesRead = referenceEnd; } - catch (Exception ex) when (linkType != Heif4CharCode.Cdsc && ImageDecoderCore.ShouldIgnoreImageDataSegmentError(this.Options, ex)) + catch (Exception ex) when (linkType != Heif4CharCode.Cdsc && HeifDecoderCore.ShouldIgnoreImageDataSegmentError(this.Options, ex)) { // IgnoreImageData permits a malformed optional image relationship to be omitted while retaining // independently reconstructable items and thumbnail fallbacks. @@ -1440,7 +1440,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { iccProfile = HeifPropertyParser.ParseIccProfile(profileData); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1482,7 +1482,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { pixelAspectRatio = HeifPropertyParser.ParsePixelAspectRatio(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1555,7 +1555,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { contentLightLevel = HeifPropertyParser.ParseContentLightLevel(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1568,7 +1568,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { masteringDisplayColorVolume = HeifPropertyParser.ParseMasteringDisplayColorVolume(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1581,7 +1581,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { contentColorVolume = HeifPropertyParser.ParseContentColorVolume(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1594,7 +1594,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { ambientViewingEnvironment = HeifPropertyParser.ParseAmbientViewingEnvironment(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1607,7 +1607,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { referenceViewingEnvironment = HeifPropertyParser.ParseReferenceViewingEnvironment(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1620,7 +1620,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore { nominalDiffuseWhite = HeifPropertyParser.ParseNominalDiffuseWhite(boxBuffer); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.Options, ex)) { // Keep the understood property index without retaining invalid ancillary metadata. } @@ -1683,7 +1683,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore break; } } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreImageDataSegmentError(this.Options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreImageDataSegmentError(this.Options, ex)) { // Invalid image properties retain their physical association index. Typed association handling ignores // the placeholder so another decodable item or the coded-image defaults can remain usable. @@ -3153,4 +3153,42 @@ internal sealed class HeifDecoderCore : ImageDecoderCore bytesRead = terminator + 1; return Encoding.UTF8.GetString(span[..terminator]); } + + /// + /// Determines whether a malformed ancillary box can be omitted under the selected integrity policy. + /// + public static bool ShouldIgnoreAncillarySegmentError(DecoderOptions options, Exception exception) + => options.SegmentIntegrityHandling is not SegmentIntegrityHandling.Strict + && IsRecoverableBoxError(exception); + + /// + /// Determines whether a malformed image-data box can be omitted under the selected integrity policy. + /// + internal static bool ShouldIgnoreImageDataSegmentError(DecoderOptions options, Exception exception) + => options.SegmentIntegrityHandling is SegmentIntegrityHandling.IgnoreImageData + && IsRecoverableBoxError(exception); + + /// + /// Identifies malformed-box failures eligible for the segment integrity policy. + /// + /// The failure raised while reading the box. + /// Whether the failure describes recoverable image content. + private static bool IsRecoverableBoxError(Exception exception) + => exception is ImageFormatException + or InvalidIccProfileException + or InvalidImageContentException + or InvalidOperationException + or NotSupportedException; + + /// + /// Rejects malformed image properties unless image-data errors are explicitly ignored. + /// + /// The description of the malformed property. + private void ThrowOrIgnoreImageDataSegmentError(string message) + { + if (this.Options.SegmentIntegrityHandling is not SegmentIntegrityHandling.IgnoreImageData) + { + throw new InvalidImageContentException(message); + } + } } diff --git a/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs b/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs index 746a92900c..9e2f52b07e 100644 --- a/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs +++ b/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs @@ -339,7 +339,7 @@ internal sealed partial class HeifEncoderCore IccProfile? iccProfile = image.Metadata.IccProfile; if (!this.encoder.SkipMetadata && iccProfile is not null) { - iccProfileData = iccProfile.GetDataForWriting(); + iccProfileData = iccProfile.ToByteArray(); } return new HeifSequenceEncoding( diff --git a/src/ImageSharp/Formats/Heif/HeifItem.cs b/src/ImageSharp/Formats/Heif/HeifItem.cs index e1c1ec4c3a..b51fc53872 100644 --- a/src/ImageSharp/Formats/Heif/HeifItem.cs +++ b/src/ImageSharp/Formats/Heif/HeifItem.cs @@ -224,7 +224,7 @@ internal sealed class HeifItem(Heif4CharCode type, uint id) { // Exact-size container writing queries the payload length before copying it. Retaining the serialized // view on this transient item prevents an entry-built profile from being serialized for both passes. - this.serializedIccProfile = this.iccProfile.GetDataForWriting(); + this.serializedIccProfile = this.iccProfile.ToByteArray(); } return this.serializedIccProfile; diff --git a/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs b/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs index 6fff6edf2f..3006822a54 100644 --- a/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs +++ b/src/ImageSharp/Formats/Heif/HeifSequenceParser.cs @@ -162,7 +162,7 @@ internal sealed class HeifSequenceParser { ValidateAlphaTrack(colorTrack, alphaTrack); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) { // Alpha is optional image data. IgnoreImageData permits a malformed auxiliary sequence to be omitted while // retaining the independently decodable color presentation. @@ -304,7 +304,7 @@ internal sealed class HeifSequenceParser stream.Position = metadata.Offset; track.Metadata = this.metadataParser.Parse(stream, metadata.Length, scratch); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) { // The validated parent range lets decoding continue safely without this optional metadata box. } @@ -963,7 +963,7 @@ internal sealed class HeifSequenceParser { ParseTrackImageProperty(stream, childLength, childType, track, scratch); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) { // The complete child range remains known, so optional metadata can be discarded safely. } @@ -977,7 +977,7 @@ internal sealed class HeifSequenceParser { ParseTrackImageProperty(stream, childLength, childType, track, scratch); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) { // IgnoreImageData permits a recoverable presentation property to be omitted. } @@ -1125,7 +1125,7 @@ internal sealed class HeifSequenceParser prefix = ReadPrefixFromStart(stream, boxLength, scratch, 11, "color information"); track.CicpProfile = HeifPropertyParser.ParseCicpProfile(prefix[4..]); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreImageDataSegmentError(this.options, ex)) { // IgnoreImageData permits the decoder to fall back to the coded sequence's color description. } @@ -1150,7 +1150,7 @@ internal sealed class HeifSequenceParser HeifBoxReader.ReadExactly(stream, profileData, "Stream length is not sufficient for box content."); track.IccProfile = HeifPropertyParser.ParseIccProfile(profileData); } - catch (Exception ex) when (ImageDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) + catch (Exception ex) when (HeifDecoderCore.ShouldIgnoreAncillarySegmentError(this.options, ex)) { // A malformed optional ICC profile does not invalidate the coded image outside strict mode. } diff --git a/src/ImageSharp/Formats/ImageDecoderCore.cs b/src/ImageSharp/Formats/ImageDecoderCore.cs index a5b238c58d..3f36b8931f 100644 --- a/src/ImageSharp/Formats/ImageDecoderCore.cs +++ b/src/ImageSharp/Formats/ImageDecoderCore.cs @@ -39,7 +39,7 @@ internal abstract class ImageDecoderCore /// The action. protected void ExecuteAncillarySegmentAction(Action action) { - if (!ShouldIgnoreAncillarySegmentErrors(this.Options)) + if (this.Options.SegmentIntegrityHandling is SegmentIntegrityHandling.Strict) { action(); return; @@ -49,7 +49,12 @@ internal abstract class ImageDecoderCore { action(); } - catch (Exception ex) when (IsRecoverableSegmentError(ex)) + catch (Exception ex) when (ex + is ImageFormatException + or InvalidIccProfileException + or InvalidImageContentException + or InvalidOperationException + or NotSupportedException) { // Intentionally ignored in non-strict segment integrity modes. } @@ -61,7 +66,7 @@ internal abstract class ImageDecoderCore /// The action. protected void ExecuteImageDataSegmentAction(Action action) { - if (!ShouldIgnoreImageDataSegmentErrors(this.Options)) + if (this.Options.SegmentIntegrityHandling is not SegmentIntegrityHandling.IgnoreImageData) { action(); return; @@ -71,62 +76,21 @@ internal abstract class ImageDecoderCore { action(); } - catch (Exception ex) when (IsRecoverableSegmentError(ex)) + catch (Exception ex) when (ex + is ImageFormatException + or InvalidIccProfileException + or InvalidImageContentException + or InvalidOperationException + or NotSupportedException) { // Intentionally ignored when image data integrity handling is set to IgnoreImageData. } } - /// - /// Determines whether an exception represents a recoverable image segment error. - /// - /// The exception raised while processing an image segment. - /// when a segment integrity policy may ignore the exception. - public static bool IsRecoverableSegmentError(Exception exception) - => exception is ImageFormatException - or InvalidIccProfileException - or InvalidImageContentException - or InvalidOperationException - or NotSupportedException; - - /// - /// Determines whether the configured policy permits recoverable ancillary-segment errors to be ignored. - /// - /// The general decoder options. - /// when recoverable ancillary-segment errors may be ignored. - public static bool ShouldIgnoreAncillarySegmentErrors(DecoderOptions options) - => options.SegmentIntegrityHandling is not SegmentIntegrityHandling.Strict; - - /// - /// Determines whether the configured policy permits recoverable image-data-segment errors to be ignored. - /// - /// The general decoder options. - /// when recoverable image-data-segment errors may be ignored. - public static bool ShouldIgnoreImageDataSegmentErrors(DecoderOptions options) - => options.SegmentIntegrityHandling is SegmentIntegrityHandling.IgnoreImageData; - - /// - /// Determines whether an ancillary-segment exception may be ignored by the configured decoder policy. - /// - /// The general decoder options. - /// The exception raised while processing an ancillary segment. - /// when decoding may continue without the ancillary segment. - public static bool ShouldIgnoreAncillarySegmentError(DecoderOptions options, Exception exception) - => ShouldIgnoreAncillarySegmentErrors(options) && IsRecoverableSegmentError(exception); - - /// - /// Determines whether an image-data-segment exception may be ignored by the configured decoder policy. - /// - /// The general decoder options. - /// The exception raised while processing an image-data segment. - /// when decoding may continue without the image-data segment. - public static bool ShouldIgnoreImageDataSegmentError(DecoderOptions options, Exception exception) - => ShouldIgnoreImageDataSegmentErrors(options) && IsRecoverableSegmentError(exception); - /// /// Throws unless the decoder is running in a non-strict segment integrity mode. - /// Use this when ancillary parsing must continue locally after the error rather than returning through - /// . + /// Use this only from within when local control flow + /// must continue after the error. /// /// The exception message. protected void ThrowOrIgnoreNonStrictSegmentError(string message) @@ -137,18 +101,6 @@ internal abstract class ImageDecoderCore } } - /// - /// Throws unless the decoder permits recoverable image-data segment errors to be ignored. - /// - /// The exception message. - protected void ThrowOrIgnoreImageDataSegmentError(string message) - { - if (!ShouldIgnoreImageDataSegmentErrors(this.Options)) - { - throw new InvalidImageContentException(message); - } - } - /// /// Reads the raw image information from the specified stream. /// diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs index d337b77e14..eaba0a045c 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs @@ -177,22 +177,6 @@ public sealed partial class IccProfile : IDeepCloneable return IccWriter.Write(this); } - /// - /// Gets the serialized profile storage for an encoder that consumes it before the operation returns. - /// - /// The existing raw profile storage, or a newly serialized profile when this instance was built from entries. - internal ReadOnlyMemory GetDataForWriting() - { - if (this.data is not null) - { - // Internal encoders only read this memory during the current operation, so the immutable view avoids - // cloning an already-owned profile before it is copied into format-specific output. - return this.data; - } - - return IccWriter.Write(this); - } - private void InitializeHeader() { if (this.header != null) diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index e5faf976cf..45c1311c9d 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -348,6 +348,7 @@ public partial class PixelOperations /// /// Bulk operation that packs 3 separate RGB channels to . + /// The destination must contain at least as many pixels as each source channel. /// /// A to the red values. /// A to the green values. diff --git a/src/ImageSharp/Primitives/Point.cs b/src/ImageSharp/Primitives/Point.cs index 8e9cadcbec..99193e3bb0 100644 --- a/src/ImageSharp/Primitives/Point.cs +++ b/src/ImageSharp/Primitives/Point.cs @@ -145,24 +145,6 @@ public struct Point : IEquatable public static Point operator /(Point left, int right) => new(left.X / right, left.Y / right); - /// - /// Shift to the right by a amount producing . - /// - /// Shifted value of type . - /// Shifted amount of type . - /// Result of type . - public static Point operator >>(Point left, int right) - => new(left.X >> right, left.Y >> right); - - /// - /// Shift to the left by a amount producing . - /// - /// Shifted value of type . - /// Shifted amount of type . - /// Result of type . - public static Point operator <<(Point left, int right) - => new(left.X << right, left.Y << right); - /// /// Compares two objects for equality. /// @@ -298,24 +280,6 @@ public struct Point : IEquatable [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(Point point) => this.Offset(point.X, point.Y); - /// - /// Shifts the coordinate value of this to the right with the specified amount. - /// - /// The point to shift. - /// The number of bits to shift to the right. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point ShiftRight(Point point, int bitCount) => new(unchecked(point.X >> bitCount), unchecked(point.Y >> bitCount)); - - /// - /// Shifts the coordinate value of this to the left with the specified amount. - /// - /// The point to shift. - /// The number of bits to shift to the left. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point ShiftLeft(Point point, int bitCount) => new(unchecked(point.X << bitCount), unchecked(point.Y << bitCount)); - /// public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y); diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs index b6ab67d5ee..c5147d9680 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs @@ -670,7 +670,7 @@ public class Av1IntraSuperblockEncoderTests const int Width = 8; const int Height = 8; Point blockOrigin = new(isIntraBlockCopy ? 320 : 0, 0); - Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Point modeInfoPosition = new(blockOrigin.X >> Av1Constants.ModeInfoSizeLog2, blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2); int frameWidth = blockOrigin.X + Width; int superblockIndex = blockOrigin.X / 64; ObuColorConfig colorConfig = new() diff --git a/tests/ImageSharp.Tests/Primitives/PointTests.cs b/tests/ImageSharp.Tests/Primitives/PointTests.cs index b740cbb28d..d2192f53bb 100644 --- a/tests/ImageSharp.Tests/Primitives/PointTests.cs +++ b/tests/ImageSharp.Tests/Primitives/PointTests.cs @@ -4,7 +4,7 @@ using System.Globalization; using System.Numerics; -namespace SixLabors.ImageSharp.Tests.Primitives; +namespace SixLabors.ImageSharp.Tests; public class PointTests { @@ -112,27 +112,6 @@ public class PointTests Assert.Equal(subExpected, Point.Subtract(p, s)); } - [Theory] - [InlineData(int.MaxValue, int.MaxValue, 5)] - [InlineData(int.MinValue, int.MinValue, 4)] - [InlineData(int.MaxValue, int.MaxValue, 2)] - [InlineData(0, 0, 3)] - public void ShiftTest(int x, int y, int s) - { - Point rightExpected, leftExpected, p = new Point(x, y); - - unchecked - { - rightExpected = new Point(x >> s, y >> s); - leftExpected = new Point(x << s, y << s); - } - - Assert.Equal(rightExpected, p >> s); - Assert.Equal(leftExpected, p << s); - Assert.Equal(rightExpected, Point.ShiftRight(p, s)); - Assert.Equal(leftExpected, Point.ShiftLeft(p, s)); - } - [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MinValue)]