Skip to content

Commit

Permalink
Fully implement Heif Item Location
Browse files Browse the repository at this point in the history
  • Loading branch information
ynse01 committed Jul 8, 2024
1 parent 23d533a commit a48e271
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 28 deletions.
60 changes: 36 additions & 24 deletions src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,10 @@ private void ParseItemLocation(BufferedReadStream stream, long boxLength)
int bytesRead = 0;
byte version = boxBuffer[bytesRead];
bytesRead += 4;
byte b1 = boxBuffer[bytesRead++];
byte b2 = boxBuffer[bytesRead++];
byte b1 = boxBuffer[bytesRead];
bytesRead++;
byte b2 = boxBuffer[bytesRead];
bytesRead++;
int offsetSize = (b1 >> 4) & 0x0f;
int lengthSize = b1 & 0x0f;
int baseOffsetSize = (b2 >> 4) & 0x0f;
Expand All @@ -554,28 +556,31 @@ private void ParseItemLocation(BufferedReadStream stream, long boxLength)
{
uint itemId = ReadUInt16Or32(boxBuffer, version == 2, ref bytesRead);
HeifItem? item = this.FindItemById(itemId);
HeifLocationOffsetOrigin constructionMethod = HeifLocationOffsetOrigin.FileOffset;
if (version is 1 or 2)
{
bytesRead++;
byte b3 = boxBuffer[bytesRead++];
int constructionMethod = b3 & 0x0f;
byte b3 = boxBuffer[bytesRead];
bytesRead++;
constructionMethod = (HeifLocationOffsetOrigin)(b3 & 0x0f);
}

uint dataReferenceIndex = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]);
bytesRead += 2;
ulong baseOffset = ReadUIntVariable(boxBuffer, baseOffsetSize, ref bytesRead);
long baseOffset = ReadUIntVariable(boxBuffer, baseOffsetSize, ref bytesRead);
uint extentCount = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]);
bytesRead += 2;
for (uint j = 0; j < extentCount; j++)
{
uint extentIndex = 0;
if (version is 1 or 2 && indexSize > 0)
{
_ = ReadUIntVariable(boxBuffer, indexSize, ref bytesRead);
extentIndex = (uint)ReadUIntVariable(boxBuffer, indexSize, ref bytesRead);
}

ulong extentOffset = ReadUIntVariable(boxBuffer, offsetSize, ref bytesRead);
ulong extentLength = ReadUIntVariable(boxBuffer, lengthSize, ref bytesRead);
HeifLocation loc = new HeifLocation((long)extentOffset, (long)extentLength);
long extentOffset = ReadUIntVariable(boxBuffer, offsetSize, ref bytesRead);
long extentLength = ReadUIntVariable(boxBuffer, lengthSize, ref bytesRead);
HeifLocation loc = new(constructionMethod, baseOffset, dataReferenceIndex, extentOffset, extentLength, extentIndex);
item?.DataLocations.Add(loc);
}
}
Expand All @@ -598,29 +603,36 @@ private static uint ReadUInt16Or32(Span<byte> buffer, bool isLarge, ref int byte
return result;
}

private static ulong ReadUIntVariable(Span<byte> buffer, int numBytes, ref int bytesRead)
private static long ReadUIntVariable(Span<byte> buffer, int numBytes, ref int bytesRead)
{
ulong result = 0UL;
if (numBytes == 8)
long result = 0L;
int shift = 0;
if (numBytes > 8)
{
result = BinaryPrimitives.ReadUInt64BigEndian(buffer[bytesRead..]);
bytesRead += 8;
throw new InvalidImageContentException($"Can't store large integer of {numBytes * 8} bits.");
}
else if (numBytes == 4)
else
if (numBytes > 4)
{
result = (long)BinaryPrimitives.ReadUInt64BigEndian(buffer[bytesRead..]);
shift = 8 - numBytes;
}
else if (numBytes > 2)
{
result = BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
shift = 4 - numBytes;
}
else if (numBytes == 2)
else if (numBytes > 1)
{
result = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
}
else if (numBytes == 1)
{
result = buffer[bytesRead++];
result = buffer[bytesRead];
}

bytesRead += numBytes;
result >>= shift << 3;
return result;
}

