From db44498536e7759e06d4ac907f0e114420c1e1ba Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Thu, 14 Mar 2024 20:58:58 +0100 Subject: [PATCH 1/5] Casing in Heif4CharCode --- src/ImageSharp/Formats/Heif/Heif4CharCode.cs | 98 +++++++++---------- src/ImageSharp/Formats/Heif/Heif4CharCode.tt | 13 ++- src/ImageSharp/Formats/Heif/HeifConstants.cs | 2 +- .../Formats/Heif/HeifDecoderCore.cs | 92 ++++++++--------- .../Formats/Heif/HeifEncoderCore.cs | 42 ++++---- .../Formats/Heif/HeifImageFormatDetector.cs | 4 +- 6 files changed, 129 insertions(+), 122 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs index d6d31ef1d3..d2324ee8b0 100644 --- a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs +++ b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs @@ -10,38 +10,38 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// /// Supported 4 character codes for use in HEIF images. /// -[GeneratedCode("T4", "")] +[GeneratedCode("TextTemplateFileGenerator", "")] public enum Heif4CharCode : uint { /// /// File Type. /// - ftyp = 0x66747970U, + Ftyp = 0x66747970U, /// /// Metadata. /// - meta = 0x6D657461U, + Meta = 0x6D657461U, /// /// Media Data. /// - mdat = 0x6D646174U, + Mdat = 0x6D646174U, /// /// Item Information Entry. /// - infe = 0x696E6665U, + Infe = 0x696E6665U, /// /// Item Data. /// - idat = 0x69646174U, + Idat = 0x69646174U, /// /// Item Location. /// - iloc = 0x696C6F63U, + Iloc = 0x696C6F63U, /// /// EXIF metadata. @@ -51,211 +51,211 @@ public enum Heif4CharCode : uint /// /// Data Reference. /// - dref = 0x64726566U, + Dref = 0x64726566U, /// /// Primary Item. /// - pitm = 0x7069746DU, + Pitm = 0x7069746DU, /// /// Item Spatial Extent. /// - ispe = 0x69737065U, + Ispe = 0x69737065U, /// /// Alternative text. /// - altt = 0x616C7474U, + Altt = 0x616C7474U, /// /// Colour information. /// - colr = 0x636F6C72U, + Colr = 0x636F6C72U, /// /// HVC configuration. /// - hvcC = 0x68766343U, + HvcC = 0x68766343U, /// /// AV1 configuration. /// - av1C = 0x61763143U, + Av1C = 0x61763143U, /// /// Image Mirror. /// - imir = 0x696D6972U, + Imir = 0x696D6972U, /// /// Image Rotation. /// - irot = 0x69726F74U, + Irot = 0x69726F74U, /// /// Image Scaling. /// - iscl = 0x6973636CU, + Iscl = 0x6973636CU, /// /// Pixel Aspect Ratio. /// - pasp = 0x70617370U, + Pasp = 0x70617370U, /// /// Pixel Information. /// - pixi = 0x70697869U, + Pixi = 0x70697869U, /// /// Reference Location. /// - rloc = 0x726C6F63U, + Rloc = 0x726C6F63U, /// /// User Description. /// - udes = 0x75646573U, + Udes = 0x75646573U, /// /// IPMP Control Box. /// - ipmc = 0x69706D63U, + Ipmc = 0x69706D63U, /// /// Item Property Container. /// - ipco = 0x6970636FU, + Ipco = 0x6970636FU, /// /// Item Property Association. /// - ipma = 0x69706D61U, + Ipma = 0x69706D61U, /// /// High Efficient Image Coding brand. /// - heic = 0x68656963U, + Heic = 0x68656963U, /// /// High Efficient Image Coding brand (legacy name). /// - heix = 0x68656978U, + Heix = 0x68656978U, /// /// High Efficient File brand. /// - mif1 = 0x6D696631U, + Mif1 = 0x6D696631U, /// /// AVIF brand. /// - avif = 0x61766966U, + Avif = 0x61766966U, /// /// High Efficiency Coding tile. /// - hvc1 = 0x68766331U, + Hvc1 = 0x68766331U, /// /// Legacy JPEG coded tile. /// - jpeg = 0x6A706567U, + Jpeg = 0x6A706567U, /// /// AOMedia Video Coding tile. /// - av01 = 0x61763031U, + Av01 = 0x61763031U, /// /// Data Information. /// - dinf = 0x64696E66U, + Dinf = 0x64696E66U, /// /// Group list. /// - grpl = 0x6772706CU, + Grpl = 0x6772706CU, /// /// Handler. /// - hdlr = 0x68646C72U, + Hdlr = 0x68646C72U, /// /// Item Information. /// - iinf = 0x69696E66U, + Iinf = 0x69696E66U, /// /// Item Property. /// - iprp = 0x69707270U, + Iprp = 0x69707270U, /// /// Item Protection. /// - ipro = 0x6970726FU, + Ipro = 0x6970726FU, /// /// Item Reference. /// - iref = 0x69726566U, + Iref = 0x69726566U, /// /// Grid. /// - grid = 0x67726964U, + Grid = 0x67726964U, /// /// Derived Image. /// - dimg = 0x64696D67U, + Dimg = 0x64696D67U, /// /// Thumbnail. /// - thmb = 0x74686D62U, + Thmb = 0x74686D62U, /// /// Content Description. /// - cdsc = 0x63647363U, + Cdsc = 0x63647363U, /// /// MIME type. /// - mime = 0x6D696D65U, + Mime = 0x6D696D65U, /// /// URI. /// - uri = 0x75726920U, + Uri = 0x75726920U, /// /// Picture handler type. /// - pict = 0x70696374U, + Pict = 0x70696374U, /// /// Unique Identifier. /// - uuid = 0x75756964U, + Uuid = 0x75756964U, /// /// Free space. /// - free = 0x66726565U, + Free = 0x66726565U, /// /// ICC Color Profile. /// - rICC = 0x72494343U, + RICC = 0x72494343U, /// /// ICC Color Profile. /// - prof = 0x70726F66U, + Prof = 0x70726F66U, } diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.tt b/src/ImageSharp/Formats/Heif/Heif4CharCode.tt index 9f8555f33b..a6af97004f 100644 --- a/src/ImageSharp/Formats/Heif/Heif4CharCode.tt +++ b/src/ImageSharp/Formats/Heif/Heif4CharCode.tt @@ -1,6 +1,6 @@ <#@ template language="C#" #> -<#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Text" #> // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// /// Supported 4 character codes for use in HEIF images. /// -[GeneratedCode("T4", "")] +[GeneratedCode("TextTemplateFileGenerator", "")] public enum Heif4CharCode : uint { <# @@ -74,12 +74,13 @@ public enum Heif4CharCode : uint { string shortName = codes[i]; string longName = codes[i + 1]; + string pascal = PascalCasing(shortName); string hex = Code2Hex(shortName); #> /// /// <#= longName #>. /// - <#= shortName #> = <#= hex #>, + <#= pascal #> = <#= hex #>, <# } @@ -87,6 +88,12 @@ public enum Heif4CharCode : uint } <#+ +private string PascalCasing(string code) +{ + char firstChar = char.ToUpper(code[0], System.Globalization.CultureInfo.InvariantCulture); + return string.Concat(firstChar, code.Substring(1)); +} + private string Code2Hex(string code) { byte[] b = Encoding.ASCII.GetBytes(code); diff --git a/src/ImageSharp/Formats/Heif/HeifConstants.cs b/src/ImageSharp/Formats/Heif/HeifConstants.cs index 5ef49053dd..f59fd56412 100644 --- a/src/ImageSharp/Formats/Heif/HeifConstants.cs +++ b/src/ImageSharp/Formats/Heif/HeifConstants.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// internal static class HeifConstants { - public const Heif4CharCode HeicBrand = Heif4CharCode.heic; + public const Heif4CharCode HeicBrand = Heif4CharCode.Heic; /// /// The list of mimetypes that equate to a HEIC. diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 8a8c8e29f6..b480c81b3f 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -71,13 +71,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals EnsureBoxBoundary(boxLength, stream); switch (boxType) { - case Heif4CharCode.meta: + case Heif4CharCode.Meta: this.ParseMetadata(stream, boxLength); break; - case Heif4CharCode.mdat: + case Heif4CharCode.Mdat: image = this.ParseMediaData(stream, boxLength); break; - case Heif4CharCode.free: + case Heif4CharCode.Free: SkipBox(stream, boxLength); break; case 0U: @@ -115,7 +115,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals EnsureBoxBoundary(boxLength, stream); switch (boxType) { - case Heif4CharCode.meta: + case Heif4CharCode.Meta: this.ParseMetadata(stream, boxLength); break; default: @@ -141,21 +141,21 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType); Span buffer = this.ReadIntoBuffer(stream, boxLength); uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer); - bool correctBrand = majorBrand is (uint)Heif4CharCode.heic or (uint)Heif4CharCode.heix or (uint)Heif4CharCode.avif; + bool correctBrand = majorBrand is (uint)Heif4CharCode.Heic or (uint)Heif4CharCode.Heix or (uint)Heif4CharCode.Avif; // TODO: Interpret minorVersion and compatible brands. - return boxType == Heif4CharCode.ftyp && correctBrand; + return boxType == Heif4CharCode.Ftyp && correctBrand; } private void UpdateMetadata(ImageMetadata metadata, HeifItem item) { HeifMetadata meta = metadata.GetHeifMetadata(); HeifCompressionMethod compressionMethod = HeifCompressionMethod.Hevc; - if (item.Type == Heif4CharCode.av01) + if (item.Type == Heif4CharCode.Av01) { compressionMethod = HeifCompressionMethod.Av1; } - else if (item.Type == Heif4CharCode.jpeg || this.itemLinks.Any(link => link.Type == Heif4CharCode.thmb)) + else if (item.Type == Heif4CharCode.Jpeg || this.itemLinks.Any(link => link.Type == Heif4CharCode.Thmb)) { compressionMethod = HeifCompressionMethod.LegacyJpeg; } @@ -209,30 +209,30 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals EnsureBoxBoundary(length, boxLength); switch (boxType) { - case Heif4CharCode.iprp: + case Heif4CharCode.Iprp: this.ParseItemProperties(stream, length); break; - case Heif4CharCode.iinf: + case Heif4CharCode.Iinf: this.ParseItemInfo(stream, length); break; - case Heif4CharCode.iref: + case Heif4CharCode.Iref: this.ParseItemReference(stream, length); break; - case Heif4CharCode.pitm: + case Heif4CharCode.Pitm: this.ParsePrimaryItem(stream, length); break; - case Heif4CharCode.hdlr: + case Heif4CharCode.Hdlr: this.ParseHandler(stream, length); break; - case Heif4CharCode.iloc: + case Heif4CharCode.Iloc: this.ParseItemLocation(stream, length); break; - case Heif4CharCode.dinf: - case Heif4CharCode.idat: - case Heif4CharCode.grpl: - case Heif4CharCode.ipro: - case Heif4CharCode.uuid: - case Heif4CharCode.ipmc: + case Heif4CharCode.Dinf: + case Heif4CharCode.Idat: + case Heif4CharCode.Grpl: + case Heif4CharCode.Ipro: + case Heif4CharCode.Uuid: + case Heif4CharCode.Ipmc: // Silently skip these boxes. SkipBox(stream, length); break; @@ -249,7 +249,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals // Only read the handler type, to check if this is not a movie file. int bytesRead = 8; Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); - if (handlerType != Heif4CharCode.pict) + if (handlerType != Heif4CharCode.Pict) { throw new ImageFormatException("Not a picture file."); } @@ -323,7 +323,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals item = new HeifItem(itemType, itemId); item.Name = ReadNullTerminatedString(buffer[bytesRead..]); bytesRead += item.Name.Length + 1; - if (item.Type == Heif4CharCode.mime) + if (item.Type == Heif4CharCode.Mime) { item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]); bytesRead += item.ContentType.Length + 1; @@ -335,7 +335,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals bytesRead += item.ContentEncoding.Length + 1; } } - else if (item.Type == Heif4CharCode.uri) + else if (item.Type == Heif4CharCode.Uri) { item.UriType = ReadNullTerminatedString(buffer[bytesRead..]); bytesRead += item.UriType.Length + 1; @@ -392,12 +392,12 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals { long containerLength = this.ReadBoxHeader(stream, out Heif4CharCode containerType); EnsureBoxBoundary(containerLength, boxLength); - if (containerType == Heif4CharCode.ipco) + if (containerType == Heif4CharCode.Ipco) { // Parse Item Property Container, which is just an array of property boxes. this.ParsePropertyContainer(stream, containerLength, properties); } - else if (containerType == Heif4CharCode.ipma) + else if (containerType == Heif4CharCode.Ipma) { // Parse Item Property Association this.ParsePropertyAssociation(stream, containerLength, properties); @@ -419,18 +419,18 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals Span buffer = this.ReadIntoBuffer(stream, itemLength); switch (itemType) { - case Heif4CharCode.ispe: + case Heif4CharCode.Ispe: // Skip over version (8 bits) and flags (24 bits). int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); int height = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]); - properties.Add(new KeyValuePair(Heif4CharCode.ispe, new Size(width, height))); + properties.Add(new KeyValuePair(Heif4CharCode.Ispe, new Size(width, height))); break; - case Heif4CharCode.pasp: + case Heif4CharCode.Pasp: int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer); int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); - properties.Add(new KeyValuePair(Heif4CharCode.pasp, new Size(horizontalSpacing, verticalSpacing))); + properties.Add(new KeyValuePair(Heif4CharCode.Pasp, new Size(horizontalSpacing, verticalSpacing))); break; - case Heif4CharCode.pixi: + case Heif4CharCode.Pixi: // Skip over version (8 bits) and flags (24 bits). int channelCount = buffer[4]; int offset = 5; @@ -440,12 +440,12 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals bitsPerPixel += buffer[offset + i]; } - properties.Add(new KeyValuePair(Heif4CharCode.pixi, new int[] { channelCount, bitsPerPixel })); + properties.Add(new KeyValuePair(Heif4CharCode.Pixi, new int[] { channelCount, bitsPerPixel })); break; - case Heif4CharCode.colr: + case Heif4CharCode.Colr: Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer); - if (profileType is Heif4CharCode.rICC or Heif4CharCode.prof) + if (profileType is Heif4CharCode.RICC or Heif4CharCode.Prof) { byte[] iccData = new byte[itemLength - 4]; buffer[4..].CopyTo(iccData); @@ -453,14 +453,14 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals } break; - case Heif4CharCode.altt: - case Heif4CharCode.imir: - case Heif4CharCode.irot: - case Heif4CharCode.iscl: - case Heif4CharCode.hvcC: - case Heif4CharCode.av1C: - case Heif4CharCode.rloc: - case Heif4CharCode.udes: + case Heif4CharCode.Altt: + case Heif4CharCode.Imir: + case Heif4CharCode.Irot: + case Heif4CharCode.Iscl: + case Heif4CharCode.HvcC: + case Heif4CharCode.Av1C: + case Heif4CharCode.Rloc: + case Heif4CharCode.Udes: // TODO: Implement properties.Add(new KeyValuePair(itemType, new object())); break; @@ -495,13 +495,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals KeyValuePair prop = properties[(int)propId]; switch (prop.Key) { - case Heif4CharCode.ispe: + case Heif4CharCode.Ispe: this.items[itemId].SetExtent((Size)prop.Value); break; - case Heif4CharCode.pasp: + case Heif4CharCode.Pasp: this.items[itemId].PixelAspectRatio = (Size)prop.Value; break; - case Heif4CharCode.pixi: + case Heif4CharCode.Pixi: int[] values = (int[])prop.Value; this.items[itemId].ChannelCount = values[0]; this.items[itemId].BitsPerPixel = values[1]; @@ -606,14 +606,14 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals where TPixel : unmanaged, IPixel { // FIXME: No HVC decoding yet, so parse only a JPEG thumbnail. - HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.thmb); + HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.Thmb); if (thumbLink == null) { throw new NotImplementedException("No thumbnail found"); } HeifItem? thumbItem = this.FindItemById(thumbLink.SourceId); - if (thumbItem == null || thumbItem.Type != Heif4CharCode.jpeg) + if (thumbItem == null || thumbItem.Type != Heif4CharCode.Jpeg) { throw new NotImplementedException("No HVC decoding implemented yet"); } diff --git a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs index 8f83d9c6d8..4842cfcf8d 100644 --- a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs @@ -16,7 +16,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals /// /// The global configuration. /// - private Configuration configuration; + private readonly Configuration configuration; /// /// The encoder with options. @@ -65,7 +65,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static void GenerateItems(Image image, byte[] pixels, List items, List links) where TPixel : unmanaged, IPixel { - HeifItem primaryItem = new(Heif4CharCode.jpeg, 1u); + HeifItem primaryItem = new(Heif4CharCode.Jpeg, 1u); primaryItem.DataLocations.Add(new HeifLocation(0L, pixels.LongLength)); primaryItem.BitsPerPixel = 24; primaryItem.ChannelCount = 3; @@ -73,7 +73,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals items.Add(primaryItem); // Create a fake thumbnail, to make our own Decoder happy. - HeifItemLink thumbnail = new(Heif4CharCode.thmb, 1u); + HeifItemLink thumbnail = new(Heif4CharCode.Thmb, 1u); thumbnail.DestinationIds.Add(1u); links.Add(thumbnail); } @@ -108,14 +108,14 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private void WriteFileTypeBox(Stream stream) { Span buffer = stackalloc byte[24]; - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.ftyp); - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic); + 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); + BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Mif1); bytesWritten += 4; - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic); + BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Heic); bytesWritten += 4; BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten); @@ -126,7 +126,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals { using AutoExpandingMemory memory = new(this.configuration, 0x1000); Span buffer = memory.GetSpan(12); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.meta, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Meta, 0, 0); bytesWritten += WriteHandlerBox(memory, bytesWritten); bytesWritten += WritePrimaryItemBox(memory, bytesWritten); bytesWritten += WriteItemInfoBox(memory, bytesWritten, items); @@ -143,10 +143,10 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteHandlerBox(AutoExpandingMemory memory, int memoryOffset) { Span buffer = memory.GetSpan(memoryOffset, 33); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.hdlr, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Hdlr, 0, 0); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0); bytesWritten += 4; - BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.pict); + BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.Pict); bytesWritten += 4; for (int i = 0; i < 13; i++) { @@ -160,7 +160,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WritePrimaryItemBox(AutoExpandingMemory memory, int memoryOffset) { Span buffer = memory.GetSpan(memoryOffset, 14); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.pitm, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Pitm, 0, 0); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1); bytesWritten += 2; @@ -171,13 +171,13 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteItemInfoBox(AutoExpandingMemory memory, int memoryOffset, List items) { Span buffer = memory.GetSpan(memoryOffset, 14 + (items.Count * 21)); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iinf, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Iinf, 0, 0); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)items.Count); bytesWritten += 2; foreach (HeifItem item in items) { int itemLengthOffset = bytesWritten; - bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.infe, 2, 0); + bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.Infe, 2, 0); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id); bytesWritten += 2; BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 0); @@ -196,7 +196,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteItemReferenceBox(AutoExpandingMemory memory, int memoryOffset, List items, List links) { Span buffer = memory.GetSpan(memoryOffset, 12 + (links.Count * (12 + (items.Count * 2)))); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iref, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Iref, 0, 0); foreach (HeifItemLink link in links) { int itemLengthOffset = bytesWritten; @@ -222,11 +222,11 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals { const ushort numPropPerItem = 1; Span buffer = memory.GetSpan(memoryOffset, 20); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iprp); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Iprp); // Write 'ipco' box int ipcoLengthOffset = bytesWritten; - bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.ipco); + bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.Ipco); foreach (HeifItem item in items) { bytesWritten += WriteSpatialExtentPropertyBox(memory, memoryOffset + bytesWritten, item); @@ -237,7 +237,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals // Write 'ipma' box int ipmaLengthOffset = bytesWritten; - bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.ipma, 0, 0); + bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.Ipma, 0, 0); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)(items.Count * numPropPerItem)); bytesWritten += 4; ushort propIndex = 0; @@ -261,7 +261,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteSpatialExtentPropertyBox(AutoExpandingMemory memory, int memoryOffset, HeifItem item) { Span buffer = memory.GetSpan(memoryOffset, 20); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.ispe, 0, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Ispe, 0, 0); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Width); bytesWritten += 4; BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Height); @@ -274,7 +274,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteItemDataBox(AutoExpandingMemory memory, int memoryOffset) { Span buffer = memory.GetSpan(memoryOffset, 10); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.idat); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Idat); BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten); return bytesWritten; @@ -283,7 +283,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private static int WriteItemLocationBox(AutoExpandingMemory memory, int memoryOffset, List items) { Span buffer = memory.GetSpan(memoryOffset, 30 + (items.Count * 8)); - int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iloc, 1, 0); + int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.Iloc, 1, 0); buffer[bytesWritten++] = 0x44; buffer[bytesWritten++] = 0; BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1); @@ -313,7 +313,7 @@ internal sealed class HeifEncoderCore : IImageEncoderInternals private void WriteMediaDataBox(Span data, Stream stream) { Span buf = stackalloc byte[12]; - int bytesWritten = WriteBoxHeader(buf, Heif4CharCode.mdat); + int bytesWritten = WriteBoxHeader(buf, Heif4CharCode.Mdat); BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)(data.Length + bytesWritten)); stream.Write(buf[..bytesWritten]); diff --git a/src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs b/src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs index 10dfa86158..b53aacc874 100644 --- a/src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs @@ -23,8 +23,8 @@ public sealed class HeifImageFormatDetector : IImageFormatDetector private static bool IsSupportedFileFormat(ReadOnlySpan header) { - bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heif4CharCode.ftyp; + bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heif4CharCode.Ftyp; uint brand = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)); - return hasFtyp && (brand == (uint)Heif4CharCode.heic || brand == (uint)Heif4CharCode.heix || brand == (uint)Heif4CharCode.avif); + return hasFtyp && (brand == (uint)Heif4CharCode.Heic || brand == (uint)Heif4CharCode.Heix || brand == (uint)Heif4CharCode.Avif); } } From 73bdbd800df6e6c9276ea2484457d34bb6d6d992 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Thu, 14 Mar 2024 21:01:56 +0100 Subject: [PATCH 2/5] Make some classes internal --- src/ImageSharp/Formats/Heif/HeifItem.cs | 2 +- src/ImageSharp/Formats/Heif/HeifItemLink.cs | 2 +- src/ImageSharp/Formats/Heif/HeifLocation.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/HeifItem.cs b/src/ImageSharp/Formats/Heif/HeifItem.cs index 60f26df3ff..25e6bb2988 100644 --- a/src/ImageSharp/Formats/Heif/HeifItem.cs +++ b/src/ImageSharp/Formats/Heif/HeifItem.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// /// Provides definition for a HEIF Item. /// -public class HeifItem(Heif4CharCode type, uint id) +internal class HeifItem(Heif4CharCode type, uint id) { /// /// Gets the ID of this Item. diff --git a/src/ImageSharp/Formats/Heif/HeifItemLink.cs b/src/ImageSharp/Formats/Heif/HeifItemLink.cs index 95e84a237f..7ed2f45402 100644 --- a/src/ImageSharp/Formats/Heif/HeifItemLink.cs +++ b/src/ImageSharp/Formats/Heif/HeifItemLink.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// /// Link between instances within the same HEIF file. /// -public class HeifItemLink(Heif4CharCode type, uint sourceId) +internal class HeifItemLink(Heif4CharCode type, uint sourceId) { /// /// Gets the type of link. diff --git a/src/ImageSharp/Formats/Heif/HeifLocation.cs b/src/ImageSharp/Formats/Heif/HeifLocation.cs index 9348ba981e..d739cb633c 100644 --- a/src/ImageSharp/Formats/Heif/HeifLocation.cs +++ b/src/ImageSharp/Formats/Heif/HeifLocation.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Heif; /// /// Location within the file of an . /// -public class HeifLocation(long offset, long length) +internal class HeifLocation(long offset, long length) { /// /// Gets the file offset of this location. From de392d8c91b4b30277004035683f37795496a92a Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Fri, 15 Mar 2024 17:54:18 +0100 Subject: [PATCH 3/5] Read header from stack allocated buffer --- .../Formats/Heif/HeifDecoderCore.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index b480c81b3f..5fdf6fe231 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Text; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -166,7 +167,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private long ReadBoxHeader(BufferedReadStream stream, out Heif4CharCode boxType) { // Read 4 bytes of length of box - Span buf = this.ReadIntoBuffer(stream, 8); + Span buf = stackalloc byte[8]; + int bytesRead = stream.Read(buf); + if (bytesRead != 8) + { + throw new InvalidImageContentException("Not enough data to read the Box header"); + } + long boxSize = BinaryPrimitives.ReadUInt32BigEndian(buf); long headerSize = 8; @@ -175,7 +182,12 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals if (boxSize == 1) { - buf = this.ReadIntoBuffer(stream, 8); + bytesRead = stream.Read(buf); + if (bytesRead != 8) + { + throw new InvalidImageContentException("Not enough data to read the Large Box header"); + } + boxSize = (long)BinaryPrimitives.ReadUInt64BigEndian(buf); headerSize += 8; } @@ -638,13 +650,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals { if (length <= this.buffer.Length) { - stream.Read(this.buffer, 0, (int)length); - return this.buffer; + int bytesRead = stream.Read(this.buffer, 0, (int)length); + return this.buffer.AsSpan(0, bytesRead); } else { Span temp = new byte[length]; - stream.Read(temp); + int bytesRead = stream.Read(temp); return temp; } } From d146069bc52cf8e0688d0d41ff82ca2ab7103da0 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Sun, 17 Mar 2024 13:44:18 +0100 Subject: [PATCH 4/5] Check Stream.Read return value --- .../Formats/Heif/HeifDecoderCore.cs | 137 +++++++++--------- 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 5fdf6fe231..79b65a296d 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -34,8 +34,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private readonly List itemLinks; - private readonly byte[] buffer; - /// /// Initializes a new instance of the class. /// @@ -47,7 +45,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals this.metadata = new ImageMetadata(); this.items = new List(); this.itemLinks = new List(); - this.buffer = new byte[80]; } /// @@ -140,8 +137,9 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private bool CheckFileTypeBox(BufferedReadStream stream) { long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType); - Span buffer = this.ReadIntoBuffer(stream, boxLength); - uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); + uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer); bool correctBrand = majorBrand is (uint)Heif4CharCode.Heic or (uint)Heif4CharCode.Heix or (uint)Heif4CharCode.Avif; // TODO: Interpret minorVersion and compatible brands. @@ -218,7 +216,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals while (stream.Position < endPosition) { long length = this.ReadBoxHeader(stream, out Heif4CharCode boxType); - EnsureBoxBoundary(length, boxLength); + EnsureBoxInsideParent(length, boxLength); switch (boxType) { case Heif4CharCode.Iprp: @@ -256,11 +254,12 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParseHandler(BufferedReadStream stream, long boxLength) { - Span buffer = this.ReadIntoBuffer(stream, boxLength); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); // Only read the handler type, to check if this is not a movie file. int bytesRead = 8; - Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); + Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[bytesRead..]); if (handlerType != Heif4CharCode.Pict) { throw new ImageFormatException("Not a picture file."); @@ -269,16 +268,17 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParseItemInfo(BufferedReadStream stream, long boxLength) { - Span buffer = this.ReadIntoBuffer(stream, boxLength); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); uint entryCount; int bytesRead = 0; - byte version = buffer[bytesRead]; + byte version = boxBuffer[bytesRead]; bytesRead += 4; - entryCount = ReadUInt16Or32(buffer, version != 0, ref bytesRead); + entryCount = ReadUInt16Or32(boxBuffer, version != 0, ref bytesRead); for (uint i = 0; i < entryCount; i++) { - bytesRead += this.ParseItemInfoEntry(buffer[bytesRead..]); + bytesRead += this.ParseItemInfoEntry(boxBuffer[bytesRead..]); } } @@ -364,21 +364,22 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParseItemReference(BufferedReadStream stream, long boxLength) { - Span buffer = this.ReadIntoBuffer(stream, boxLength); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); int bytesRead = 0; - bool largeIds = buffer[bytesRead] != 0; + bool largeIds = boxBuffer[bytesRead] != 0; bytesRead += 4; while (bytesRead < boxLength) { - bytesRead += ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heif4CharCode linkType); - uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead); + bytesRead += ParseBoxHeader(boxBuffer[bytesRead..], out long subBoxLength, out Heif4CharCode linkType); + uint sourceId = ReadUInt16Or32(boxBuffer, largeIds, ref bytesRead); HeifItemLink link = new(linkType, sourceId); - int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]); + int count = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]); bytesRead += 2; for (uint i = 0; i < count; i++) { - uint destId = ReadUInt16Or32(buffer, largeIds, ref bytesRead); + uint destId = ReadUInt16Or32(boxBuffer, largeIds, ref bytesRead); link.DestinationIds.Add(destId); } @@ -389,10 +390,11 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParsePrimaryItem(BufferedReadStream stream, long boxLength) { // BoxLength should be 6 or 8. - Span buffer = this.ReadIntoBuffer(stream, boxLength); - byte version = buffer[0]; + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); + byte version = boxBuffer[0]; int bytesRead = 4; - this.primaryItem = ReadUInt16Or32(buffer, version != 0, ref bytesRead); + this.primaryItem = ReadUInt16Or32(boxBuffer, version != 0, ref bytesRead); } private void ParseItemProperties(BufferedReadStream stream, long boxLength) @@ -403,7 +405,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals while (stream.Position < endBoxPosition) { long containerLength = this.ReadBoxHeader(stream, out Heif4CharCode containerType); - EnsureBoxBoundary(containerLength, boxLength); + EnsureBoxInsideParent(containerLength, boxLength); if (containerType == Heif4CharCode.Ipco) { // Parse Item Property Container, which is just an array of property boxes. @@ -427,40 +429,41 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals while (stream.Position < endPosition) { int itemLength = (int)this.ReadBoxHeader(stream, out Heif4CharCode itemType); - EnsureBoxBoundary(itemLength, boxLength); - Span buffer = this.ReadIntoBuffer(stream, itemLength); + EnsureBoxInsideParent(itemLength, boxLength); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, itemLength); + Span boxBuffer = boxMemory.GetSpan(); switch (itemType) { case Heif4CharCode.Ispe: // Skip over version (8 bits) and flags (24 bits). - int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); - int height = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]); + int width = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]); + int height = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[8..]); properties.Add(new KeyValuePair(Heif4CharCode.Ispe, new Size(width, height))); break; case Heif4CharCode.Pasp: - int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer); - int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); + int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer); + int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]); properties.Add(new KeyValuePair(Heif4CharCode.Pasp, new Size(horizontalSpacing, verticalSpacing))); break; case Heif4CharCode.Pixi: // Skip over version (8 bits) and flags (24 bits). - int channelCount = buffer[4]; + int channelCount = boxBuffer[4]; int offset = 5; int bitsPerPixel = 0; for (int i = 0; i < channelCount; i++) { - bitsPerPixel += buffer[offset + i]; + bitsPerPixel += boxBuffer[offset + i]; } properties.Add(new KeyValuePair(Heif4CharCode.Pixi, new int[] { channelCount, bitsPerPixel })); break; case Heif4CharCode.Colr: - Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer); + Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer); if (profileType is Heif4CharCode.RICC or Heif4CharCode.Prof) { byte[] iccData = new byte[itemLength - 4]; - buffer[4..].CopyTo(iccData); + boxBuffer[4..].CopyTo(iccData); this.metadata.IccProfile = new IccProfile(iccData); } @@ -484,24 +487,25 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParsePropertyAssociation(BufferedReadStream stream, long boxLength, List> properties) { - Span buffer = this.ReadIntoBuffer(stream, boxLength); - byte version = buffer[0]; - byte flags = buffer[3]; + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); + byte version = boxBuffer[0]; + byte flags = boxBuffer[3]; int bytesRead = 4; - int itemId = (int)ReadUInt16Or32(buffer, version >= 1, ref bytesRead); + int itemId = (int)ReadUInt16Or32(boxBuffer, version >= 1, ref bytesRead); - int associationCount = buffer[bytesRead++]; + int associationCount = boxBuffer[bytesRead++]; for (int i = 0; i < associationCount; i++) { uint propId; if (flags == 1) { - propId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]) & 0x4FFFU; + propId = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]) & 0x4FFFU; bytesRead += 2; } else { - propId = buffer[bytesRead++] & 0x4FU; + propId = boxBuffer[bytesRead++] & 0x4FU; } KeyValuePair prop = properties[(int)propId]; @@ -524,12 +528,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private void ParseItemLocation(BufferedReadStream stream, long boxLength) { - Span buffer = this.ReadIntoBuffer(stream, boxLength); + using IMemoryOwner boxMemory = this.ReadIntoBuffer(stream, boxLength); + Span boxBuffer = boxMemory.GetSpan(); int bytesRead = 0; - byte version = buffer[bytesRead]; + byte version = boxBuffer[bytesRead]; bytesRead += 4; - byte b1 = buffer[bytesRead++]; - byte b2 = buffer[bytesRead++]; + byte b1 = boxBuffer[bytesRead++]; + byte b2 = boxBuffer[bytesRead++]; int offsetSize = (b1 >> 4) & 0x0f; int lengthSize = b1 & 0x0f; int baseOffsetSize = (b2 >> 4) & 0x0f; @@ -539,32 +544,32 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals indexSize = b2 & 0x0f; } - uint itemCount = ReadUInt16Or32(buffer, version == 2, ref bytesRead); + uint itemCount = ReadUInt16Or32(boxBuffer, version == 2, ref bytesRead); for (uint i = 0; i < itemCount; i++) { - uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead); + uint itemId = ReadUInt16Or32(boxBuffer, version == 2, ref bytesRead); HeifItem? item = this.FindItemById(itemId); if (version is 1 or 2) { bytesRead++; - byte b3 = buffer[bytesRead++]; + byte b3 = boxBuffer[bytesRead++]; int constructionMethod = b3 & 0x0f; } - uint dataReferenceIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]); + uint dataReferenceIndex = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]); bytesRead += 2; - ulong baseOffset = ReadUIntVariable(buffer, baseOffsetSize, ref bytesRead); - uint extentCount = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]); + ulong baseOffset = ReadUIntVariable(boxBuffer, baseOffsetSize, ref bytesRead); + uint extentCount = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]); bytesRead += 2; for (uint j = 0; j < extentCount; j++) { if (version is 1 or 2 && indexSize > 0) { - _ = ReadUIntVariable(buffer, indexSize, ref bytesRead); + _ = ReadUIntVariable(boxBuffer, indexSize, ref bytesRead); } - ulong extentOffset = ReadUIntVariable(buffer, offsetSize, ref bytesRead); - ulong extentLength = ReadUIntVariable(buffer, lengthSize, ref bytesRead); + ulong extentOffset = ReadUIntVariable(boxBuffer, offsetSize, ref bytesRead); + ulong extentLength = ReadUIntVariable(boxBuffer, lengthSize, ref bytesRead); HeifLocation loc = new HeifLocation((long)extentOffset, (long)extentLength); item?.DataLocations.Add(loc); } @@ -617,7 +622,8 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private Image ParseMediaData(BufferedReadStream stream, long boxLength) where TPixel : unmanaged, IPixel { - // FIXME: No HVC decoding yet, so parse only a JPEG thumbnail. + EnsureBoxBoundary(boxLength, stream); + // FIXME: No specific decoding yet, so parse only a JPEG thumbnail. HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.Thmb); if (thumbLink == null) { @@ -633,9 +639,9 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals 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); + EnsureBoxBoundary(thumbFileLength, stream); + using IMemoryOwner thumbMemory = this.ReadIntoBuffer(stream, thumbFileLength); Span thumbSpan = thumbMemory.GetSpan(); - stream.Read(thumbSpan); HeifMetadata meta = this.metadata.GetHeifMetadata(); meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg; @@ -646,25 +652,22 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals private static void SkipBox(BufferedReadStream stream, long boxLength) => stream.Skip((int)boxLength); - private Span ReadIntoBuffer(BufferedReadStream stream, long length) + private IMemoryOwner ReadIntoBuffer(BufferedReadStream stream, long length) { - if (length <= this.buffer.Length) + IMemoryOwner buffer = this.configuration.MemoryAllocator.Allocate((int)length); + int bytesRead = stream.Read(buffer.GetSpan()); + if (bytesRead != length) { - int bytesRead = stream.Read(this.buffer, 0, (int)length); - return this.buffer.AsSpan(0, bytesRead); - } - else - { - Span temp = new byte[length]; - int bytesRead = stream.Read(temp); - return temp; + throw new InvalidImageContentException("Stream length not sufficient for box content"); } + + return buffer; } private static void EnsureBoxBoundary(long boxLength, Stream stream) - => EnsureBoxBoundary(boxLength, stream.Length - stream.Position); + => EnsureBoxInsideParent(boxLength, stream.Length - stream.Position); - private static void EnsureBoxBoundary(long boxLength, long parentLength) + private static void EnsureBoxInsideParent(long boxLength, long parentLength) { if (boxLength > parentLength) { From c847f5d72d0ebc93dde3bc9caef166f8323fc6e2 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Sun, 17 Mar 2024 13:48:02 +0100 Subject: [PATCH 5/5] Fix build --- src/ImageSharp/Formats/Heif/HeifDecoderCore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs index 79b65a296d..5201793606 100644 --- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs +++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs @@ -623,6 +623,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals where TPixel : unmanaged, IPixel { EnsureBoxBoundary(boxLength, stream); + // FIXME: No specific decoding yet, so parse only a JPEG thumbnail. HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.Thmb); if (thumbLink == null)