Browse Source

Separate grid item decoder

pull/2633/head
Ynse Hoornenborg 1 year ago
parent
commit
0ee06fd857
  1. 12
      src/ImageSharp/Common/Helpers/DisposableDictionary.cs
  2. 12
      src/ImageSharp/Common/Helpers/DisposableList.cs
  3. 70
      src/ImageSharp/Formats/Heif/GridHeifItemDecoder.cs
  4. 78
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
  5. 2
      src/ImageSharp/Formats/Heif/HeifItem.cs

12
src/ImageSharp/Common/Helpers/DisposableDictionary.cs

@ -14,6 +14,18 @@ public sealed class DisposableDictionary<TKey, TValue> : Dictionary<TKey, TValue
{
private bool disposedValue;
/// <inheritdoc />
public DisposableDictionary()
: base()
{
}
/// <inheritdoc />
public DisposableDictionary(int capacity)
: base(capacity)
{
}
/// <inheritdoc />
public void Dispose()
{

12
src/ImageSharp/Common/Helpers/DisposableList.cs

@ -12,6 +12,18 @@ public sealed class DisposableList<TValue> : List<TValue>, IDisposable
{
private bool disposedValue;
/// <inheritdoc />
public DisposableList()
: base()
{
}
/// <inheritdoc />
public DisposableList(int capacity)
: base(capacity)
{
}
/// <inheritdoc />
public void Dispose()
{

70
src/ImageSharp/Formats/Heif/GridHeifItemDecoder.cs

@ -0,0 +1,70 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Decoder for a grid of several <see cref="HeifItem"/> into a single image.
/// </summary>
internal class GridHeifItemDecoder<TPixel> : IHeifItemDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly Configuration configuration;
private readonly IList<HeifItem> items;
private readonly IList<HeifItemLink> itemLinks;
private readonly IDictionary<uint, IMemoryOwner<byte>> buffers;
public GridHeifItemDecoder(Configuration configuration, IList<HeifItem> items, IList<HeifItemLink> itemLinks, IDictionary<uint, IMemoryOwner<byte>> buffers)
{
this.configuration = configuration;
this.items = items;
this.itemLinks = itemLinks;
this.buffers = buffers;
}
/// <summary>
/// Gets the item type this decoder decodes, which is <see cref="Heif4CharCode.Grid"/>.
/// </summary>
public Heif4CharCode Type => Heif4CharCode.Grid;
/// <summary>
/// Gets the compression method this doceder uses.
/// </summary>
public HeifCompressionMethod CompressionMethod { get; private set; }
/// <summary>
/// Decode the specified item as single image.
/// </summary>
public Image<TPixel> DecodeItemData(Configuration configuration, HeifItem gridItem, Span<byte> data)
{
List<uint> linked = this.itemLinks.First(l => l.SourceId == gridItem.Id).DestinationIds;
using DisposableList<Image<TPixel>> gridTiles = new(linked.Count);
foreach (uint id in linked)
{
HeifItem? item = this.items.FirstOrDefault(item => item.Id == id);
if (item != null)
{
IHeifItemDecoder<TPixel>? decoder = HeifCompressionFactory.GetDecoder<TPixel>(item.Type);
if (decoder != null)
{
this.CompressionMethod = decoder.CompressionMethod;
IMemoryOwner<byte> itemMemory = this.buffers[item.Id];
gridTiles.Add(decoder.DecodeItemData(this.configuration, item, itemMemory.GetSpan()));
}
}
}
if (gridTiles.Count == 0)
{
return new Image<TPixel>(1, 1);
}
// TODO: Combine grid tiles into a single image.
return new Image<TPixel>(1, 1);
}
}

78
src/ImageSharp/Formats/Heif/HeifDecoderCore.cs

@ -58,6 +58,8 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
throw new ImageFormatException("Not an HEIF image.");
}
this.items.Clear();
this.itemLinks.Clear();
Image<TPixel>? image = null;
while (stream.Position < stream.Length)
{
@ -645,7 +647,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
}
}
using DisposableDictionary<uint, IMemoryOwner<byte>> buffers = [];
using DisposableDictionary<uint, IMemoryOwner<byte>> buffers = new(locations.Count);
foreach (HeifLocation loc in locations.Keys)
{
HeifItem item = locations[loc];
@ -659,44 +661,38 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
HeifItem? rootItem = this.FindItemById(this.primaryItem);
if (rootItem == null)
{
throw new ImageFormatException("No primary HEIF item found.");
throw new ImageFormatException("No primary HEIF item defined.");
}
IHeifItemDecoder<TPixel>? itemDecoder;
if (rootItem.Type == Heif4CharCode.Grid)
{
Image<TPixel> gridImage = this.DecodeGrid<TPixel>(rootItem, buffers);
if (gridImage.Width > 1 && gridImage.Height > 1)
{
return gridImage;
}
gridImage.Dispose();
itemDecoder = new GridHeifItemDecoder<TPixel>(this.configuration, this.items, this.itemLinks, buffers);
}
IHeifItemDecoder<TPixel>? itemDecoder = HeifCompressionFactory.GetDecoder<TPixel>(rootItem.Type);
itemDecoder = HeifCompressionFactory.GetDecoder<TPixel>(rootItem.Type);
HeifItem itemToDecode = rootItem;
if (itemDecoder == null)
{
// FIXME: No specific decoding yet, so parse only a JPEG thumbnail.
// Unable to decode the primary image, decode the thumbnail instead.
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<TPixel>(thumbItem.Type);
if (itemDecoder == null)
if (thumbLink != null)
{
throw new NotImplementedException($"Don't know how to decode a thumbnail of type: {thumbItem.Type}.");
HeifItem? thumbItem = this.FindItemById(thumbLink.SourceId);
if (thumbItem != null)
{
itemDecoder = HeifCompressionFactory.GetDecoder<TPixel>(thumbItem.Type);
if (itemDecoder != null)
{
itemToDecode = thumbItem;
}
}
}
}
itemToDecode = thumbItem;
if (itemDecoder == null)
{
throw new ImageFormatException("No decodable item found inside this HEIF container.");
}
HeifMetadata meta = this.metadata.GetHeifMetadata();
@ -706,36 +702,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
return itemDecoder.DecodeItemData(this.configuration, itemToDecode, itemMemory.GetSpan());
}
private Image<TPixel> DecodeGrid<TPixel>(HeifItem gridItem, DisposableDictionary<uint, IMemoryOwner<byte>> buffers)
where TPixel : unmanaged, IPixel<TPixel>
{
List<uint> linked = this.itemLinks.First(l => l.SourceId == gridItem.Id).DestinationIds;
HeifMetadata meta = this.metadata.GetHeifMetadata();
using DisposableList<Image<TPixel>> gridTiles = [];
foreach (uint id in linked)
{
HeifItem? item = this.FindItemById(id);
if (item != null)
{
IHeifItemDecoder<TPixel>? decoder = HeifCompressionFactory.GetDecoder<TPixel>(item.Type);
if (decoder != null)
{
meta.CompressionMethod = decoder.CompressionMethod;
IMemoryOwner<byte> itemMemory = buffers[item.Id];
gridTiles.Add(decoder.DecodeItemData(this.configuration, item, itemMemory.GetSpan()));
}
}
}
if (gridTiles.Count == 0)
{
return new Image<TPixel>(1, 1);
}
// return CombineImageTiles(gridTiles);
return new Image<TPixel>(1, 1);
}
private static void SkipBox(Stream stream, long boxLength)
=> stream.Skip((int)boxLength);

2
src/ImageSharp/Formats/Heif/HeifItem.cs

@ -91,4 +91,6 @@ internal class HeifItem(Heif4CharCode type, uint id)
this.GridCellExtent = extent;
}
}
public override string ToString() => $"{this.Type}:{this.Id}";
}

Loading…
Cancel
Save