Browse Source

Rename namespace and classes to Heif

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
4645e40725
  1. 4
      .gitattributes
  2. 6
      src/ImageSharp/Configuration.cs
  3. 18
      src/ImageSharp/Formats/Heic/HeicConfigurationModule.cs
  4. 6
      src/ImageSharp/Formats/Heif/Heif4CharCode.cs
  5. 6
      src/ImageSharp/Formats/Heif/Heif4CharCode.tt
  6. 4
      src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs
  7. 18
      src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs
  8. 8
      src/ImageSharp/Formats/Heif/HeifConstants.cs
  9. 14
      src/ImageSharp/Formats/Heif/HeifDecoder.cs
  10. 201
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
  11. 10
      src/ImageSharp/Formats/Heif/HeifEncoder.cs
  12. 92
      src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
  13. 14
      src/ImageSharp/Formats/Heif/HeifFormat.cs
  14. 12
      src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs
  15. 10
      src/ImageSharp/Formats/Heif/HeifItem.cs
  16. 8
      src/ImageSharp/Formats/Heif/HeifItemLink.cs
  17. 6
      src/ImageSharp/Formats/Heif/HeifLocation.cs
  18. 18
      src/ImageSharp/Formats/Heif/HeifMetadata.cs
  19. 4
      src/ImageSharp/Formats/Heif/IHeicEncoderOptions.cs
  20. 8
      src/ImageSharp/Formats/Heif/MetadataExtensions.cs
  21. 0
      src/ImageSharp/Formats/Heif/Readme.md
  22. 8
      src/ImageSharp/ImageSharp.csproj
  23. 28
      tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs
  24. 18
      tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs
  25. 6
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  26. 16
      tests/ImageSharp.Tests/TestImages.cs
  27. 4
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  28. 0
      tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png

4
.gitattributes

@ -136,7 +136,3 @@
*.ico filter=lfs diff=lfs merge=lfs -text
*.cur filter=lfs diff=lfs merge=lfs -text
*.ani filter=lfs diff=lfs merge=lfs -text
*.heic filter=lfs diff=lfs merge=lfs -text
*.heif filter=lfs diff=lfs merge=lfs -text
*.hif filter=lfs diff=lfs merge=lfs -text
*.avif filter=lfs diff=lfs merge=lfs -text

6
src/ImageSharp/Configuration.cs

@ -5,7 +5,7 @@ using System.Collections.Concurrent;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
@ -212,7 +212,7 @@ public sealed class Configuration
/// <see cref="TiffConfigurationModule"/>.
/// <see cref="WebpConfigurationModule"/>.
/// <see cref="QoiConfigurationModule"/>.
/// <see cref="HeicConfigurationModule"/>.
/// <see cref="HeifConfigurationModule"/>.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance() => new(
@ -225,5 +225,5 @@ public sealed class Configuration
new TiffConfigurationModule(),
new WebpConfigurationModule(),
new QoiConfigurationModule(),
new HeicConfigurationModule());
new HeifConfigurationModule());
}

18
src/ImageSharp/Formats/Heic/HeicConfigurationModule.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the HEIC format.
/// </summary>
public sealed class HeicConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(HeicFormat.Instance, new HeicEncoder());
configuration.ImageFormatsManager.SetDecoder(HeicFormat.Instance, HeicDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new HeicImageFormatDetector());
}
}

6
src/ImageSharp/Formats/Heic/Heic4CharCode.cs → src/ImageSharp/Formats/Heif/Heif4CharCode.cs

