mirror of https://github.com/SixLabors/ImageSharp
12 changed files with 480 additions and 33 deletions
@ -0,0 +1,42 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Common.Helpers; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Dictionary of <see cref="IDisposable"/> objects, which is itself <see cref="IDisposable"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
|
/// <typeparam name="TValue">Tye type of value, needs to implement <see cref="IDisposable"/>.</typeparam>
|
||||
|
public sealed class DisposableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IDisposable |
||||
|
where TKey : notnull |
||||
|
where TValue : IDisposable |
||||
|
{ |
||||
|
private bool disposedValue; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
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<TKey, TValue> pair in this) |
||||
|
{ |
||||
|
pair.Value?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.Clear(); |
||||
|
this.disposedValue = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Common.Helpers; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// List of <see cref="IDisposable"/> objects, which is itself <see cref="IDisposable"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TValue">Tye type of value, needs to implement <see cref="IDisposable"/>.</typeparam>
|
||||
|
public sealed class DisposableList<TValue> : List<TValue>, IDisposable |
||||
|
where TValue : IDisposable |
||||
|
{ |
||||
|
private bool disposedValue; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decoder for a single <see cref="HeifItem"/> into a AVIF image.
|
||||
|
/// </summary>
|
||||
|
internal class Av1HeifItemDecoder<TPixel> : IHeifItemDecoder<TPixel> |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the item type this decoder decodes, which is <see cref="Heif4CharCode.Av01"/>.
|
||||
|
/// </summary>
|
||||
|
public Heif4CharCode Type => Heif4CharCode.Av01; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the compression method this doceder uses, which is <see cref="HeifCompressionMethod.Av1"/>.
|
||||
|
/// </summary>
|
||||
|
public HeifCompressionMethod CompressionMethod => HeifCompressionMethod.Av1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decode the specified item as AVIF.
|
||||
|
/// </summary>
|
||||
|
public Image<TPixel> DecodeItemData(Configuration configuration, HeifItem item, Span<byte> data) |
||||
|
{ |
||||
|
Av1Decoder decoder = new(configuration); |
||||
|
return decoder.Decode<TPixel>(data); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Factory for item decoders inside the HEIF container format.
|
||||
|
/// </summary>
|
||||
|
internal class HeifCompressionFactory |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Get a decoder implementation.
|
||||
|
/// </summary>
|
||||
|
public static IHeifItemDecoder<TPixel>? GetDecoder<TPixel>(Heif4CharCode type) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> => type switch |
||||
|
{ |
||||
|
Heif4CharCode.Jpeg => new JpegHeifItemDecoder<TPixel>(), |
||||
|
Heif4CharCode.Av01 => new Av1HeifItemDecoder<TPixel>(), |
||||
|
_ => null |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif; |
||||
|
|
||||
|
internal class HeifLocationComparer : IComparer<HeifLocation> |
||||
|
{ |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decoder for a single <see cref="HeifItem"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel type to use.</typeparam>
|
||||
|
internal interface IHeifItemDecoder<TPixel> |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the type of item this decoder can decode.
|
||||
|
/// </summary>
|
||||
|
public Heif4CharCode Type { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="HeifCompressionMethod"/> tis decoder uses.
|
||||
|
/// </summary>
|
||||
|
public HeifCompressionMethod CompressionMethod { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decode the specified item, given encoded data.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The configuration to used.</param>
|
||||
|
/// <param name="item">The item to decode.</param>
|
||||
|
/// <param name="data">The encoded data.</param>
|
||||
|
/// <returns>The decoded image.</returns>
|
||||
|
public Image<TPixel> DecodeItemData(Configuration configuration, HeifItem item, Span<byte> data); |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decoder for a single <see cref="HeifItem"/> into a JPEG image.
|
||||
|
/// </summary>
|
||||
|
internal class JpegHeifItemDecoder<TPixel> : IHeifItemDecoder<TPixel> |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the item type this decoder decodes, which is <see cref="Heif4CharCode.Jpeg"/>.
|
||||
|
/// </summary>
|
||||
|
public Heif4CharCode Type => Heif4CharCode.Jpeg; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the compression method this doceder uses, which is <see cref="HeifCompressionMethod.LegacyJpeg"/>.
|
||||
|
/// </summary>
|
||||
|
public HeifCompressionMethod CompressionMethod => HeifCompressionMethod.LegacyJpeg; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decode the specified item as JPEG.
|
||||
|
/// </summary>
|
||||
|
public Image<TPixel> DecodeItemData(Configuration configuration, HeifItem item, Span<byte> data) |
||||
|
{ |
||||
|
Image<TPixel> image = Image.Load<TPixel>(data); |
||||
|
return image; |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue