Browse Source

Decode HEIF sequences without a separate primary image item

pull/2633/head
James Jackson-South 4 weeks ago
parent
commit
6716c8e9ff
  1. 34
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
  2. 10
      tests/ImageSharp.Tests/Formats/Heif/HeifSequenceParserTests.cs

34
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<TPixel>
{
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;
}
/// <summary>
/// Gets the primary image item required alongside an AVIF image sequence.
/// </summary>
private HeifItem FindSequencePrimaryItem()
=> this.FindItemById(this.primaryItem)
?? throw new InvalidImageContentException("The HEIF image sequence contains no primary image item.");
/// <summary>
/// Determines whether the primary image item reuses the first presented sequence sample.
/// </summary>

10
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<Rgba32> expected = Image.Load<Rgba32>(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]);
}
}
}
/// <summary>

Loading…
Cancel
Save