Expand All @@ -642,11 +654,11 @@ private Image<TPixel> ParseMediaData<TPixel>(BufferedReadStream stream, long box
throw new NotImplementedException("No HVC decoding implemented yet");
}

int thumbFileOffset = (int)thumbItem.DataLocations[0].Offset;
int thumbFileLength = (int)thumbItem.DataLocations[0].Length;
stream.Skip((int)(thumbFileOffset - stream.Position));
EnsureBoxBoundary(thumbFileLength, stream);
using IMemoryOwner<byte> thumbMemory = this.ReadIntoBuffer(stream, thumbFileLength);
long thumbPosition = thumbItem.DataLocations[0].GetStreamPosition(stream.Position, stream.Position);
long thumbLength = thumbItem.DataLocations[0].Length;
stream.Skip((int)(thumbPosition - stream.Position));
EnsureBoxBoundary(thumbLength, stream);
using IMemoryOwner<byte> thumbMemory = this.ReadIntoBuffer(stream, thumbLength);
Span<byte> thumbSpan = thumbMemory.GetSpan();

HeifMetadata meta = this.metadata.GetHeifMetadata();
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private static void GenerateItems<TPixel>(Image<TPixel> image, byte[] pixels, Li
where TPixel : unmanaged, IPixel<TPixel>
{
HeifItem primaryItem = new(Heif4CharCode.Jpeg, 1u);
primaryItem.DataLocations.Add(new HeifLocation(0L, pixels.LongLength));
primaryItem.DataLocations.Add(new HeifLocation(HeifLocationOffsetOrigin.ItemDataOffset, 0L, 0U, 0L, pixels.LongLength, 0U));
primaryItem.BitsPerPixel = 24;
primaryItem.ChannelCount = 3;
primaryItem.SetExtent(image.Size);
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Heif/HeifItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ internal class HeifItem(Heif4CharCode type, uint id)
/// <summary>
/// Gets the list of data locations for this item.
/// </summary>
public List<HeifLocation> DataLocations { get; } = new List<HeifLocation>();
public List<HeifLocation> DataLocations { get; } = [];

/// <summary>
/// Set the image extent.
Expand Down
36 changes: 34 additions & 2 deletions src/ImageSharp/Formats/Heif/HeifLocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,47 @@ namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Location within the file of an <see cref="HeifItem"/>.
/// </summary>
internal class HeifLocation(long offset, long length)
internal class HeifLocation(HeifLocationOffsetOrigin method, long baseOffset, uint dataReferenceIndex, long offset, long length, uint extentIndex)
{
/// <summary>
/// Gets the file offset of this location.
/// Gets the origin of the offsets in this location.
/// </summary>
public HeifLocationOffsetOrigin Origin { get; } = method;

/// <summary>
/// Gets the base offset of this location.
/// </summary>
public long BaseOffset { get; } = baseOffset;

/// <summary>
/// Gets the data reference index of this location.
/// </summary>
public uint DataReferenceInxdex { get; } = dataReferenceIndex;

/// <summary>
/// Gets the offset of this location.
/// </summary>
public long Offset { get; } = offset;

/// <summary>
/// Gets the length of this location.
/// </summary>
public long Length { get; } = length;

/// <summary>
/// Gets the extent index of this location.
/// </summary>
public uint ExtentInxdex { get; } = extentIndex;

/// <summary>
/// Gets the stream position of this location.
/// </summary>
/// <param name="positionOfMediaData">Stream position of the MediaData box.</param>
/// <param name="positionOfItem">Stream position of the previous box.</param>
public long GetStreamPosition(long positionOfMediaData, long positionOfItem) => this.Origin switch
{
HeifLocationOffsetOrigin.FileOffset => this.BaseOffset + this.Offset,
HeifLocationOffsetOrigin.ItemDataOffset => positionOfMediaData + this.BaseOffset + this.Offset,
_ => positionOfItem + this.BaseOffset + this.Offset
};
}
11 changes: 11 additions & 0 deletions src/ImageSharp/Formats/Heif/HeifLocationOffsetOrigin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Heif;

internal enum HeifLocationOffsetOrigin
{
FileOffset = 0,
ItemDataOffset = 1,
ItemOffset = 2
}

0 comments on commit a48e271

Please sign in to comment.