Browse Source

Byte alignment fixes

pull/2633/head
Ynse Hoornenborg 3 years ago
parent
commit
f892c0475f
  1. 73
      src/ImageSharp/Formats/Heic/HeicDecoderCore.cs

73
src/ImageSharp/Formats/Heic/HeicDecoderCore.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Globalization;
using System.Text; using System.Text;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -62,7 +63,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
throw new ImageFormatException("Not an HEIC image."); throw new ImageFormatException("Not an HEIC image.");
} }
while (stream.EofHitCount == 0) while (stream.Position < stream.Length)
{ {
long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
switch (boxType) switch (boxType)
@ -97,7 +98,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{ {
this.CheckFileTypeBox(stream); this.CheckFileTypeBox(stream);
while (stream.EofHitCount == 0) while (stream.Position < stream.Length)
{ {
long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
switch (boxType) switch (boxType)
@ -170,13 +171,14 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private void ParseMetadata(BufferedReadStream stream, long boxLength) private void ParseMetadata(BufferedReadStream stream, long boxLength)
{ {
long endPosition = stream.Position + boxLength; long endPosition = stream.Position + boxLength;
stream.Skip(4);
while (stream.Position < endPosition) while (stream.Position < endPosition)
{ {
long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
switch (boxType) switch (boxType)
{ {
case Heic4CharCode.iprp: case Heic4CharCode.iprp:
this.ParseItemPropertyContainer(stream, length); this.ParseItemProperties(stream, length);
break; break;
case Heic4CharCode.iinf: case Heic4CharCode.iinf:
this.ParseItemInfo(stream, length); this.ParseItemInfo(stream, length);
@ -273,9 +275,9 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// Skip Protection Index, not sure what that means... // Skip Protection Index, not sure what that means...
bytesRead += 2; bytesRead += 2;
uint itemType = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); Heic4CharCode itemType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4; bytesRead += 4;
item = new HeicItem((Heic4CharCode)itemId, itemType); item = new HeicItem(itemType, itemId);
item.Name = ReadNullTerminatedString(buffer[bytesRead..]); item.Name = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.Name.Length + 1; bytesRead += item.Name.Length + 1;
if (item.Type == Heic4CharCode.mime) if (item.Type == Heic4CharCode.mime)
@ -297,6 +299,11 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
} }
} }
if (item != null)
{
this.items.Add(item);
}
return bytesRead; return bytesRead;
} }
@ -308,7 +315,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bytesRead += 4; bytesRead += 4;
while (bytesRead < boxLength) while (bytesRead < boxLength)
{ {
ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heic4CharCode linkType); bytesRead += ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heic4CharCode linkType);
uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead); uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead);
HeicItemLink link = new(linkType, sourceId); HeicItemLink link = new(linkType, sourceId);
@ -329,24 +336,44 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// BoxLength should be 6 or 8. // BoxLength should be 6 or 8.
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength); Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0]; byte version = buffer[0];
int bytesRead = 0; int bytesRead = 4;
this.primaryItem = ReadUInt16Or32(buffer, version != 0, ref bytesRead); this.primaryItem = ReadUInt16Or32(buffer, version != 0, ref bytesRead);
} }
private void ParseItemPropertyContainer(BufferedReadStream stream, long boxLength) private void ParseItemProperties(BufferedReadStream stream, long boxLength)
{ {
// Cannot use Dictionary here, Properties can have multiple instances with the same key. // Cannot use Dictionary here, Properties can have multiple instances with the same key.
List<KeyValuePair<Heic4CharCode, object>> properties = new(); List<KeyValuePair<Heic4CharCode, object>> properties = new();
long endBoxPosition = stream.Position + boxLength;
while (stream.Position < endBoxPosition)
{
long containerLength = this.ReadBoxHeader(stream, out Heic4CharCode containerType); long containerLength = this.ReadBoxHeader(stream, out Heic4CharCode containerType);
if (containerType == Heic4CharCode.ipco) if (containerType == Heic4CharCode.ipco)
{ {
// Parse Item Property Container, which is just an array of preperty boxes. // Parse Item Property Container, which is just an array of property boxes.
long endPosition = stream.Position + containerLength; this.ParsePropertyContainer(stream, containerLength, properties);
}
else if (containerType == Heic4CharCode.ipma)
{
// Parse Item Property Association
this.ParsePropertyAssociation(stream, containerLength, properties);
}
else
{
string enumName = Enum.GetName(containerType) ?? ((uint)containerType).ToString("X", CultureInfo.InvariantCulture);
throw new ImageFormatException($"Unknown container type in property box of '{enumName}'");
}
}
}
private void ParsePropertyContainer(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heic4CharCode, object>> properties)
{
long endPosition = stream.Position + boxLength;
while (stream.Position < endPosition) while (stream.Position < endPosition)
{ {
int length = (int)this.ReadBoxHeader(stream, out Heic4CharCode boxType); int itemLength = (int)this.ReadBoxHeader(stream, out Heic4CharCode itemType);
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength); Span<byte> buffer = this.ReadIntoBuffer(stream, itemLength);
switch (boxType) switch (itemType)
{ {
case Heic4CharCode.ispe: case Heic4CharCode.ispe:
// Skip over version (8 bits) and flags (24 bits). // Skip over version (8 bits) and flags (24 bits).
@ -373,22 +400,24 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
break; break;
case Heic4CharCode.altt: case Heic4CharCode.altt:
case Heic4CharCode.colr:
case Heic4CharCode.imir: case Heic4CharCode.imir:
case Heic4CharCode.irot: case Heic4CharCode.irot:
case Heic4CharCode.iscl: case Heic4CharCode.iscl:
case Heic4CharCode.hvcC:
case Heic4CharCode.rloc: case Heic4CharCode.rloc:
case Heic4CharCode.udes: case Heic4CharCode.udes:
// TODO: Implement // TODO: Implement
SkipBox(stream, length);
break; break;
default: default:
throw new ImageFormatException($"Unknown item property box type of '{Enum.GetName(boxType)}'"); string enumName = Enum.GetName(itemType) ?? ((uint)itemType).ToString("X", CultureInfo.InvariantCulture);
throw new ImageFormatException($"Unknown item type in property box of '{enumName}'");
} }
} }
} }
else if (containerType == Heic4CharCode.ipma)
private void ParsePropertyAssociation(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heic4CharCode, object>> properties)
{ {
// Parse Item Property Association
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength); Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0]; byte version = buffer[0];
byte flags = buffer[3]; byte flags = buffer[3];
@ -426,13 +455,13 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
} }
} }
} }
}
private void ParseItemLocation(BufferedReadStream stream, long boxLength) private void ParseItemLocation(BufferedReadStream stream, long boxLength)
{ {
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength); Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
int bytesRead = 0; int bytesRead = 0;
byte version = buffer[bytesRead++]; byte version = buffer[bytesRead];
bytesRead += 4;
byte b1 = buffer[bytesRead++]; byte b1 = buffer[bytesRead++];
byte b2 = buffer[bytesRead++]; byte b2 = buffer[bytesRead++];
int offsetSize = (b1 >> 4) & 0x0f; int offsetSize = (b1 >> 4) & 0x0f;
@ -444,13 +473,13 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
indexSize = b2 & 0x0f; indexSize = b2 & 0x0f;
} }
uint itemCount = ReadUInt16Or32(buffer, version >= 2, ref bytesRead); uint itemCount = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
for (uint i = 0; i < itemCount; i++) for (uint i = 0; i < itemCount; i++)
{ {
uint itemId = ReadUInt16Or32(buffer, version >= 2, ref bytesRead); uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
if (version is 1 or 2) if (version is 1 or 2)
{ {
bytesRead += 2; bytesRead++;
byte b3 = buffer[bytesRead++]; byte b3 = buffer[bytesRead++];
int constructionMethod = b3 & 0x0f; int constructionMethod = b3 & 0x0f;
} }

Loading…
Cancel
Save