diff --git a/src/ImageSharp/Common/Helpers/DisposableDictionary.cs b/src/ImageSharp/Common/Helpers/DisposableDictionary.cs
new file mode 100644
index 0000000000..3b078f25ca
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/DisposableDictionary.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Common.Helpers;
+
+///
+/// Dictionary of objects, which is itself .
+///
+/// The type of the key.
+/// Tye type of value, needs to implement .
+public sealed class DisposableDictionary : Dictionary, IDisposable
+ where TKey : notnull
+ where TValue : IDisposable
+{
+ private bool disposedValue;
+
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!this.disposedValue)
+ {
+ if (disposing)
+ {
+ foreach (KeyValuePair pair in this)
+ {
+ pair.Value?.Dispose();
+ }
+ }
+
+ this.Clear();
+ this.disposedValue = true;
+ }
+ }
+
+}
diff --git a/src/ImageSharp/Common/Helpers/DisposableList.cs b/src/ImageSharp/Common/Helpers/DisposableList.cs
new file mode 100644
index 0000000000..8d0512b69c
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/DisposableList.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Common.Helpers;
+
+///
+/// List of objects, which is itself .
+///
+/// Tye type of value, needs to implement .
+public sealed class DisposableList : List, IDisposable
+ where TValue : IDisposable
+{
+ private bool disposedValue;
+
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!this.disposedValue)
+ {
+ if (disposing)
+ {
+ foreach (TValue item in this)
+ {
+ item?.Dispose();
+ }
+ }
+
+ this.Clear();
+ this.disposedValue = true;
+ }
+ }
+
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs b/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs
index 3b3663522e..767743430a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs
@@ -4,6 +4,7 @@
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@@ -28,7 +29,8 @@ internal class Av1Decoder : IAv1TileReader
public Av1FrameBuffer? FrameBuffer { get; private set; }
- public void Decode(Span buffer)
+ public Image Decode(Span buffer)
+ where TPixel : unmanaged, IPixel
{
Av1BitStreamReader reader = new(buffer);
this.obuReader.ReadAll(ref reader, buffer.Length, () => this, false);
@@ -40,6 +42,9 @@ internal class Av1Decoder : IAv1TileReader
this.FrameBuffer = new(this.configuration, this.SequenceHeader, this.SequenceHeader.ColorConfig.GetColorFormat(), false);
this.frameDecoder = new(this.SequenceHeader, this.FrameHeader, this.FrameInfo, this.FrameBuffer);
this.frameDecoder.DecodeFrame();
+
+ // TODO: Implement returning proper pixels.
+ return new Image(1, 1);
}
public void ReadTile(Span tileData, int tileNum)
diff --git a/src/ImageSharp/Formats/Heif/Av1HeifItemDecoder.cs b/src/ImageSharp/Formats/Heif/Av1HeifItemDecoder.cs
new file mode 100644
index 0000000000..3b96a3b46c
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1HeifItemDecoder.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Heif.Av1;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Heif;
+
+///
+/// Decoder for a single into a AVIF image.
+///
+internal class Av1HeifItemDecoder : IHeifItemDecoder
+ where TPixel : unmanaged, IPixel
+{
+ ///
+ /// Gets the item type this decoder decodes, which is .
+ ///
+ public Heif4CharCode Type => Heif4CharCode.Av01;
+
+ ///
+ /// Gets the compression method this doceder uses, which is .
+ ///
+ public HeifCompressionMethod CompressionMethod => HeifCompressionMethod.Av1;
+
+ ///
+ /// Decode the specified item as AVIF.
+ ///
+ public Image DecodeItemData(Configuration configuration, HeifItem item, Span data)
+ {
+ Av1Decoder decoder = new(configuration);
+ return decoder.Decode(data);
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs b/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs
new file mode 100644
index 0000000000..cb874d5bc3
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/HeifCompressionFactory.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Heif;
+
+///
+/// Factory for item decoders inside the HEIF container format.
+///
+internal class HeifCompressionFactory
+{
+ ///
+ /// Get a decoder implementation.
+ ///
+ public static IHeifItemDecoder? GetDecoder(Heif4CharCode type)
+ where TPixel : unmanaged, IPixel => type switch
+ {
+ Heif4CharCode.Jpeg => new JpegHeifItemDecoder(),
+ Heif4CharCode.Av01 => new Av1HeifItemDecoder(),
+ _ => null
+ };
+}
diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
index 4481aadf7b..6da3d100d9 100644
--- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
@@ -5,6 +5,7 @@ using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Text;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -573,7 +574,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
long extentOffset = ReadUIntVariable(boxBuffer, offsetSize, ref bytesRead);
long extentLength = ReadUIntVariable(boxBuffer, lengthSize, ref bytesRead);
- HeifLocation loc = new(constructionMethod, baseOffset, dataReferenceIndex, extentOffset, extentLength, extentIndex);
+ HeifLocation loc = new(constructionMethod, baseOffset, extentOffset, extentLength);
item?.DataLocations.Add(loc);
}
}
@@ -629,41 +630,117 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
return result;
}
- private Image ParseMediaData(BufferedReadStream stream, long boxLength)
+ private Image ParseMediaData(Stream stream, long boxLength)
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)
+ IComparer comparer = new HeifLocationComparer(stream.Position, stream.Position);
+ SortedList locations = new(comparer);
+ foreach (HeifItem item in this.items)
{
- throw new NotImplementedException("No thumbnail found");
+ HeifLocation loc = item.DataLocations[0];
+ if (loc.Length != 0)
+ {
+ locations[loc] = item;
+ }
+ }
+
+ using DisposableDictionary> buffers = [];
+ foreach (HeifLocation loc in locations.Keys)
+ {
+ HeifItem item = locations[loc];
+ long streamPosition = loc.GetStreamPosition(stream.Position, stream.Position);
+ long dataLength = loc.Length;
+ stream.Skip((int)(streamPosition - stream.Position));
+ EnsureBoxBoundary(dataLength, stream);
+ buffers.Add(item.Id, this.ReadIntoBuffer(stream, dataLength));
}
- HeifItem? thumbItem = this.FindItemById(thumbLink.SourceId);
- if (thumbItem == null || thumbItem.Type != Heif4CharCode.Jpeg)
+ HeifItem? rootItem = this.FindItemById(this.primaryItem);
+ if (rootItem == null)
{
- throw new NotImplementedException("No HVC decoding implemented yet");
+ throw new ImageFormatException("No primary HEIF item found.");
}
- 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 thumbMemory = this.ReadIntoBuffer(stream, thumbLength);
- Span thumbSpan = thumbMemory.GetSpan();
+ if (rootItem.Type == Heif4CharCode.Grid)
+ {
+ Image gridImage = this.DecodeGrid(rootItem, buffers);
+ if (gridImage.Width > 1 && gridImage.Height > 1)
+ {
+ return gridImage;
+ }
+
+ gridImage.Dispose();
+ }
+ IHeifItemDecoder? itemDecoder = HeifCompressionFactory.GetDecoder(rootItem.Type);
+ HeifItem itemToDecode = rootItem;
+ if (itemDecoder == null)
+ {
+ // 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)
+ {
+ throw new NotImplementedException("No thumbnail defined");
+ }
+
+ itemDecoder = HeifCompressionFactory.GetDecoder(thumbItem.Type);
+ if (itemDecoder == null)
+ {
+ throw new NotImplementedException($"Don't know how to decode a thumbnail of type: {thumbItem.Type}.");
+ }
+
+ itemToDecode = thumbItem;
+ }
+
+ HeifMetadata meta = this.metadata.GetHeifMetadata();
+ meta.CompressionMethod = itemDecoder.CompressionMethod;
+
+ IMemoryOwner itemMemory = buffers[itemToDecode.Id];
+ return itemDecoder.DecodeItemData(this.configuration, itemToDecode, itemMemory.GetSpan());
+ }
+
+ private Image DecodeGrid(HeifItem gridItem, IDictionary> buffers)
+ where TPixel : unmanaged, IPixel
+ {
+ List linked = this.itemLinks.First(l => l.SourceId == gridItem.Id).DestinationIds;
HeifMetadata meta = this.metadata.GetHeifMetadata();
- meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg;
+ using DisposableList> gridTiles = [];
+ foreach (uint id in linked)
+ {
+ HeifItem? item = this.FindItemById(id);
+ if (item != null)
+ {
+ IHeifItemDecoder? decoder = HeifCompressionFactory.GetDecoder(item.Type);
+ if (decoder != null)
+ {
+ meta.CompressionMethod = decoder.CompressionMethod;
+ IMemoryOwner itemMemory = buffers[item.Id];
+ gridTiles.Add(decoder.DecodeItemData(this.configuration, item, itemMemory.GetSpan()));
+ }
+ }
+ }
+
+ if (gridTiles.Count == 0)
+ {
+ return new Image(1, 1);
+ }
- return Image.Load(thumbSpan);
+ // return CombineImageTiles(gridTiles);
+ return new Image(1, 1);
}
- private static void SkipBox(BufferedReadStream stream, long boxLength)
+ private static void SkipBox(Stream stream, long boxLength)
=> stream.Skip((int)boxLength);
- private IMemoryOwner ReadIntoBuffer(BufferedReadStream stream, long length)
+ private IMemoryOwner ReadIntoBuffer(Stream stream, long length)
{
IMemoryOwner buffer = this.configuration.MemoryAllocator.Allocate((int)length);
int bytesRead = stream.Read(buffer.GetSpan());
diff --git a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
index 519aa3200b..8cee2f02c5 100644
--- a/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
@@ -66,7 +66,7 @@ internal sealed class HeifEncoderCore
where TPixel : unmanaged, IPixel
{
HeifItem primaryItem = new(Heif4CharCode.Jpeg, 1u);
- primaryItem.DataLocations.Add(new HeifLocation(HeifLocationOffsetOrigin.ItemDataOffset, 0L, 0U, 0L, pixels.LongLength, 0U));
+ primaryItem.DataLocations.Add(new HeifLocation(HeifLocationOffsetOrigin.ItemDataOffset, 0L, 0L, pixels.LongLength));
primaryItem.BitsPerPixel = 24;
primaryItem.ChannelCount = 3;
primaryItem.SetExtent(image.Size);
diff --git a/src/ImageSharp/Formats/Heif/HeifLocation.cs b/src/ImageSharp/Formats/Heif/HeifLocation.cs
index afbc3ad035..1f2f35787f 100644
--- a/src/ImageSharp/Formats/Heif/HeifLocation.cs
+++ b/src/ImageSharp/Formats/Heif/HeifLocation.cs
@@ -6,23 +6,18 @@ namespace SixLabors.ImageSharp.Formats.Heif;
///
/// Location within the file of an .
///
-internal class HeifLocation(HeifLocationOffsetOrigin method, long baseOffset, uint dataReferenceIndex, long offset, long length, uint extentIndex)
+internal class HeifLocation(HeifLocationOffsetOrigin origin, long baseOffset, long offset, long length)
{
///
/// Gets the origin of the offsets in this location.
///
- public HeifLocationOffsetOrigin Origin { get; } = method;
+ public HeifLocationOffsetOrigin Origin { get; } = origin;
///
/// Gets the base offset of this location.
///
public long BaseOffset { get; } = baseOffset;
- ///
- /// Gets the data reference index of this location.
- ///
- public uint DataReferenceInxdex { get; } = dataReferenceIndex;
-
///
/// Gets the offset of this location.
///
@@ -33,11 +28,6 @@ internal class HeifLocation(HeifLocationOffsetOrigin method, long baseOffset, ui
///
public long Length { get; } = length;
- ///
- /// Gets the extent index of this location.
- ///
- public uint ExtentInxdex { get; } = extentIndex;
-
///
/// Gets the stream position of this location.
///
@@ -49,4 +39,21 @@ internal class HeifLocation(HeifLocationOffsetOrigin method, long baseOffset, ui
HeifLocationOffsetOrigin.ItemDataOffset => positionOfMediaData + this.BaseOffset + this.Offset,
_ => positionOfItem + this.BaseOffset + this.Offset
};
+
+ public override int GetHashCode() => HashCode.Combine(this.Origin, this.Offset, this.Length, this.BaseOffset);
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is not HeifLocation other)
+ {
+ return false;
+ }
+
+ if (this.Origin != other.Origin || this.Length != other.Length)
+ {
+ return false;
+ }
+
+ return this.Offset == other.Offset && this.BaseOffset == other.BaseOffset;
+ }
}
diff --git a/src/ImageSharp/Formats/Heif/HeifLocationComparer.cs b/src/ImageSharp/Formats/Heif/HeifLocationComparer.cs
new file mode 100644
index 0000000000..e249114ced
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/HeifLocationComparer.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif;
+
+internal class HeifLocationComparer : IComparer
+{
+ private readonly long positionOfMediaData;
+ private readonly long positionOfItem;
+
+ public HeifLocationComparer(long positionOfMediaData, long positionOfItem)
+ {
+ this.positionOfMediaData = positionOfMediaData;
+ this.positionOfItem = positionOfItem;
+ }
+
+ public int Compare(HeifLocation? x, HeifLocation? y)
+ {
+ if (x == null)
+ {
+ if (y == null)
+ {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (y == null)
+ {
+ return -1;
+ }
+
+ long xPos = x.GetStreamPosition(this.positionOfMediaData, this.positionOfItem);
+ long yPos = y.GetStreamPosition(this.positionOfMediaData, this.positionOfItem);
+
+ return Math.Sign(xPos - yPos);
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/IHeifItemDecoder.cs b/src/ImageSharp/Formats/Heif/IHeifItemDecoder.cs
new file mode 100644
index 0000000000..b5f66afe4a
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/IHeifItemDecoder.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Heif;
+
+///
+/// Decoder for a single .
+///
+/// The pixel type to use.
+internal interface IHeifItemDecoder
+ where TPixel : unmanaged, IPixel
+{
+ ///
+ /// Gets the type of item this decoder can decode.
+ ///
+ public Heif4CharCode Type { get; }
+
+ ///
+ /// Gets the tis decoder uses.
+ ///
+ public HeifCompressionMethod CompressionMethod { get; }
+
+ ///
+ /// Decode the specified item, given encoded data.
+ ///
+ /// The configuration to used.
+ /// The item to decode.
+ /// The encoded data.
+ /// The decoded image.
+ public Image DecodeItemData(Configuration configuration, HeifItem item, Span data);
+}
diff --git a/src/ImageSharp/Formats/Heif/JpegHeifItemDecoder.cs b/src/ImageSharp/Formats/Heif/JpegHeifItemDecoder.cs
new file mode 100644
index 0000000000..b095f6eb14
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/JpegHeifItemDecoder.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Heif;
+
+///
+/// Decoder for a single into a JPEG image.
+///
+internal class JpegHeifItemDecoder : IHeifItemDecoder
+ where TPixel : unmanaged, IPixel
+{
+ ///
+ /// Gets the item type this decoder decodes, which is .
+ ///
+ public Heif4CharCode Type => Heif4CharCode.Jpeg;
+
+ ///
+ /// Gets the compression method this doceder uses, which is .
+ ///
+ public HeifCompressionMethod CompressionMethod => HeifCompressionMethod.LegacyJpeg;
+
+ ///
+ /// Decode the specified item as JPEG.
+ ///
+ public Image DecodeItemData(Configuration configuration, HeifItem item, Span data)
+ {
+ Image image = Image.Load(data);
+ return image;
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/HeifLocationTests.cs b/tests/ImageSharp.Tests/Formats/Heif/HeifLocationTests.cs
new file mode 100644
index 0000000000..b1877c7b5a
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Heif/HeifLocationTests.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Heif;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Heif;
+
+[Trait("Format", "Heif")]
+public class HeifLocationTests
+{
+ [Fact]
+ public void CheckSameLocationFromDifferentOrigin()
+ {
+ // Arrange
+ const int dataPosition = 50;
+ const int itemPosition = 100;
+ HeifLocation fromItem = new(HeifLocationOffsetOrigin.ItemOffset, 0, 10, 42);
+ HeifLocation fromFile = new(HeifLocationOffsetOrigin.FileOffset, 0, 110, 42);
+ HeifLocation fromData = new(HeifLocationOffsetOrigin.ItemDataOffset, 0, 60, 42);
+
+ // Act
+ long itemActual = fromItem.GetStreamPosition(dataPosition, itemPosition);
+ long fileActual = fromFile.GetStreamPosition(dataPosition, itemPosition);
+ long dataActual = fromData.GetStreamPosition(dataPosition, itemPosition);
+
+ // Assert
+ Assert.Equal(110, itemActual);
+ Assert.Equal(110, fileActual);
+ Assert.Equal(110, dataActual);
+ }
+
+ [Fact]
+ public void CheckSameLocationFromDifferentOriginWithBaseOffset()
+ {
+ // Arrange
+ const int dataPosition = 50;
+ const int itemPosition = 100;
+ HeifLocation fromItem = new(HeifLocationOffsetOrigin.ItemOffset, 40, 10, 42);
+ HeifLocation fromFile = new(HeifLocationOffsetOrigin.FileOffset, 40, 110, 42);
+ HeifLocation fromData = new(HeifLocationOffsetOrigin.ItemDataOffset, 40, 60, 42);
+
+ // Act
+ long itemActual = fromItem.GetStreamPosition(dataPosition, itemPosition);
+ long fileActual = fromFile.GetStreamPosition(dataPosition, itemPosition);
+ long dataActual = fromData.GetStreamPosition(dataPosition, itemPosition);
+
+ // Assert
+ Assert.Equal(150, itemActual);
+ Assert.Equal(150, fileActual);
+ Assert.Equal(150, dataActual);
+ }
+
+ [Fact]
+ public void CheckComparerOnSameLocation()
+ {
+ // Arrange
+ const int dataPosition = 50;
+ const int itemPosition = 100;
+ HeifLocation fromItem = new(HeifLocationOffsetOrigin.ItemOffset, 0, 50, 42);
+ HeifLocation fromFile = new(HeifLocationOffsetOrigin.FileOffset, 30, 120, 42);
+ HeifLocation fromData = new(HeifLocationOffsetOrigin.ItemDataOffset, 40, 60, 42);
+ HeifLocationComparer comparer = new(dataPosition, itemPosition);
+
+ // Act
+ int item2Data = comparer.Compare(fromItem, fromData);
+ int item2File = comparer.Compare(fromItem, fromFile);
+ int file2Data = comparer.Compare(fromFile, fromData);
+ int data2File = comparer.Compare(fromData, fromFile);
+
+ // Assert
+ Assert.Equal(0, item2Data);
+ Assert.Equal(0, item2File);
+ Assert.Equal(0, file2Data);
+ Assert.Equal(0, data2File);
+ }
+
+ [Fact]
+ public void CheckComparerOnLowerLocation()
+ {
+ // Arrange
+ const int dataPosition = 50;
+ const int itemPosition = 100;
+ HeifLocation fromItem = new(HeifLocationOffsetOrigin.ItemOffset, 0, 50, 42);
+ HeifLocation fromFile = new(HeifLocationOffsetOrigin.FileOffset, 30, 150, 42);
+ HeifLocation fromData = new(HeifLocationOffsetOrigin.ItemDataOffset, 40, 80, 42);
+ HeifLocationComparer comparer = new(dataPosition, itemPosition);
+
+ // Act
+ int item2Data = comparer.Compare(fromItem, fromData);
+ int item2File = comparer.Compare(fromItem, fromFile);
+
+ // Assert
+ Assert.Equal(-1, item2Data);
+ Assert.Equal(-1, item2File);
+ }
+
+ [Fact]
+ public void CheckComparerOnHigherLocation()
+ {
+ // Arrange
+ const int dataPosition = 50;
+ const int itemPosition = 100;
+ HeifLocation fromItem = new(HeifLocationOffsetOrigin.ItemOffset, 10, 50, 42);
+ HeifLocation fromFile = new(HeifLocationOffsetOrigin.FileOffset, 30, 120, 42);
+ HeifLocation fromData = new(HeifLocationOffsetOrigin.ItemDataOffset, 40, 60, 42);
+ HeifLocationComparer comparer = new(dataPosition, itemPosition);
+
+ // Act
+ int item2Data = comparer.Compare(fromItem, fromData);
+ int item2File = comparer.Compare(fromItem, fromFile);
+
+ // Assert
+ Assert.Equal(1, item2Data);
+ Assert.Equal(1, item2File);
+ }
+}