@ -5,13 +5,13 @@
using System.CodeDom.Compiler;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Supported 4 character codes for use in HEIC images.
/// Supported 4 character codes for use in HEIF images.
/// </summary>
[GeneratedCode("T4", "")]
public enum Heic4CharCode : uint
public enum Heif4CharCode : uint
{
/// <summary>
/// File Type.

6
src/ImageSharp/Formats/Heic/Heic4CharCode.tt → src/ImageSharp/Formats/Heif/Heif4CharCode.tt

@ -59,13 +59,13 @@
using System.CodeDom.Compiler;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Supported 4 character codes for use in HEIC images.
/// Supported 4 character codes for use in HEIF images.
/// </summary>
[GeneratedCode("T4", "")]
public enum Heic4CharCode : uint
public enum Heif4CharCode : uint
{
<#
for (int i = 0; i < codes.Length; i += 2)

4
src/ImageSharp/Formats/Heic/HeicCompressionMethod.cs → src/ImageSharp/Formats/Heif/HeifCompressionMethod.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Compression algorithms possible inside an HEIF (High Efficiency File Format) based file.
/// </summary>
public enum HeicCompressionMethod
public enum HeifCompressionMethod
{
/// <summary>
/// High Efficiency Video Coding

18
src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the HEIF format.
/// </summary>
public sealed class HeifConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(HeifFormat.Instance, new HeifEncoder());
configuration.ImageFormatsManager.SetDecoder(HeifFormat.Instance, HeifDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new HeifImageFormatDetector());
}
}

8
src/ImageSharp/Formats/Heic/HeicConstants.cs → src/ImageSharp/Formats/Heif/HeifConstants.cs

@ -1,14 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Contains HEIC (and H.265) constant values defined in the specification.
/// Contains HEIF constant values defined in the specification.
/// </summary>
internal static class HeicConstants
internal static class HeifConstants
{
public const Heic4CharCode HeicBrand = Heic4CharCode.heic;
public const Heif4CharCode HeicBrand = Heif4CharCode.heic;
/// <summary>
/// The list of mimetypes that equate to a HEIC.

14
src/ImageSharp/Formats/Heic/HeicDecoder.cs → src/ImageSharp/Formats/Heif/HeifDecoder.cs

@ -3,21 +3,21 @@
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Image decoder for reading HEIC images from a stream.
/// Image decoder for reading HEIF images from a stream.
/// </summary>
public sealed class HeicDecoder : ImageDecoder
public sealed class HeifDecoder : ImageDecoder
{
private HeicDecoder()
private HeifDecoder()
{
}
/// <summary>
/// Gets the shared instance.
/// </summary>
public static HeicDecoder Instance { get; } = new();
public static HeifDecoder Instance { get; } = new();
/// <inheritdoc/>
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
@ -25,7 +25,7 @@ public sealed class HeicDecoder : ImageDecoder
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new HeicDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
return new HeifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc />
@ -34,7 +34,7 @@ public sealed class HeicDecoder : ImageDecoder
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
HeicDecoderCore decoder = new(options);
HeifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
return image;

201
src/ImageSharp/Formats/Heic/HeicDecoderCore.cs → src/ImageSharp/Formats/Heif/HeifDecoderCore.cs

@ -3,19 +3,18 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Globalization;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Performs the PBM decoding operation.
/// Performs the HEIF decoding operation.
/// </summary>
internal sealed class HeicDecoderCore : IImageDecoderInternals
internal sealed class HeifDecoderCore : IImageDecoderInternals
{
/// <summary>
/// The general configuration.
@ -29,23 +28,23 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private uint primaryItem;
private readonly List<HeicItem> items;
private readonly List<HeifItem> items;
private readonly List<HeicItemLink> itemLinks;
private readonly List<HeifItemLink> itemLinks;
private readonly byte[] buffer;
/// <summary>
/// Initializes a new instance of the <see cref="HeicDecoderCore" /> class.
/// Initializes a new instance of the <see cref="HeifDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public HeicDecoderCore(DecoderOptions options)
public HeifDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.Configuration;
this.metadata = new ImageMetadata();
this.items = new List<HeicItem>();
this.itemLinks = new List<HeicItemLink>();
this.items = new List<HeifItem>();
this.itemLinks = new List<HeifItemLink>();
this.buffer = new byte[80];
}
@ -61,23 +60,23 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{
if (!this.CheckFileTypeBox(stream))
{
throw new ImageFormatException("Not an HEIC image.");
throw new ImageFormatException("Not an HEIF image.");
}
Image<TPixel>? image = null;
while (stream.Position < stream.Length)
{
long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(boxLength, stream);
switch (boxType)
{
case Heic4CharCode.meta:
case Heif4CharCode.meta:
this.ParseMetadata(stream, boxLength);
break;
case Heic4CharCode.mdat:
case Heif4CharCode.mdat:
image = this.ParseMediaData<TPixel>(stream, boxLength);
break;
case Heic4CharCode.free:
case Heif4CharCode.free:
SkipBox(stream, boxLength);
break;
case 0U:
@ -89,7 +88,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
HeicItem? item = this.FindItemById(this.primaryItem);
HeifItem? item = this.FindItemById(this.primaryItem);
if (item == null)
{
throw new ImageFormatException("No primary item found");
@ -100,6 +99,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
throw new NotImplementedException("No JPEG image decoded");
}
this.UpdateMetadata(image.Metadata, item);
return image;
}
@ -110,11 +110,11 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
while (stream.Position < stream.Length)
{
long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(boxLength, stream);
switch (boxType)
{
case Heic4CharCode.meta:
case Heif4CharCode.meta:
this.ParseMetadata(stream, boxLength);
break;
default:
@ -124,40 +124,45 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
HeicItem? item = this.FindItemById(this.primaryItem);
HeifItem? item = this.FindItemById(this.primaryItem);
if (item == null)
{
throw new ImageFormatException("No primary item found");
}
HeicMetadata meta = this.metadata.GetHeicMetadata();
HeicCompressionMethod compressionMethod = HeicCompressionMethod.Hevc;
if (item.Type == Heic4CharCode.av01)
{
compressionMethod = HeicCompressionMethod.Av1;
}
else if (this.itemLinks.Any(link => link.Type == Heic4CharCode.thmb))
{
compressionMethod = HeicCompressionMethod.LegacyJpeg;
}
meta.CompressionMethod = compressionMethod;
this.UpdateMetadata(this.metadata, item);
return new ImageInfo(new PixelTypeInfo(item.BitsPerPixel), new(item.Extent.Width, item.Extent.Height), this.metadata);
}
private bool CheckFileTypeBox(BufferedReadStream stream)
{
long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer);
bool correctBrand = majorBrand == (uint)Heic4CharCode.heic || majorBrand == (uint)Heic4CharCode.heix;
bool correctBrand = majorBrand is (uint)Heif4CharCode.heic or (uint)Heif4CharCode.heix or (uint)Heif4CharCode.avif;
// TODO: Interpret minorVersion and compatible brands.
return boxType == Heic4CharCode.ftyp && correctBrand;
return boxType == Heif4CharCode.ftyp && correctBrand;
}
private void UpdateMetadata(ImageMetadata metadata, HeifItem item)
{
HeifMetadata meta = metadata.GetHeifMetadata();
HeifCompressionMethod compressionMethod = HeifCompressionMethod.Hevc;
if (item.Type == Heif4CharCode.av01)
{
compressionMethod = HeifCompressionMethod.Av1;
}
else if (item.Type == Heif4CharCode.jpeg || this.itemLinks.Any(link => link.Type == Heif4CharCode.thmb))
{
compressionMethod = HeifCompressionMethod.LegacyJpeg;
}
meta.CompressionMethod = compressionMethod;
}
private long ReadBoxHeader(BufferedReadStream stream, out Heic4CharCode boxType)
private long ReadBoxHeader(BufferedReadStream stream, out Heif4CharCode boxType)
{
// Read 4 bytes of length of box
Span<byte> buf = this.ReadIntoBuffer(stream, 8);
@ -165,7 +170,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
long headerSize = 8;
// Read 4 bytes of box type
boxType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buf[4..]);
boxType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buf[4..]);
if (boxSize == 1)
{
@ -177,11 +182,11 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
return boxSize - headerSize;
}
private static int ParseBoxHeader(Span<byte> buffer, out long length, out Heic4CharCode boxType)
private static int ParseBoxHeader(Span<byte> buffer, out long length, out Heif4CharCode boxType)
{
long boxSize = BinaryPrimitives.ReadUInt32BigEndian(buffer);
int bytesRead = 4;
boxType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
boxType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
if (boxSize == 1)
{
@ -199,34 +204,34 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
stream.Skip(4);
while (stream.Position < endPosition)
{
long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType);
long length = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(length, boxLength);
switch (boxType)
{
case Heic4CharCode.iprp:
case Heif4CharCode.iprp:
this.ParseItemProperties(stream, length);
break;
case Heic4CharCode.iinf:
case Heif4CharCode.iinf:
this.ParseItemInfo(stream, length);
break;
case Heic4CharCode.iref:
case Heif4CharCode.iref:
this.ParseItemReference(stream, length);
break;
case Heic4CharCode.pitm:
case Heif4CharCode.pitm:
this.ParsePrimaryItem(stream, length);
break;
case Heic4CharCode.hdlr:
case Heif4CharCode.hdlr:
this.ParseHandler(stream, length);
break;
case Heic4CharCode.iloc:
case Heif4CharCode.iloc:
this.ParseItemLocation(stream, length);
break;
case Heic4CharCode.dinf:
case Heic4CharCode.idat:
case Heic4CharCode.grpl:
case Heic4CharCode.ipro:
case Heic4CharCode.uuid:
case Heic4CharCode.ipmc:
case Heif4CharCode.dinf:
case Heif4CharCode.idat:
case Heif4CharCode.grpl:
case Heif4CharCode.ipro:
case Heif4CharCode.uuid:
case Heif4CharCode.ipmc:
// Silently skip these boxes.
SkipBox(stream, length);
break;
@ -242,8 +247,8 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// Only read the handler type, to check if this is not a movie file.
int bytesRead = 8;
Heic4CharCode handlerType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
if (handlerType != Heic4CharCode.pict)
Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
if (handlerType != Heif4CharCode.pict)
{
throw new ImageFormatException("Not a picture file.");
}
@ -266,15 +271,15 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private int ParseItemInfoEntry(Span<byte> buffer)
{
int bytesRead = ParseBoxHeader(buffer, out long boxLength, out Heic4CharCode boxType);
int bytesRead = ParseBoxHeader(buffer, out long boxLength, out Heif4CharCode boxType);
byte version = buffer[bytesRead];
bytesRead += 4;
HeicItem? item = null;
HeifItem? item = null;
if (version is 0 or 1)
{
uint itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
item = new HeicItem(boxType, itemId);
item = new HeifItem(boxType, itemId);
// Skip Protection Index, not sure what that means...
bytesRead += 2;
@ -312,12 +317,12 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// Skip Protection Index, not sure what that means...
bytesRead += 2;
Heic4CharCode itemType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
Heif4CharCode itemType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4;
item = new HeicItem(itemType, itemId);
item = new HeifItem(itemType, itemId);
item.Name = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.Name.Length + 1;
if (item.Type == Heic4CharCode.mime)
if (item.Type == Heif4CharCode.mime)
{
item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentType.Length + 1;
@ -329,7 +334,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bytesRead += item.ContentEncoding.Length + 1;
}
}
else if (item.Type == Heic4CharCode.uri)
else if (item.Type == Heif4CharCode.uri)
{
item.UriType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.UriType.Length + 1;
@ -352,9 +357,9 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bytesRead += 4;
while (bytesRead < boxLength)
{
bytesRead += ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heic4CharCode linkType);
bytesRead += ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heif4CharCode linkType);
uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead);
HeicItemLink link = new(linkType, sourceId);
HeifItemLink link = new(linkType, sourceId);
int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2;
@ -380,18 +385,18 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private void ParseItemProperties(BufferedReadStream stream, long boxLength)
{
// Cannot use Dictionary here, Properties can have multiple instances with the same key.
List<KeyValuePair<Heic4CharCode, object>> properties = new();
List<KeyValuePair<Heif4CharCode, 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 Heif4CharCode containerType);
EnsureBoxBoundary(containerLength, boxLength);
if (containerType == Heic4CharCode.ipco)
if (containerType == Heif4CharCode.ipco)
{
// Parse Item Property Container, which is just an array of property boxes.
this.ParsePropertyContainer(stream, containerLength, properties);
}
else if (containerType == Heic4CharCode.ipma)
else if (containerType == Heif4CharCode.ipma)
{
// Parse Item Property Association
this.ParsePropertyAssociation(stream, containerLength, properties);
@ -403,28 +408,28 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
private void ParsePropertyContainer(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heic4CharCode, object>> properties)
private void ParsePropertyContainer(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heif4CharCode, object>> properties)
{
long endPosition = stream.Position + boxLength;
while (stream.Position < endPosition)
{
int itemLength = (int)this.ReadBoxHeader(stream, out Heic4CharCode itemType);
int itemLength = (int)this.ReadBoxHeader(stream, out Heif4CharCode itemType);
EnsureBoxBoundary(itemLength, boxLength);
Span<byte> buffer = this.ReadIntoBuffer(stream, itemLength);
switch (itemType)
{
case Heic4CharCode.ispe:
case Heif4CharCode.ispe:
// Skip over version (8 bits) and flags (24 bits).
int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
int height = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]);
properties.Add(new KeyValuePair<Heic4CharCode, object>(Heic4CharCode.ispe, new Size(width, height)));
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.ispe, new Size(width, height)));
break;
case Heic4CharCode.pasp:
case Heif4CharCode.pasp:
int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer);
int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
properties.Add(new KeyValuePair<Heic4CharCode, object>(Heic4CharCode.pasp, new Size(horizontalSpacing, verticalSpacing)));
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.pasp, new Size(horizontalSpacing, verticalSpacing)));
break;
case Heic4CharCode.pixi:
case Heif4CharCode.pixi:
// Skip over version (8 bits) and flags (24 bits).
int channelCount = buffer[4];
int offset = 5;
@ -434,20 +439,20 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bitsPerPixel += buffer[offset + i];
}
properties.Add(new KeyValuePair<Heic4CharCode, object>(Heic4CharCode.pixi, new int[] { channelCount, bitsPerPixel }));
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.pixi, new int[] { channelCount, bitsPerPixel }));
break;
case Heic4CharCode.altt:
case Heic4CharCode.colr:
case Heic4CharCode.imir:
case Heic4CharCode.irot:
case Heic4CharCode.iscl:
case Heic4CharCode.hvcC:
case Heic4CharCode.av1C:
case Heic4CharCode.rloc:
case Heic4CharCode.udes:
case Heif4CharCode.altt:
case Heif4CharCode.colr:
case Heif4CharCode.imir:
case Heif4CharCode.irot:
case Heif4CharCode.iscl:
case Heif4CharCode.hvcC:
case Heif4CharCode.av1C:
case Heif4CharCode.rloc:
case Heif4CharCode.udes:
// TODO: Implement
properties.Add(new KeyValuePair<Heic4CharCode, object>(itemType, new object()));
properties.Add(new KeyValuePair<Heif4CharCode, object>(itemType, new object()));
break;
default:
throw new ImageFormatException($"Unknown item type in property box of '{PrettyPrint(itemType)}'");
@ -455,7 +460,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
private void ParsePropertyAssociation(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heic4CharCode, object>> properties)
private void ParsePropertyAssociation(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heif4CharCode, object>> properties)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0];
@ -477,16 +482,16 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
propId = buffer[bytesRead++] & 0x4FU;
}
KeyValuePair<Heic4CharCode, object> prop = properties[(int)propId];
KeyValuePair<Heif4CharCode, object> prop = properties[(int)propId];
switch (prop.Key)
{
case Heic4CharCode.ispe:
case Heif4CharCode.ispe:
this.items[itemId].SetExtent((Size)prop.Value);
break;
case Heic4CharCode.pasp:
case Heif4CharCode.pasp:
this.items[itemId].PixelAspectRatio = (Size)prop.Value;
break;
case Heic4CharCode.pixi:
case Heif4CharCode.pixi:
int[] values = (int[])prop.Value;
this.items[itemId].ChannelCount = values[0];
this.items[itemId].BitsPerPixel = values[1];
@ -516,7 +521,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
for (uint i = 0; i < itemCount; i++)
{
uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
HeicItem? item = this.FindItemById(itemId);
HeifItem? item = this.FindItemById(itemId);
if (version is 1 or 2)
{
bytesRead++;
@ -538,7 +543,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
ulong extentOffset = ReadUIntVariable(buffer, offsetSize, ref bytesRead);
ulong extentLength = ReadUIntVariable(buffer, lengthSize, ref bytesRead);
HeicLocation loc = new HeicLocation((long)extentOffset, (long)extentLength);
HeifLocation loc = new HeifLocation((long)extentOffset, (long)extentLength);
item?.DataLocations.Add(loc);
}
}
@ -591,14 +596,14 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel>
{
// FIXME: No HVC decoding yet, so parse only a JPEG thumbnail.
HeicItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heic4CharCode.thmb);
HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.thmb);
if (thumbLink == null)
{
throw new NotImplementedException("No thumbnail found");
}
HeicItem? thumbItem = this.FindItemById(thumbLink.SourceId);
if (thumbItem == null || thumbItem.Type != Heic4CharCode.jpeg)
HeifItem? thumbItem = this.FindItemById(thumbLink.SourceId);
if (thumbItem == null || thumbItem.Type != Heif4CharCode.jpeg)
{
throw new NotImplementedException("No HVC decoding implemented yet");
}
@ -610,8 +615,8 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
Span<byte> thumbSpan = thumbMemory.GetSpan();
stream.Read(thumbSpan);
HeicMetadata meta = this.metadata.GetHeicMetadata();
meta.CompressionMethod = HeicCompressionMethod.LegacyJpeg;
HeifMetadata meta = this.metadata.GetHeifMetadata();
meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg;
return Image.Load<TPixel>(thumbSpan);
}
@ -645,7 +650,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
}
}
private HeicItem? FindItemById(uint itemId)
private HeifItem? FindItemById(uint itemId)
=> this.items.FirstOrDefault(item => item.Id == itemId);
private static string ReadNullTerminatedString(Span<byte> span)
@ -654,7 +659,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
return Encoding.UTF8.GetString(bytes);
}
private static string PrettyPrint(Heic4CharCode code)
private static string PrettyPrint(Heif4CharCode code)
{
string? pretty = Enum.GetName(code);
if (string.IsNullOrEmpty(pretty))

10
src/ImageSharp/Formats/Heic/HeicEncoder.cs → src/ImageSharp/Formats/Heif/HeifEncoder.cs

@ -1,19 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Image encoder for writing an image to a stream as HEIC images.
/// Image encoder for writing an image to a stream as HEIF images.
/// </summary>
public sealed class HeicEncoder : ImageEncoder
public sealed class HeifEncoder : ImageEncoder
{
/// <inheritdoc/>
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
HeicEncoderCore encoder = new(image.Configuration, this);
HeifEncoderCore encoder = new(image.Configuration, this);
encoder.Encode(image, stream, cancellationToken);
}
}

