diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 5d1f66a22e..21ecd2cb2b 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
-using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Heic;
+using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi;
diff --git a/src/ImageSharp/Formats/Heic/FourCharacterCode.cs b/src/ImageSharp/Formats/Heic/FourCharacterCode.cs
index ad4cad7443..fc9ac748dc 100644
--- a/src/ImageSharp/Formats/Heic/FourCharacterCode.cs
+++ b/src/ImageSharp/Formats/Heic/FourCharacterCode.cs
@@ -1,11 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Buffers.Binary;
+using System.CodeDom.Compiler;
+using System.Text;
+
namespace SixLabors.ImageSharp.Formats.Heic;
///
/// Provides constants for 4 Character codes used in HEIC images.
///
+[GeneratedCode("T4", null)]
public static class FourCharacterCode
{
// TODO: Create T4 template for this file
@@ -13,197 +18,202 @@ public static class FourCharacterCode
///
/// File Type
///
- public const uint ftyp = 0x66747970U,
+ public const uint ftyp = 0x66747970U;
///
/// Metadata container
///
- public const uint meta = 0x6D657461U,
+ public const uint meta = 0x6D657461U;
///
/// Media Data
///
- public const uint mdat = 0x6D646174U,
+ public const uint mdat = 0x6D646174U;
///
/// Item Information Entry
///
- public const uint infe = 0x696E6665U,
+ public const uint infe = 0x696E6665U;
///
/// Item Data
///
- public const uint idat = 0x69646174U,
+ public const uint idat = 0x69646174U;
///
/// Item Location
///
- public const uint iloc = 0x696C6F63U,
+ public const uint iloc = 0x696C6F63U;
///
/// EXIF metadata
///
- public const uint Exif = 0x45786966U,
+ public const uint Exif = 0x45786966U;
///
/// Data Reference
///
- public const uint dref = 0x64726566U,
+ public const uint dref = 0x64726566U;
///
/// Primary Item
///
- public const uint pitm = 0x7069746DU,
+ public const uint pitm = 0x7069746DU;
///
/// Item Spatial Extent
///
- public const uint ispe = 0x69737064U,
+ public const uint ispe = 0x69737064U;
///
/// Alternative text
///
- public const uint altt = 0, // 'altt'
+ public const uint altt = 0; // 'altt'
///
/// Colour information
///
- public const uint colr = 0, // 'colr'
+ public const uint colr = 0; // 'colr'
///
/// HVC configuration
///
- public const uint hvcC = 0, // 'hvcC'
+ public const uint hvcC = 0; // 'hvcC'
///
/// Image Mirror
///
- public const uint imir = 0, // 'imir'
+ public const uint imir = 0; // 'imir'
///
/// Image Rotation
///
- public const uint irot = 0, // 'irot'
+ public const uint irot = 0; // 'irot'
///
/// Image Scaling
///
- public const uint iscl = 0, // 'iscl'
+ public const uint iscl = 0; // 'iscl'
///
/// Pixel Aspect Ration
///
- public const uint pasp = 0, // 'pasp'
+ public const uint pasp = 0; // 'pasp'
///
/// Pixel Information
///
- public const uint pixi = 0x70697869U,
+ public const uint pixi = 0x70697869U;
///
/// Reference Location
///
- public const uint rloc = 0, // 'rloc
+ public const uint rloc = 0; // 'rloc
///
/// User Description
///
- public const uint udes = 0, // 'udes'
+ public const uint udes = 0; // 'udes'
///
/// Item Property Container
///
- public const uint ipco = 0,
+ public const uint ipco = 0;
///
/// Item Property Association
///
- public const uint ipma = 0,
+ public const uint ipma = 0;
///
/// High Efficient Image Coding
///
- public const uint heic = 0,
+ public const uint heic = 0;
///
/// High Efficiency Coding tile
///
- public const uint hvc1 = 0,
+ public const uint hvc1 = 0;
///
/// Data Information
///
- public const uint dinf = 0,
+ public const uint dinf = 0;
///
/// Group list
///
- public const uint grpl = 0,
+ public const uint grpl = 0;
///
/// Handler
///
- public const uint hdlr = 0,
-
- ///
- /// Item Data
- ///
- public const uint idat = 0, // 'idat'
+ public const uint hdlr = 0;
///
/// Item Information
///
- public const uint iinf = 0, // 'iinf'
+ public const uint iinf = 0; // 'iinf'
///
/// Item Property
///
- public const uint iprp = 0, // 'iprp'
+ public const uint iprp = 0; // 'iprp'
///
/// Item Protection
///
- public const uint ipro = 0, // 'ipro'
+ public const uint ipro = 0; // 'ipro'
///
/// Item Reference
///
- public const uint iref = 0, // 'iref'
+ public const uint iref = 0; // 'iref'
///
/// Grid
///
- public const uint grid = 0, // 'grid'
+ public const uint grid = 0; // 'grid'
///
/// Derived Image
///
- public const uint dimg = 0, // 'dimg'
+ public const uint dimg = 0; // 'dimg'
///
/// Thumbnail
///
- public const uint thmb = 0, // 'thmb'
+ public const uint thmb = 0; // 'thmb'
///
/// Content Description
///
- public const uint cdsc = 0, // 'cdsc'
+ public const uint cdsc = 0; // 'cdsc'
+
+ ///
+ /// MIME type
+ ///
+ public const uint mime = 0; // 'mime'
+
+ ///
+ /// URI
+ ///
+ public const uint uri = 0; // 'uri '
public static uint Parse(string code)
{
if (code.Length != 4)
{
- throw new ImageFormatException();
+ throw new ImageFormatException("Unbale to parse FourCC code of more than 4 characters.");
}
Span span = Encoding.UTF8.GetBytes(code);
- return BinaryPrimitives.ReadUInt32BigEndian(buffer);
+ return BinaryPrimitives.ReadUInt32BigEndian(span);
}
public static string ToString(uint fourcc)
{
- Span span = stackalloc new byte[4];
- BinaryPrimitives.WriteUInt32BigEndian(buffer, fourcc);
+ Span span = stackalloc byte[4];
+ BinaryPrimitives.WriteUInt32BigEndian(span, fourcc);
return Encoding.UTF8.GetString(span);
}
}
diff --git a/src/ImageSharp/Formats/Heic/HeicBoxType.cs b/src/ImageSharp/Formats/Heic/HeicBoxType.cs
deleted file mode 100644
index 45cecacb68..0000000000
--- a/src/ImageSharp/Formats/Heic/HeicBoxType.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats.Heic;
-
-///
-/// Provides enumeration of supported ISO Base Format Box Types for HEIC.
-///
-public enum HeicBoxType : uint
-{
- FileType = FourCharacterCode.ftyp,
- Meta = FourCharacterCode.meta,
- MediaData = FourCharacterCode.mdat,
- ItemInfo = FourCharacterCode.infe
- ItemData = FourCharacterCode.idat,
- ItemLocation = FourCharacterCode.iloc,
- Exif = FourCharacterCode.Exif,
- ItemPropertyAssociation = FourCharacterCode.ipma,
- DataReference = FourCharacterCode.dref,
- PrimaryItemReference = FourCharacterCode.pitm,
- ImageSpatialExtentsProperty = FourCharacterCode.ispe,
-
- // Possible box types outside of HEIC images:
- Movie = FourCharacterCode.moov,
- Track = FourCharacterCode.trak,
-}
diff --git a/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs b/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs
index 551696e912..a9e7c4d056 100644
--- a/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs
+++ b/src/ImageSharp/Formats/Heic/HeicDecoderCore.cs
@@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Diagnostics.CodeAnalysis;
+using System.Buffers.Binary;
+using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats.Heic;
@@ -25,6 +25,8 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
///
private ImageMetadata? metadata;
+ private uint primaryItem;
+
private List items;
private List itemLinks;
@@ -37,6 +39,9 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{
this.Options = options;
this.configuration = options.Configuration;
+ this.metadata = new ImageMetadata();
+ this.items = new List();
+ this.itemLinks = new List();
}
///
@@ -51,19 +56,19 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{
if (!this.CheckFileTypeBox(stream))
{
- throw new InvalidImageFormatException();
+ throw new ImageFormatException("Not an HEIC image.");
}
- while (!stream.Eof)
+ while (stream.EofHitCount == 0)
{
- var length = ReadBoxHeader(stream, out var boxType);
+ long length = this.ReadBoxHeader(stream, out var boxType);
switch (boxType)
{
- case HeicBoxType.Meta:
- ParseMetadata(stream, length);
+ case FourCharacterCode.meta:
+ this.ParseMetadata(stream, length);
break;
- case HeicBoxType.MediaData:
- ParseMediaData(stream, length);
+ case FourCharacterCode.mdat:
+ this.ParseMediaData(stream, length);
break;
default:
throw new ImageFormatException($"Unknown box type of '{FourCharacterCode.ToString(boxType)}'");
@@ -82,63 +87,64 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{
this.CheckFileTypeBox(stream);
- while (!stream.Eof)
+ while (stream.EofHitCount == 0)
{
- var length = ReadBoxHeader(stream, out var boxType);
- var buffer = new byte[length];
- stream.Read(buffer);
+ long length = this.ReadBoxHeader(stream, out uint boxType);
switch (boxType)
{
- case HeicBoxType.Metadata:
- ParseMetadata(buffer);
+ case FourCharacterCode.meta:
+ this.ParseMetadata(stream, length);
break;
default:
// Silently skip all other box types.
break;
}
}
+
return new ImageInfo(new PixelTypeInfo(bitsPerPixel), new(this.pixelSize.Width, this.pixelSize.Height), this.metadata);
}
private bool CheckFileTypeBox(BufferedReadStream stream)
{
- var boxLength = ReadBoxHeader(stream, out var boxType);
- Span buffer = stackalloc new byte[boxLength];
+ var boxLength = this.ReadBoxHeader(stream, out var boxType);
+ Span buffer = stackalloc byte[(int)boxLength];
stream.Read(buffer);
- var majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buf);
+ var majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer);
+
// TODO: Interpret minorVersion and compatible brands.
- return boxTypepe == HeicBoxType.FileType && majorBrand == HeicConstants.HeicBrand;
+ return boxType == FourCharacterCode.ftyp && majorBrand == FourCharacterCode.heic;
}
- private ulong ReadBoxHeader(BufferedReadStream stream, out uint boxType)
+ private long ReadBoxHeader(BufferedReadStream stream, out uint boxType)
{
// Read 4 bytes of length of box
- Span buf = stackalloc new byte[8];
+ Span buf = stackalloc byte[8];
stream.Read(buf);
- ulong boxSize = BinaryPrimitives.ReadUInt32BigEndian(buf);
- ulong headerSize = 8;
+ long boxSize = BinaryPrimitives.ReadUInt32BigEndian(buf);
+ long headerSize = 8;
+
// Read 4 bytes of box type
- boxType = BinaryPrimitives.ReadUInt32BigEndian(buf.Slice(4));
+ boxType = BinaryPrimitives.ReadUInt32BigEndian(buf[4..]);
if (boxSize == 1)
{
stream.Read(buf);
- boxSize = BinaryPrimitives.ReadUInt64BigEndian(buf);
- headerSize += 8UL;
+ boxSize = (long)BinaryPrimitives.ReadUInt64BigEndian(buf);
+ headerSize += 8;
}
return boxSize - headerSize;
}
- private uint ParseBoxHeader(Span buffer, out ulong length, out uint boxType)
+ private static int ParseBoxHeader(Span buffer, out long length, out uint boxType)
{
- ulong boxSize = BinaryPrimitives.ReadUInt32BigEndian(buffer);
- ulong bytesRead = 4;
- boxType = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ long boxSize = BinaryPrimitives.ReadUInt32BigEndian(buffer);
+ int bytesRead = 4;
+ boxType = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
if (boxSize == 1)
{
- boxSize = BinaryPrimitives.ReadUInt64BigEndian(buffer.Slice(bytesRead));
+ boxSize = (long)BinaryPrimitives.ReadUInt64BigEndian(buffer[bytesRead..]);
bytesRead += 8;
}
@@ -146,30 +152,32 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
return bytesRead;
}
- private void ParseMetadata(BufferedReadStream stream, uint boxLength)
+ private void ParseMetadata(BufferedReadStream stream, long boxLength)
{
- var endPosition = stream.Position + boxLength;
+ long endPosition = stream.Position + boxLength;
while (stream.Position < endPosition)
{
- var length = ReadBoxHeader(stream, out var boxType);
+ long length = this.ReadBoxHeader(stream, out uint boxType);
switch (boxType)
{
- case HeicMetaSubBoxType.ItemProperty:
- ParseItemPropertyContainer(stream, length);
+ case FourCharacterCode.iprp:
+ this.ParseItemPropertyContainer(stream, length);
break;
- case HeicMetaSubBoxType.ItemInfo:
- ParseItemInfo(stream, length);
+ case FourCharacterCode.iinf:
+ this.ParseItemInfo(stream, length);
break;
- case HeicMetaSubBoxType.ItemReference:
- ParseItemReference(stream, length);
+ case FourCharacterCode.iref:
+ this.ParseItemReference(stream, length);
break;
- case HeicMetaSubBoxType.DataInformation:
- case HeicMetaSubBoxType.GroupsList:
- case HeicMetaSubBoxType.Handler:
- case HeicMetaSubBoxType.ItemData:
- case HeicMetaSubBoxType.ItemLocation:
- case HeicMetaSubBoxType.ItemProtection:
- case HeicMetaSubBoxType.PrimaryItem:
+ case FourCharacterCode.pitm:
+ this.ParsePrimaryItem(stream, length);
+ break;
+ case FourCharacterCode.dinf:
+ case FourCharacterCode.grpl:
+ case FourCharacterCode.hdlr:
+ case FourCharacterCode.idat:
+ case FourCharacterCode.iloc:
+ case FourCharacterCode.ipro:
// TODO: Implement
break;
default:
@@ -178,63 +186,65 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
- private void ParseItemInfo(BufferedReadStream stream, uint boxLength)
+ private void ParseItemInfo(BufferedReadStream stream, long boxLength)
{
- Span buffer = new byte[length];
+ Span buffer = stackalloc byte[(int)boxLength];
stream.Read(buffer);
uint entryCount;
int bytesRead = 0;
if (buffer[bytesRead] == 0)
{
bytesRead += 4;
- entryCount = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ entryCount = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
}
else
{
bytesRead += 4;
- entryCount = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ entryCount = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
}
- for(uint i = 0; i < entryCount; i++)
+ for (uint i = 0; i < entryCount; i++)
{
- bytesRead += ParseBoxHeader(out var subBoxLength, out var boxType);
- ParseItemInfoEntry(buffer.Slice(bytesRead, subBoxLength));
- bytesRead += subBoxLength;
+ bytesRead += this.ParseItemInfoEntry(buffer[bytesRead..]);
}
}
- private void ParseItemInfoEntry(Span buffer, uint boxLength)
+ private int ParseItemInfoEntry(Span buffer)
{
- int bytesRead = 0;
- var version = buffer[bytesRead];
+ int bytesRead = ParseBoxHeader(buffer, out long boxLength, out uint boxType);
+ byte version = buffer[bytesRead];
bytesRead += 4;
- var item = new HeicItem();
- if (version == 0 || version == 1)
+ HeicItem? item = null;
+ if (version is 0 or 1)
{
- item.Id = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ uint itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
- item.ProtectionIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ item = new HeicItem(boxType, itemId);
+
+ // Skip Protection Index, not sure what that means...
bytesRead += 2;
- item.Name = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item.Name = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.Name.Length + 1;
- item.ContentType = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentType.Length + 1;
+
// Optional field.
if (bytesRead < boxLength)
{
- item.ContentEncoding = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item.ContentEncoding = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentEncoding.Length + 1;
}
}
if (version == 1)
{
+
// Optional fields.
if (bytesRead < boxLength)
{
- item.ExtensionType = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ item!.ExtensionType = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
}
@@ -246,132 +256,162 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
if (version >= 2)
{
- if (getVersion() == 2)
+ uint itemId = 0U;
+ if (version == 2)
{
- item.Id = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
}
- else if (getVersion() == 3)
+ else if (version == 3)
{
- item.Id = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ itemId = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
}
- item.ProtectionIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ // Skip Protection Index, not sure what that means...
bytesRead += 2;
- item.Type = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ uint itemType = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
- item.Name = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item = new HeicItem(itemId, itemType);
+ item.Name = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.Name.Length + 1;
- if (item.Type == "mime")
+ if (item.Type == FourCharacterCode.mime)
{
- item.ContentType = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentType.Length + 1;
+
// Optional field.
if (bytesRead < boxLength)
{
- item.ContentEncoding = ReadNullTerminatedString(buffer.Slice(bytesRead));
+ item.ContentEncoding = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentEncoding.Length + 1;
}
}
- else if (item.Type == "uri ")
+ else if (item.Type == FourCharacterCode.uri)
{
- item.UriType = ReadNullTerminatedString(buffer.Slice(bytesRead));
- bytesRead += item.ContentEncoding.Length + 1;
+ item.UriType = ReadNullTerminatedString(buffer[bytesRead..]);
+ bytesRead += item.UriType.Length + 1;
}
}
+
+ return bytesRead;
}
- private void ParseItemReference(BufferedReadStream stream, uint boxLength)
+ private void ParseItemReference(BufferedReadStream stream, long boxLength)
{
- Span buffer = new byte[length];
+ Span buffer = new byte[boxLength];
stream.Read(buffer);
int bytesRead = 0;
bool largeIds = buffer[bytesRead] != 0;
bytesRead += 4;
- while(bytesRead < boxLength)
+ while (bytesRead < boxLength)
{
- ParseBoxHeader(buffer.Slice(bytesRead), out var subBoxLength, out var linkType);
- var link = new HeicItemLink();
- link.Type = linkType;
+ ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out uint linkType);
+ uint sourceId;
if (largeIds)
{
- link.Source = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ sourceId = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
}
else
{
- link.Source = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ sourceId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
}
- var count = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ HeicItemLink link = new(linkType, sourceId);
+
+ int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
- for(uint i = 0; i < count; i++)
+ for (uint i = 0; i < count; i++)
{
uint destId;
if (largeIds)
{
- destId = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
+ destId = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
}
else
{
- destId = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
+ destId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
}
- link.Destinations.Add(destId);
+
+ link.DestinationIds.Add(destId);
}
- itemLinks.Add(link);
+ this.itemLinks!.Add(link);
}
}
- private void ParseItemPropertyContainer(BufferedReadStream stream, uint boxLength)
+ private void ParsePrimaryItem(BufferedReadStream stream, long boxLength)
{
- var containerLength = ReadBoxHeader(stream, out var containerType);
+ // BoxLength should be 6 or 8.
+ Span buffer = stackalloc byte[(int)boxLength];
+ stream.Read(buffer);
+ byte version = buffer[0];
+ if (version == 0)
+ {
+ this.primaryItem = BinaryPrimitives.ReadUInt16BigEndian(buffer[4..]);
+ }
+ else
+ {
+ this.primaryItem = BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
+ }
+ }
+
+ private void ParseItemPropertyContainer(BufferedReadStream stream, long boxLength)
+ {
+ // Cannot use Dictionary here, Properties can have multiple instances with the same key.
+ List> properties = new();
+ long containerLength = this.ReadBoxHeader(stream, out uint containerType);
if (containerType == FourCharacterCode.ipco)
{
// Parse Item Property Container, which is just an array of preperty boxes.
- var endPosition = stream.Position + containerLength;
+ long endPosition = stream.Position + containerLength;
while (stream.Position < endPosition)
{
- var length = ReadBoxHeader(stream, out var boxType);
+ int length = (int)this.ReadBoxHeader(stream, out uint boxType);
+ Span buffer = stackalloc byte[length];
switch (boxType)
{
- case HeicItemPropertyType.ImageSpatialExtents:
+ case FourCharacterCode.ispe:
// Length should be 12.
- Span buffer = stackalloc new byte[length];
stream.Read(buffer);
+
// Skip over version (8 bits) and flags (24 bits).
- var width = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(4));
- var height = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(8));
+ uint width = BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
+ uint height = BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]);
+ properties.Add(new KeyValuePair(FourCharacterCode.ispe, new uint[] { width, height }));
break;
- case HeicItemPropertyType.PixelAspectRatio:
+ case FourCharacterCode.pasp:
// Length should be 8.
- Span buffer = stackalloc new byte[length];
stream.Read(buffer);
- var horizontalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer);
- var verticalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(4));
+ uint horizontalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer);
+ uint verticalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
+ properties.Add(new KeyValuePair(FourCharacterCode.pasp, new uint[] { horizontalSpacing, verticalSpacing }));
break;
- case HeicItemPropertyType.PixelInformation:
- Span buffer = stackalloc new byte[length];
+ case FourCharacterCode.pixi:
stream.Read(buffer);
+
// Skip over version (8 bits) and flags (24 bits).
- var channelCount = buffer[4];
+ int channelCount = buffer[4];
int offset = 5;
int bitsPerPixel = 0;
for (int i = 0; i < channelCount; i++)
{
- bitsPerPixel += buffer[i];
+ bitsPerPixel += buffer[offset + i];
}
+
+ properties.Add(new KeyValuePair(FourCharacterCode.pixi, new int[] { channelCount, bitsPerPixel }));
+
break;
- case HeicItemPropertyType.AcessibilityText:
- case HeicItemPropertyType.ImageMirror:
- case HeicItemPropertyType.ImageRotation:
- case HeicItemPropertyType.ImageScaling:
- case HeicItemPropertyType.RelativeLocation:
- case HeicItemPropertyType.UserDescription;
+ case FourCharacterCode.altt:
+ case FourCharacterCode.imir:
+ case FourCharacterCode.irot:
+ case FourCharacterCode.iscl:
+ case FourCharacterCode.rloc:
+ case FourCharacterCode.udes:
// TODO: Implement
break;
default:
@@ -382,42 +422,49 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
else if (containerType == FourCharacterCode.ipma)
{
// Parse Item Property Association
+ Span buffer = stackalloc byte[(int)boxLength];
+ byte version = buffer[0];
+ byte flags = buffer[3];
+ int itemId;
+ int bytesRead = 4;
+ if (version < 1)
+ {
+ itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
+ bytesRead += 2;
+ }
+ else
+ {
+ itemId = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
+ bytesRead += 4;
+ }
+
+ int associationCount = buffer[bytesRead++];
+ for (int i = 0; i < associationCount; i++)
+ {
+ uint propId;
+ if (flags == 1)
+ {
+ propId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]) & 0x4FFFU;
+ bytesRead += 2;
+ }
+ else
+ {
+ propId = buffer[bytesRead++] & 0x4FU;
+ }
+
+ this.items![itemId].SetProperty(properties[(int)propId]);
+ }
}
}
- private void ParseMediaData(BufferedReadStream stream, uint boxLength)
+ private void ParseMediaData(BufferedReadStream stream, long boxLength)
{
// TODO: Implement
}
- ///
- /// Forwards the stream to just past the Start of NAL marker.
- ///
- private void FindStartOfNal(BufferedReadStream stream)
+ private static string ReadNullTerminatedString(Span span)
{
- uint i = stream.Position;
- uint length = 0;
- var dataLength = stream.Length;
-
- while (i < streamLength)
- {
- var current = stream.ReadByte();
- if (current == 0)
- {
- length++;
- }
- else if (length > 1 && current == 1)
- {
- // Found the marker !
- //length++;
- break;
- }
- else
- {
- // False alarm, resetting...
- length = 0;
- }
- i++;
- }
+ Span bytes = span[..span.IndexOf((byte)0)];
+ return Encoding.UTF8.GetString(bytes);
}
}
diff --git a/src/ImageSharp/Formats/Heic/HeicEncoderCore.cs b/src/ImageSharp/Formats/Heic/HeicEncoderCore.cs
index 291ece9d52..55f892a8ce 100644
--- a/src/ImageSharp/Formats/Heic/HeicEncoderCore.cs
+++ b/src/ImageSharp/Formats/Heic/HeicEncoderCore.cs
@@ -47,7 +47,6 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
Guard.NotNull(stream, nameof(stream));
// TODO: Implement
-
stream.Flush();
}
}
diff --git a/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs b/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs
index 583d9f4f54..eeda941092 100644
--- a/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Formats.Heic;
@@ -11,8 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Heic;
public sealed class HeicImageFormatDetector : IImageFormatDetector
{
///
- int HeaderSize => 12;
-
+ public int HeaderSize => 12;
///
public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format)
@@ -21,10 +21,7 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector
return format != null;
}
- private static bool IsSupportedFileFormat(ReadOnlySpan header)
- {
- return
- BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == FourCharacterCode.ftyp &&
- BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)) == FourCharacterCode.heic
- }
+ private static bool IsSupportedFileFormat(ReadOnlySpan header) =>
+ BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == FourCharacterCode.ftyp &&
+ BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)) == FourCharacterCode.heic;
}
diff --git a/src/ImageSharp/Formats/Heic/HeicItem.cs b/src/ImageSharp/Formats/Heic/HeicItem.cs
index 0dfc442d47..4349a65858 100644
--- a/src/ImageSharp/Formats/Heic/HeicItem.cs
+++ b/src/ImageSharp/Formats/Heic/HeicItem.cs
@@ -3,30 +3,62 @@
namespace SixLabors.ImageSharp.Formats.Heic;
-public enum HeicItemType
-{
- Hvc1,
- Grid,
- Exif
-}
-
-public class HeicItemLink
-{
- public uint Type;
- public HeicItem Source;
- public List Destinations = new List();
-}
-
///
/// Provides definition for a HEIC Item.
///
-public class HeicItem
+public class HeicItem(uint type, uint id)
{
- public uint Id;
- public HeicItemType type;
- public string Name;
- public string ContentType;
- public string ContentEncoding;
- public uint ExtensionType;
- public string UriType;
+ ///
+ /// Gets the ID of this Item.
+ ///
+ public uint Id { get; } = id;
+
+ ///
+ /// Gets the type of this Item.
+ ///
+ public uint Type { get; } = type;
+
+ ///
+ /// Gets or sets the name of this item.
+ ///
+ public string? Name { get; set; }
+
+ ///
+ /// Gets or sets the Content Type of this item.
+ ///
+ public string? ContentType { get; set; }
+
+ ///
+ /// Gets or sets the Content Encoding of this item.
+ ///
+ public string? ContentEncoding { get; set; }
+
+ ///
+ /// Gets or sets the type of extension of this item.
+ ///
+ public uint ExtensionType { get; set; }
+
+ ///
+ /// Gets or sets the URI of this item.
+ ///
+ public string? UriType { get; set; }
+
+ ///
+ /// Sets a property on this item.
+ ///
+ public void SetProperty(KeyValuePair pair)
+ {
+ switch (pair.Key)
+ {
+ case FourCharacterCode.ispe:
+ // Set image extents
+ break;
+ case FourCharacterCode.pasp:
+ // Set pixel aspact ratio
+ break;
+ case FourCharacterCode.pixi:
+ // Set pixel information
+ break;
+ }
+ }
}
diff --git a/src/ImageSharp/Formats/Heic/HeicItemLink.cs b/src/ImageSharp/Formats/Heic/HeicItemLink.cs
new file mode 100644
index 0000000000..a93946c2c4
--- /dev/null
+++ b/src/ImageSharp/Formats/Heic/HeicItemLink.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heic;
+
+///
+/// Link between instances within the same HEIC file.
+///
+public class HeicItemLink(uint type, uint sourceId)
+{
+ ///
+ /// Gets the type of link.
+ ///
+ public uint Type { get; } = type;
+
+ ///
+ /// Gets the ID of the source item of this link.
+ ///
+ public uint SourceId { get; } = sourceId;
+
+ ///
+ /// Gets the destination item IDs of this link.
+ ///
+ public List DestinationIds { get; } = new List();
+}
diff --git a/src/ImageSharp/Formats/Heic/HeicItemPropertyType.cs b/src/ImageSharp/Formats/Heic/HeicItemPropertyType.cs
deleted file mode 100644
index 157860f184..0000000000
--- a/src/ImageSharp/Formats/Heic/HeicItemPropertyType.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats.Heic;
-
-///
-/// Provides enumeration of supported Item Property Types for HEIC.
-///
-public enum HeicItemPropertyType : uint
-{
- Invalid = 0,
- AcessibilityText = FourCharacterCode.altt,
- Colour = FourCharacterCode.colr,
- HvcConfiguration = FourCharacterCode.hvcC,
- ImageMirror = FourCharacterCode.imir,
- ImageRotation = FourCharacterCode.irot,
- ImageScaling = FourCharacterCode.iscl,
- ImageSpatialExtents = FourCharacterCode.ispe,
- PixelAspectRatio = FourCharacterCode.pasp,
- PixelInformation = FourCharacterCode.pixi,
- RelativeLocation = FourCharacterCode.rloc,
- UserDescription = FourCharacterCode.udes,
-}
diff --git a/src/ImageSharp/Formats/Heic/HeicMetaSubBoxType.cs b/src/ImageSharp/Formats/Heic/HeicMetaSubBoxType.cs
deleted file mode 100644
index 46d29878c3..0000000000
--- a/src/ImageSharp/Formats/Heic/HeicMetaSubBoxType.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats.Heic;
-
-///
-/// Provides enumeration of supported sub type boxes within the 'meta' box for HEIC.
-///
-public enum HeicMetaSubBoxType : uint
-{
- Invalid = 0,
- DataInformation = 0, // 'dinf'
- GroupsList = 0, // 'grpl'
- Handler = 0, // 'hdlr'
- ItemData = 0, // 'idat'
- ItemInfo = 0, // 'iinf'
- ItemLocation = 0, // 'iloc'
- ItemProperty = 0, // 'iprp'
- ItemProtection = 0, // 'ipro'
- ItemReference = 0, // 'iref'
- PrimaryItem = 0, // 'pitm'
-}