diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 468b83ecc2..0c054b84d5 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -307,16 +307,24 @@ internal sealed class HeifDecoderCore : ImageDecoderCore private ImageInfo IdentifyImageSequence(HeifSequence sequence) { HeifSequenceTrack colorTrack = sequence.ColorTrack; - HeifItem primaryItem = this.FindSequencePrimaryItem(); - bool animateRootFrame = IsPrimaryItemFirstSequenceSample(primaryItem, colorTrack); + HeifItem? primaryItem = this.FindItemById(this.primaryItem); + bool animateRootFrame = primaryItem is null || IsPrimaryItemFirstSequenceSample(primaryItem, colorTrack); Size sequenceExtent = GetSequencePresentationExtent(colorTrack); - this.Dimensions = animateRootFrame ? sequenceExtent : GetPresentationExtent(primaryItem); - if (!animateRootFrame && this.Dimensions != sequenceExtent) + this.Dimensions = sequenceExtent; + if (primaryItem is not null) { - throw new InvalidImageContentException("The primary image and image sequence have different presentation dimensions."); + if (!animateRootFrame) + { + this.Dimensions = GetPresentationExtent(primaryItem); + if (this.Dimensions != sequenceExtent) + { + throw new InvalidImageContentException("The primary image and image sequence have different presentation dimensions."); + } + } + + this.UpdateMetadata(this.metadata, primaryItem); } - this.UpdateMetadata(this.metadata, primaryItem); this.UpdateSequenceMetadata(this.metadata, sequence, animateRootFrame); ImageFrameMetadata[] frameMetadata = CreateSequenceFrameMetadata(colorTrack, animateRootFrame); return new ImageInfo(this.Dimensions, this.metadata, frameMetadata); @@ -337,8 +345,11 @@ internal sealed class HeifDecoderCore : ImageDecoderCore where TPixel : unmanaged, IPixel { HeifSequenceTrack colorTrack = sequence.ColorTrack; - HeifItem primaryItem = this.FindSequencePrimaryItem(); - bool animateRootFrame = IsPrimaryItemFirstSequenceSample(primaryItem, colorTrack); + HeifItem? primaryItem = this.FindItemById(this.primaryItem); + + // Track samples carry their own codec configuration and presentation metadata. When no still item is + // available, the first visible sample supplies the root; an independent still item keeps its own root. + bool animateRootFrame = primaryItem is null || IsPrimaryItemFirstSequenceSample(primaryItem, colorTrack); this.UpdateSequenceMetadata(this.metadata, sequence, animateRootFrame); Size codedSize = new(colorTrack.CodedWidth, colorTrack.CodedHeight); Rectangle sourceRectangle = colorTrack.CleanAperture is not null @@ -827,13 +838,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore return result; } - /// - /// Gets the primary image item required alongside an AVIF image sequence. - /// - private HeifItem FindSequencePrimaryItem() - => this.FindItemById(this.primaryItem) - ?? throw new InvalidImageContentException("The HEIF image sequence contains no primary image item."); - /// /// Determines whether the primary image item reuses the first presented sequence sample. /// diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs index 65e75cbb50..1ea41f4e58 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs @@ -160,6 +160,7 @@ public class HeifSequenceParserTests Assert.Equal(HeifBitDepth.Bit8, metadata.BitDepth); Assert.Equal(3, metadata.RepeatCount); Assert.False(metadata.HasAlpha); + Assert.True(metadata.AnimateRootFrame); Assert.NotNull(info.Metadata.CicpProfile); Assert.NotNull(info.Metadata.ExifProfile); Assert.NotNull(info.Metadata.XmpProfile); @@ -313,6 +314,15 @@ public class HeifSequenceParserTests Assert.Equal(new Size(4, 4), image.Size); Assert.Single(image.Frames); + Assert.True(image.Metadata.GetHeifMetadata().AnimateRootFrame); + using Image expected = Image.Load(source); + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(expected[x, y], image[x, y]); + } + } } ///