92
src/ImageSharp/Formats/Heic/HeicEncoderCore.cs → src/ImageSharp/Formats/Heif/HeifEncoderCore.cs

@ -1,18 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Image encoder for writing an image to a stream as a HEIC image.
/// Image encoder for writing an image to a stream as a HEIF image.
/// </summary>
internal sealed class HeicEncoderCore : IImageEncoderInternals
internal sealed class HeifEncoderCore : IImageEncoderInternals
{
/// <summary>
/// The global configuration.
@ -22,14 +21,14 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
/// <summary>
/// The encoder with options.
/// </summary>
private readonly HeicEncoder encoder;
private readonly HeifEncoder encoder;
/// <summary>
/// Initializes a new instance of the <see cref="HeicEncoderCore"/> class.
/// Initializes a new instance of the <see cref="HeifEncoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="encoder">The encoder with options.</param>
public HeicEncoderCore(Configuration configuration, HeicEncoder encoder)
public HeifEncoderCore(Configuration configuration, HeifEncoder encoder)
{
this.configuration = configuration;
this.encoder = encoder;
@ -49,8 +48,8 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
Guard.NotNull(stream, nameof(stream));
byte[] pixels = await CompressPixels(image, cancellationToken);
List<HeicItem> items = new();
List<HeicItemLink> links = new();
List<HeifItem> items = new();
List<HeifItemLink> links = new();
GenerateItems(image, pixels, items, links);
// Write out the generated header and pixels.
@ -58,25 +57,28 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
this.WriteMetadataBox(items, links, stream);
this.WriteMediaDataBox(pixels, stream);
stream.Flush();
HeifMetadata meta = image.Metadata.GetHeifMetadata();
meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg;
}
private static void GenerateItems<TPixel>(Image<TPixel> image, byte[] pixels, List<HeicItem> items, List<HeicItemLink> links)
private static void GenerateItems<TPixel>(Image<TPixel> image, byte[] pixels, List<HeifItem> items, List<HeifItemLink> links)
where TPixel : unmanaged, IPixel<TPixel>
{
HeicItem primaryItem = new(Heic4CharCode.jpeg, 1u);
primaryItem.DataLocations.Add(new HeicLocation(0L, pixels.LongLength));
HeifItem primaryItem = new(Heif4CharCode.jpeg, 1u);
primaryItem.DataLocations.Add(new HeifLocation(0L, pixels.LongLength));
primaryItem.BitsPerPixel = 24;
primaryItem.ChannelCount = 3;
primaryItem.SetExtent(image.Size);
items.Add(primaryItem);
// Create a fake thumbnail, to make our own Decoder happy.
HeicItemLink thumbnail = new(Heic4CharCode.thmb, 1u);
HeifItemLink thumbnail = new(Heif4CharCode.thmb, 1u);
thumbnail.DestinationIds.Add(1u);
links.Add(thumbnail);
}
private static int WriteBoxHeader(Span<byte> buffer, Heic4CharCode type)
private static int WriteBoxHeader(Span<byte> buffer, Heif4CharCode type)
{
int bytesWritten = 0;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 8U);
@ -87,7 +89,7 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten;
}
private static int WriteBoxHeader(Span<byte> buffer, Heic4CharCode type, byte version, uint flags)
private static int WriteBoxHeader(Span<byte> buffer, Heif4CharCode type, byte version, uint flags)
{
int bytesWritten = 0;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 12);
@ -106,25 +108,25 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private void WriteFileTypeBox(Stream stream)
{
Span<byte> buffer = stackalloc byte[24];
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.ftyp);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.heic);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.ftyp);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.mif1);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.mif1);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.heic);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten);
stream.Write(buffer);
}
private void WriteMetadataBox(List<HeicItem> items, List<HeicItemLink> links, Stream stream)
private void WriteMetadataBox(List<HeifItem> items, List<HeifItemLink> links, Stream stream)
{
using AutoExpandingMemory<byte> memory = new(this.configuration, 0x1000);
Span<byte> buffer = memory.GetSpan(12);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.meta, 0, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.meta, 0, 0);
bytesWritten += WriteHandlerBox(memory, bytesWritten);
bytesWritten += WritePrimaryItemBox(memory, bytesWritten);
bytesWritten += WriteItemInfoBox(memory, bytesWritten, items);
@ -141,10 +143,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private static int WriteHandlerBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 33);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.hdlr, 0, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.hdlr, 0, 0);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.pict);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.pict);
bytesWritten += 4;
for (int i = 0; i < 13; i++)
{
@ -158,7 +160,7 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private static int WritePrimaryItemBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 14);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.pitm, 0, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.pitm, 0, 0);
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1);
bytesWritten += 2;
@ -166,16 +168,16 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten;
}
private static int WriteItemInfoBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeicItem> items)
private static int WriteItemInfoBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeifItem> items)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 14 + (items.Count * 21));
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iinf, 0, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iinf, 0, 0);
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)items.Count);
bytesWritten += 2;
foreach (HeicItem item in items)
foreach (HeifItem item in items)
{
int itemLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heic4CharCode.infe, 2, 0);
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.infe, 2, 0);
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id);
bytesWritten += 2;
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 0);
@ -191,11 +193,11 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten;
}
private static int WriteItemReferenceBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeicItem> items, List<HeicItemLink> links)
private static int WriteItemReferenceBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeifItem> items, List<HeifItemLink> links)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 12 + (links.Count * (12 + (items.Count * 2))));
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iref, 0, 0);
foreach (HeicItemLink link in links)
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iref, 0, 0);
foreach (HeifItemLink link in links)
{
int itemLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], link.Type);
@ -216,16 +218,16 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten;
}
private static int WriteItemPropertiesBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeicItem> items)
private static int WriteItemPropertiesBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeifItem> items)
{
const ushort numPropPerItem = 1;
Span<byte> buffer = memory.GetSpan(memoryOffset, 20);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iprp);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iprp);
// Write 'ipco' box
int ipcoLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heic4CharCode.ipco);
foreach (HeicItem item in items)
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.ipco);
foreach (HeifItem item in items)
{
bytesWritten += WriteSpatialExtentPropertyBox(memory, memoryOffset + bytesWritten, item);
}
@ -235,11 +237,11 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
// Write 'ipma' box
int ipmaLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heic4CharCode.ipma, 0, 0);
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.ipma, 0, 0);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)(items.Count * numPropPerItem));
bytesWritten += 4;
ushort propIndex = 0;
foreach (HeicItem item in items)
foreach (HeifItem item in items)
{
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id);
bytesWritten += 2;
@ -256,10 +258,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten;
}
private static int WriteSpatialExtentPropertyBox(AutoExpandingMemory<byte> memory, int memoryOffset, HeicItem item)
private static int WriteSpatialExtentPropertyBox(AutoExpandingMemory<byte> memory, int memoryOffset, HeifItem item)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 20);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.ispe, 0, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.ispe, 0, 0);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Width);
bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Height);
@ -272,16 +274,16 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private static int WriteItemDataBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 10);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.idat);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.idat);
BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten);
return bytesWritten;
}
private static int WriteItemLocationBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeicItem> items)
private static int WriteItemLocationBox(AutoExpandingMemory<byte> memory, int memoryOffset, List<HeifItem> items)
{
Span<byte> buffer = memory.GetSpan(memoryOffset, 30 + (items.Count * 8));
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iloc, 1, 0);
int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iloc, 1, 0);
buffer[bytesWritten++] = 0x44;
buffer[bytesWritten++] = 0;
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1);
@ -293,10 +295,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
buffer[bytesWritten++] = 0;
}
IEnumerable<HeicLocation> itemLocs = items.SelectMany(item => item.DataLocations).Where(loc => loc != null);
IEnumerable<HeifLocation> itemLocs = items.SelectMany(item => item.DataLocations).Where(loc => loc != null);
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)itemLocs.Count());
bytesWritten += 2;
foreach (HeicLocation loc in itemLocs)
foreach (HeifLocation loc in itemLocs)
{
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)loc.Offset);
bytesWritten += 4;
@ -311,7 +313,7 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private void WriteMediaDataBox(Span<byte> data, Stream stream)
{
Span<byte> buf = stackalloc byte[12];
int bytesWritten = WriteBoxHeader(buf, Heic4CharCode.mdat);
int bytesWritten = WriteBoxHeader(buf, Heif4CharCode.mdat);
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)(data.Length + bytesWritten));
stream.Write(buf[..bytesWritten]);

