diff --git a/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs b/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs index 23679740e6..0d3a31f0d3 100644 --- a/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs +++ b/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; using System.Buffers.Binary; using System.Globalization; using System.Text; @@ -63,6 +64,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals throw new ImageFormatException("Not an HEIC image."); } + Image? image = null; while (stream.Position < stream.Length) { long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType); @@ -73,7 +75,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals this.ParseMetadata(stream, boxLength); break; case Heic4CharCode.mdat: - this.ParseMediaData(stream, boxLength); + image = this.ParseMediaData(stream, boxLength); break; case Heic4CharCode.free: SkipBox(stream, boxLength); @@ -93,10 +95,10 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals throw new ImageFormatException("No primary item found"); } - var image = new Image(this.configuration, item.Extent.Width, item.Extent.Height, this.metadata); - - // TODO: Parse pixel data - Buffer2D pixels = image.GetRootFramePixelBuffer(); + if (image == null) + { + throw new NotImplementedException("No JPEG image decoded"); + } return image; } @@ -569,9 +571,30 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals return result; } - private void ParseMediaData(BufferedReadStream stream, long boxLength) + private Image ParseMediaData(BufferedReadStream stream, long boxLength) + where TPixel : unmanaged, IPixel { - SkipBox(stream, boxLength); + // FIXME: No HVC decoding yet, so parse only a JPEG thumbnail. + HeicItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heic4CharCode.thmb); + if (thumbLink == null) + { + throw new NotImplementedException("No thumbnail found"); + } + + HeicItem? thumbItem = this.FindItemById(thumbLink.SourceId); + if (thumbItem == null || thumbItem.Type != Heic4CharCode.jpeg) + { + throw new NotImplementedException("No HVC decoding implemented yet"); + } + + int thumbFileOffset = (int)thumbItem.DataLocations[0].Offset; + int thumbFileLength = (int)thumbItem.DataLocations[0].Length; + stream.Skip((int)(thumbFileOffset - stream.Position)); + using IMemoryOwner thumbMemory = this.configuration.MemoryAllocator.Allocate(thumbFileLength); + Span thumbSpan = thumbMemory.GetSpan(); + stream.Read(thumbSpan); + + return Image.Load(thumbSpan); } private static void SkipBox(BufferedReadStream stream, long boxLength) diff --git a/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs b/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs index e100069cb4..a48f272b5f 100644 --- a/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs @@ -21,7 +21,8 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector return format != null; } - private static bool IsSupportedFileFormat(ReadOnlySpan header) { + private static bool IsSupportedFileFormat(ReadOnlySpan header) + { bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heic4CharCode.ftyp; uint brand = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)); return hasFtyp && (brand == (uint)Heic4CharCode.heic || brand == (uint)Heic4CharCode.heix); diff --git a/src/ImageSharp/Formats/Heic/HeicItem.cs b/src/ImageSharp/Formats/Heic/HeicItem.cs index beccfada38..4127aebb01 100644 --- a/src/ImageSharp/Formats/Heic/HeicItem.cs +++ b/src/ImageSharp/Formats/Heic/HeicItem.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. - namespace SixLabors.ImageSharp.Formats.Heic; ///