diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs
index 3c238f2acf..f259b24929 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,202 +51,202 @@ 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,
///
/// Color profile.
@@ -256,11 +256,11 @@ public enum Heif4CharCode : uint
///
/// 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 6c9bd514ca..c537e4dbb6 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.
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Heif;
///
/// Supported 4 character codes for use in HEIF images.
///
-[GeneratedCode("T4", "")]
+[GeneratedCode("TextTemplateFileGenerator", "")]
public enum Heif4CharCode : uint
{
<#
@@ -75,12 +75,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 #>,
<#
}
@@ -88,6 +89,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 b39f24517d..c76f7bac3f 100644
--- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
@@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.Text;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@@ -35,8 +36,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private readonly List itemLinks;
- private readonly byte[] buffer;
-
///
/// Initializes a new instance of the class.
///
@@ -48,7 +47,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
this.metadata = new ImageMetadata();
this.items = new List();
this.itemLinks = new List();
- this.buffer = new byte[80];
}
///
@@ -73,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:
@@ -117,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,23 +139,24 @@ 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);
- bool correctBrand = majorBrand is (uint)Heif4CharCode.heic or (uint)Heif4CharCode.heix or (uint)Heif4CharCode.avif;
+ 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.
- 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;
}
@@ -168,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;
@@ -177,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;
}
@@ -208,33 +218,33 @@ 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:
+ 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;
@@ -246,12 +256,13 @@ 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..]);
- if (handlerType != Heif4CharCode.pict)
+ Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[bytesRead..]);
+ if (handlerType != Heif4CharCode.Pict)
{
throw new ImageFormatException("Not a picture file.");
}
@@ -259,16 +270,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..]);
}
}
@@ -325,7 +337,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;
@@ -337,7 +349,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;
@@ -354,21 +366,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);
}
@@ -379,10 +392,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)
@@ -393,13 +407,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
while (stream.Position < endBoxPosition)
{
long containerLength = this.ReadBoxHeader(stream, out Heif4CharCode containerType);
- EnsureBoxBoundary(containerLength, boxLength);
- if (containerType == Heif4CharCode.ipco)
+ EnsureBoxInsideParent(containerLength, boxLength);
+ 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);
@@ -417,52 +431,53 @@ 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:
+ 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)));
+ 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..]);
- properties.Add(new KeyValuePair(Heif4CharCode.pasp, new Size(horizontalSpacing, verticalSpacing)));
+ case Heif4CharCode.Pasp:
+ 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:
+ 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 }));
+ properties.Add(new KeyValuePair(Heif4CharCode.Pixi, new int[] { channelCount, bitsPerPixel }));
break;
- case Heif4CharCode.colr:
- Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer);
- if (profileType is Heif4CharCode.rICC or Heif4CharCode.prof)
+ case Heif4CharCode.Colr:
+ 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);
}
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;
@@ -474,36 +489,37 @@ 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];
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];
@@ -514,12 +530,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;
@@ -529,32 +546,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);
}
@@ -607,15 +624,17 @@ 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.
- HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.thmb);
+ 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)
{
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");
}
@@ -623,9 +642,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;
@@ -636,25 +655,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)
{
- stream.Read(this.buffer, 0, (int)length);
- return this.buffer;
- }
- else
- {
- Span temp = new byte[length];
- 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)
{
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);
}
}
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.