14
src/ImageSharp/Formats/Heic/HeicFormat.cs → src/ImageSharp/Formats/Heif/HeifFormat.cs

@ -1,21 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the HEIC format.
/// </summary>
public sealed class HeicFormat : IImageFormat<HeicMetadata>
public sealed class HeifFormat : IImageFormat<HeifMetadata>
{
private HeicFormat()
private HeifFormat()
{
}
/// <summary>
/// Gets the shared instance.
/// </summary>
public static HeicFormat Instance { get; } = new();
public static HeifFormat Instance { get; } = new();
/// <inheritdoc/>
public string Name => "HEIC";
@ -24,11 +24,11 @@ public sealed class HeicFormat : IImageFormat<HeicMetadata>
public string DefaultMimeType => "image/heif";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => HeicConstants.MimeTypes;
public IEnumerable<string> MimeTypes => HeifConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => HeicConstants.FileExtensions;
public IEnumerable<string> FileExtensions => HeifConstants.FileExtensions;
/// <inheritdoc/>
public HeicMetadata CreateDefaultFormatMetadata() => new();
public HeifMetadata CreateDefaultFormatMetadata() => new();
}

12
src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs → src/ImageSharp/Formats/Heif/HeifImageFormatDetector.cs

@ -4,12 +4,12 @@
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Detects HEIC file headers.
/// Detects HEIF file headers.
/// </summary>
public sealed class HeicImageFormatDetector : IImageFormatDetector
public sealed class HeifImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 12;
@ -17,14 +17,14 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector
/// <inheritdoc/>
public bool TryDetectFormat(ReadOnlySpan<byte> header, [NotNullWhen(true)] out IImageFormat? format)
{
format = IsSupportedFileFormat(header) ? HeicFormat.Instance : null;
format = IsSupportedFileFormat(header) ? HeifFormat.Instance : null;
return format != null;
}
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heic4CharCode.ftyp;
bool hasFtyp = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == (uint)Heif4CharCode.ftyp;
uint brand = BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8));
return hasFtyp && (brand == (uint)Heic4CharCode.heic || brand == (uint)Heic4CharCode.heix || brand == (uint)Heic4CharCode.avif);
return hasFtyp && (brand == (uint)Heif4CharCode.heic || brand == (uint)Heif4CharCode.heix || brand == (uint)Heif4CharCode.avif);
}
}

10
src/ImageSharp/Formats/Heic/HeicItem.cs → src/ImageSharp/Formats/Heif/HeifItem.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Provides definition for a HEIC Item.
/// Provides definition for a HEIF Item.
/// </summary>
public class HeicItem(Heic4CharCode type, uint id)
public class HeifItem(Heif4CharCode type, uint id)
{
/// <summary>
/// Gets the ID of this Item.
@ -16,7 +16,7 @@ public class HeicItem(Heic4CharCode type, uint id)
/// <summary>
/// Gets the type of this Item.
/// </summary>
public Heic4CharCode Type { get; } = type;
public Heif4CharCode Type { get; } = type;
/// <summary>
/// Gets or sets the name of this item.
@ -71,7 +71,7 @@ public class HeicItem(Heic4CharCode type, uint id)
/// <summary>
/// Gets the list of data locations for this item.
/// </summary>
public List<HeicLocation> DataLocations { get; } = new List<HeicLocation>();
public List<HeifLocation> DataLocations { get; } = new List<HeifLocation>();
/// <summary>
/// Set the image extent.

8
src/ImageSharp/Formats/Heic/HeicItemLink.cs → src/ImageSharp/Formats/Heif/HeifItemLink.cs

@ -1,17 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Link between <see cref="HeicItem"/> instances within the same HEIC file.
/// Link between <see cref="HeifItem"/> instances within the same HEIF file.
/// </summary>
public class HeicItemLink(Heic4CharCode type, uint sourceId)
public class HeifItemLink(Heif4CharCode type, uint sourceId)
{
/// <summary>
/// Gets the type of link.
/// </summary>
public Heic4CharCode Type { get; } = type;
public Heif4CharCode Type { get; } = type;
/// <summary>
/// Gets the ID of the source item of this link.

6
src/ImageSharp/Formats/Heic/HeicLocation.cs → src/ImageSharp/Formats/Heif/HeifLocation.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Location within the file of an <see cref="HeicItem"/>.
/// Location within the file of an <see cref="HeifItem"/>.
/// </summary>
public class HeicLocation(long offset, long length)
public class HeifLocation(long offset, long length)
{
/// <summary>
/// Gets the file offset of this location.

18
src/ImageSharp/Formats/Heic/HeicMetadata.cs → src/ImageSharp/Formats/Heif/HeifMetadata.cs

@ -1,32 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Provides HEIC specific metadata information for the image.
/// Provides HEIF specific metadata information for the image.
/// </summary>
public class HeicMetadata : IDeepCloneable
public class HeifMetadata : IDeepCloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="HeicMetadata"/> class.
/// Initializes a new instance of the <see cref="HeifMetadata"/> class.
/// </summary>
public HeicMetadata()
public HeifMetadata()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HeicMetadata"/> class.
/// Initializes a new instance of the <see cref="HeifMetadata"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private HeicMetadata(HeicMetadata other)
private HeifMetadata(HeifMetadata other)
=> this.CompressionMethod = other.CompressionMethod;
/// <summary>
/// Gets or sets the compression method used for the primary frame.
/// </summary>
public HeicCompressionMethod CompressionMethod { get; set; }
public HeifCompressionMethod CompressionMethod { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new HeicMetadata(this);
public IDeepCloneable DeepClone() => new HeifMetadata(this);
}

4
src/ImageSharp/Formats/Heic/IHeicEncoderOptions.cs → src/ImageSharp/Formats/Heif/IHeicEncoderOptions.cs

@ -1,10 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Configuration options for use during HEIC encoding.
/// Configuration options for use during HEIF encoding.
/// </summary>
internal interface IHeicEncoderOptions
{

8
src/ImageSharp/Formats/Heic/MetadataExtensions.cs → src/ImageSharp/Formats/Heif/MetadataExtensions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp;
@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp;
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the pbm format specific metadata for the image.
/// Gets the HEIF format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="HeicMetadata"/>.</returns>
public static HeicMetadata GetHeicMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(HeicFormat.Instance);
/// <returns>The <see cref="HeifMetadata"/>.</returns>
public static HeifMetadata GetHeifMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(HeifFormat.Instance);
}

0
src/ImageSharp/Formats/Heic/Readme.md → src/ImageSharp/Formats/Heif/Readme.md

8
src/ImageSharp/ImageSharp.csproj

@ -52,10 +52,10 @@
</ItemGroup>
<ItemGroup>
<Compile Update="Formats\Heic\Heic4CharCode.cs">
<Compile Update="Formats\Heif\Heif4CharCode.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Heic4CharCode.tt</DependentUpon>
<DependentUpon>Heif4CharCode.tt</DependentUpon>
</Compile>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime>
@ -155,9 +155,9 @@
</ItemGroup>
<ItemGroup>
<None Update="Formats\Heic\Heic4CharCode.tt">
<None Update="Formats\Heif\Heif4CharCode.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Heic4CharCode.cs</LastGenOutput>
<LastGenOutput>Heif4CharCode.cs</LastGenOutput>
</None>
<None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>

28
tests/ImageSharp.Tests/Formats/Heic/HeicDecoderTests.cs → tests/ImageSharp.Tests/Formats/Heif/HeifDecoderTests.cs

@ -1,43 +1,43 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.Formats.Heic;
namespace SixLabors.ImageSharp.Tests.Formats.Heif;
[Trait("Format", "Heic")]
[Trait("Format", "Heif")]
[ValidateDisposedMemoryAllocations]
public class HeicDecoderTests
public class HeifDecoderTests
{
[Theory]
[InlineData(TestImages.Heic.Image1, HeicCompressionMethod.Hevc)]
[InlineData(TestImages.Heic.Sample640x427, HeicCompressionMethod.Hevc)]
[InlineData(TestImages.Heic.FujiFilmHif, HeicCompressionMethod.LegacyJpeg)]
[InlineData(TestImages.Heic.IrvineAvif, HeicCompressionMethod.Av1)]
public void Identify(string imagePath, HeicCompressionMethod compressionMethod)
[InlineData(TestImages.Heif.Image1, HeifCompressionMethod.Hevc)]
[InlineData(TestImages.Heif.Sample640x427, HeifCompressionMethod.Hevc)]
[InlineData(TestImages.Heif.FujiFilmHif, HeifCompressionMethod.LegacyJpeg)]
[InlineData(TestImages.Heif.IrvineAvif, HeifCompressionMethod.Av1)]
public void Identify(string imagePath, HeifCompressionMethod compressionMethod)
{
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream);
HeicMetadata heicMetadata = imageInfo.Metadata.GetHeicMetadata();
HeifMetadata heicMetadata = imageInfo.Metadata.GetHeifMetadata();
Assert.NotNull(imageInfo);
Assert.Equal(HeicFormat.Instance, imageInfo.Metadata.DecodedImageFormat);
Assert.Equal(HeifFormat.Instance, imageInfo.Metadata.DecodedImageFormat);
Assert.Equal(compressionMethod, heicMetadata.CompressionMethod);
}
[Theory]
[WithFile(TestImages.Heic.FujiFilmHif, PixelTypes.Rgba32)]
[WithFile(TestImages.Heif.FujiFilmHif, PixelTypes.Rgba32)]
public void Decode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
HeicMetadata heicMetadata = image.Metadata.GetHeicMetadata();
HeifMetadata heicMetadata = image.Metadata.GetHeifMetadata();
image.DebugSave(provider);
image.CompareToReferenceOutput(provider);
Assert.Equal(HeicCompressionMethod.Hevc, heicMetadata.CompressionMethod);
Assert.Equal(HeifCompressionMethod.LegacyJpeg, heicMetadata.CompressionMethod);
}
}

18
tests/ImageSharp.Tests/Formats/Heic/HeicEncoderTests.cs → tests/ImageSharp.Tests/Formats/Heif/HeifEncoderTests.cs

@ -1,32 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
namespace SixLabors.ImageSharp.Tests.Formats.Heic;
namespace SixLabors.ImageSharp.Tests.Formats.Heif;
[Trait("Format", "Heic")]
[Trait("Format", "Heif")]
[ValidateDisposedMemoryAllocations]
public class HeicEncoderTests
public class HeifEncoderTests
{
[Theory]
[WithFile(TestImages.Heic.Sample640x427, PixelTypes.Rgba32)]
public static void Encode<TPixel>(TestImageProvider<TPixel> provider)
[WithFile(TestImages.Heif.Sample640x427, PixelTypes.Rgba32, HeifCompressionMethod.LegacyJpeg)]
public static void Encode<TPixel>(TestImageProvider<TPixel> provider, HeifCompressionMethod compressionMethod)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(new MagickReferenceDecoder());
using MemoryStream stream = new();
HeicEncoder encoder = new();
HeifEncoder encoder = new();
image.Save(stream, encoder);
stream.Position = 0;
using Image<TPixel> encodedImage = Image.Load<TPixel>(stream);
HeicMetadata heicMetadata = encodedImage.Metadata.GetHeicMetadata();
HeifMetadata heifMetadata = encodedImage.Metadata.GetHeifMetadata();
ImageComparer.Exact.CompareImages(image, encodedImage);
//Assert.Equal(heicMetadata.Channels, channels);
Assert.Equal(compressionMethod, heifMetadata.CompressionMethod);
}
}

6
tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs

@ -5,8 +5,8 @@ using Moq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
@ -36,7 +36,7 @@ public class ImageFormatManagerTests
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<BmpEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<JpegEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<GifEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<HeicEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<HeifEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<TgaEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<TiffEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<WebpEncoder>().Count());
@ -46,7 +46,7 @@ public class ImageFormatManagerTests
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<JpegDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<GifDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<HeicDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<HeifDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<TgaDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<TiffDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<WebpDecoder>().Count());

16
tests/ImageSharp.Tests/TestImages.cs

@ -1105,18 +1105,18 @@ public static class TestImages
public const string Wikipedia008 = "Qoi/wikipedia_008.qoi";
}
public static class Heic
public static class Heif
{
public const string Image1 = "Heic/image1.heic";
public const string Image2 = "Heic/image2.heic";
public const string Image3 = "Heic/image3.heic";
public const string Image4 = "Heic/image4.heic";
public const string Sample640x427 = "Heic/dwsample-heic-640.heic";
public const string Image1 = "Heif/image1.heic";
public const string Image2 = "Heif/image2.heic";
public const string Image3 = "Heif/image3.heic";
public const string Image4 = "Heif/image4.heic";
public const string Sample640x427 = "Heif/dwsample-heic-640.heic";
// Downloaded from: https://github.com/draktable-org/darktable/issues/14473
public const string FujiFilmHif = "Heic/IMG-20230508-0053.hif";
public const string FujiFilmHif = "Heif/IMG-20230508-0053.hif";
// Downloaded from: https://github.com/AOMediaCodec/av1-avif/blob/master/testFiles/Microsoft/Irvine_CA.avif
public const string IrvineAvif = "Heic/Irvine_CA.avif";
public const string IrvineAvif = "Heif/Irvine_CA.avif";
}
}

4
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -4,8 +4,8 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi;
@ -60,7 +60,7 @@ public static partial class TestEnvironment
Configuration cfg = new(
new JpegConfigurationModule(),
new GifConfigurationModule(),
new HeicConfigurationModule(),
new HeifConfigurationModule(),
new PbmConfigurationModule(),
new TgaConfigurationModule(),
new WebpConfigurationModule(),

0
tests/Images/External/ReferenceOutput/HeicDecoderTests/Decode_Rgba32_IMG-20230508-0053.png → tests/Images/External/ReferenceOutput/HeifDecoderTests/Decode_Rgba32_IMG-20230508-0053.png

Loading…
Cancel
Save