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 *.ico filter=lfs diff=lfs merge=lfs -text
*.cur filter=lfs diff=lfs merge=lfs -text *.cur filter=lfs diff=lfs merge=lfs -text
*.ani 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;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heic; using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
@ -212,7 +212,7 @@ public sealed class Configuration
/// <see cref="TiffConfigurationModule"/>. /// <see cref="TiffConfigurationModule"/>.
/// <see cref="WebpConfigurationModule"/>. /// <see cref="WebpConfigurationModule"/>.
/// <see cref="QoiConfigurationModule"/>. /// <see cref="QoiConfigurationModule"/>.
/// <see cref="HeicConfigurationModule"/>. /// <see cref="HeifConfigurationModule"/>.
/// </summary> /// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns> /// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance() => new( internal static Configuration CreateDefaultInstance() => new(
@ -225,5 +225,5 @@ public sealed class Configuration
new TiffConfigurationModule(), new TiffConfigurationModule(),
new WebpConfigurationModule(), new WebpConfigurationModule(),
new QoiConfigurationModule(), 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; using System.CodeDom.Compiler;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Supported 4 character codes for use in HEIC images. /// Supported 4 character codes for use in HEIF images.
/// </summary> /// </summary>
[GeneratedCode("T4", "")] [GeneratedCode("T4", "")]
public enum Heic4CharCode : uint public enum Heif4CharCode : uint
{ {
/// <summary> /// <summary>
/// File Type. /// File Type.

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

@ -59,13 +59,13 @@
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Supported 4 character codes for use in HEIC images. /// Supported 4 character codes for use in HEIF images.
/// </summary> /// </summary>
[GeneratedCode("T4", "")] [GeneratedCode("T4", "")]
public enum Heic4CharCode : uint public enum Heif4CharCode : uint
{ {
<# <#
for (int i = 0; i < codes.Length; i += 2) 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Compression algorithms possible inside an HEIF (High Efficiency File Format) based file. /// Compression algorithms possible inside an HEIF (High Efficiency File Format) based file.
/// </summary> /// </summary>
public enum HeicCompressionMethod public enum HeifCompressionMethod
{ {
/// <summary> /// <summary>
/// High Efficiency Video Coding /// 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Contains HEIC (and H.265) constant values defined in the specification. /// Contains HEIF constant values defined in the specification.
/// </summary> /// </summary>
internal static class HeicConstants internal static class HeifConstants
{ {
public const Heic4CharCode HeicBrand = Heic4CharCode.heic; public const Heif4CharCode HeicBrand = Heif4CharCode.heic;
/// <summary> /// <summary>
/// The list of mimetypes that equate to a HEIC. /// 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; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Image decoder for reading HEIC images from a stream. /// Image decoder for reading HEIF images from a stream.
/// </summary> /// </summary>
public sealed class HeicDecoder : ImageDecoder public sealed class HeifDecoder : ImageDecoder
{ {
private HeicDecoder() private HeifDecoder()
{ {
} }
/// <summary> /// <summary>
/// Gets the shared instance. /// Gets the shared instance.
/// </summary> /// </summary>
public static HeicDecoder Instance { get; } = new(); public static HeifDecoder Instance { get; } = new();
/// <inheritdoc/> /// <inheritdoc/>
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) 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(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
return new HeicDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); return new HeifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,7 +34,7 @@ public sealed class HeicDecoder : ImageDecoder
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
HeicDecoderCore decoder = new(options); HeifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
return image; 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;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Globalization;
using System.Text; using System.Text;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Performs the PBM decoding operation. /// Performs the HEIF decoding operation.
/// </summary> /// </summary>
internal sealed class HeicDecoderCore : IImageDecoderInternals internal sealed class HeifDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The general configuration. /// The general configuration.
@ -29,23 +28,23 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private uint primaryItem; 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; private readonly byte[] buffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeicDecoderCore" /> class. /// Initializes a new instance of the <see cref="HeifDecoderCore" /> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public HeicDecoderCore(DecoderOptions options) public HeifDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
this.metadata = new ImageMetadata(); this.metadata = new ImageMetadata();
this.items = new List<HeicItem>(); this.items = new List<HeifItem>();
this.itemLinks = new List<HeicItemLink>(); this.itemLinks = new List<HeifItemLink>();
this.buffer = new byte[80]; this.buffer = new byte[80];
} }
@ -61,23 +60,23 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
{ {
if (!this.CheckFileTypeBox(stream)) if (!this.CheckFileTypeBox(stream))
{ {
throw new ImageFormatException("Not an HEIC image."); throw new ImageFormatException("Not an HEIF image.");
} }
Image<TPixel>? image = null; Image<TPixel>? image = null;
while (stream.Position < stream.Length) while (stream.Position < stream.Length)
{ {
long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(boxLength, stream); EnsureBoxBoundary(boxLength, stream);
switch (boxType) switch (boxType)
{ {
case Heic4CharCode.meta: case Heif4CharCode.meta:
this.ParseMetadata(stream, boxLength); this.ParseMetadata(stream, boxLength);
break; break;
case Heic4CharCode.mdat: case Heif4CharCode.mdat:
image = this.ParseMediaData<TPixel>(stream, boxLength); image = this.ParseMediaData<TPixel>(stream, boxLength);
break; break;
case Heic4CharCode.free: case Heif4CharCode.free:
SkipBox(stream, boxLength); SkipBox(stream, boxLength);
break; break;
case 0U: 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) if (item == null)
{ {
throw new ImageFormatException("No primary item found"); throw new ImageFormatException("No primary item found");
@ -100,6 +99,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
throw new NotImplementedException("No JPEG image decoded"); throw new NotImplementedException("No JPEG image decoded");
} }
this.UpdateMetadata(image.Metadata, item);
return image; return image;
} }
@ -110,11 +110,11 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
while (stream.Position < stream.Length) while (stream.Position < stream.Length)
{ {
long boxLength = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(boxLength, stream); EnsureBoxBoundary(boxLength, stream);
switch (boxType) switch (boxType)
{ {
case Heic4CharCode.meta: case Heif4CharCode.meta:
this.ParseMetadata(stream, boxLength); this.ParseMetadata(stream, boxLength);
break; break;
default: 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) if (item == null)
{ {
throw new ImageFormatException("No primary item found"); throw new ImageFormatException("No primary item found");
} }
HeicMetadata meta = this.metadata.GetHeicMetadata(); this.UpdateMetadata(this.metadata, item);
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;
return new ImageInfo(new PixelTypeInfo(item.BitsPerPixel), new(item.Extent.Width, item.Extent.Height), this.metadata); return new ImageInfo(new PixelTypeInfo(item.BitsPerPixel), new(item.Extent.Width, item.Extent.Height), this.metadata);
} }
private bool CheckFileTypeBox(BufferedReadStream stream) 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); Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer); 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. // 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 // Read 4 bytes of length of box
Span<byte> buf = this.ReadIntoBuffer(stream, 8); Span<byte> buf = this.ReadIntoBuffer(stream, 8);
@ -165,7 +170,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
long headerSize = 8; long headerSize = 8;
// Read 4 bytes of box type // Read 4 bytes of box type
boxType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buf[4..]); boxType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buf[4..]);
if (boxSize == 1) if (boxSize == 1)
{ {
@ -177,11 +182,11 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
return boxSize - headerSize; 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); long boxSize = BinaryPrimitives.ReadUInt32BigEndian(buffer);
int bytesRead = 4; int bytesRead = 4;
boxType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); boxType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4; bytesRead += 4;
if (boxSize == 1) if (boxSize == 1)
{ {
@ -199,34 +204,34 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
stream.Skip(4); stream.Skip(4);
while (stream.Position < endPosition) while (stream.Position < endPosition)
{ {
long length = this.ReadBoxHeader(stream, out Heic4CharCode boxType); long length = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(length, boxLength); EnsureBoxBoundary(length, boxLength);
switch (boxType) switch (boxType)
{ {
case Heic4CharCode.iprp: case Heif4CharCode.iprp:
this.ParseItemProperties(stream, length); this.ParseItemProperties(stream, length);
break; break;
case Heic4CharCode.iinf: case Heif4CharCode.iinf:
this.ParseItemInfo(stream, length); this.ParseItemInfo(stream, length);
break; break;
case Heic4CharCode.iref: case Heif4CharCode.iref:
this.ParseItemReference(stream, length); this.ParseItemReference(stream, length);
break; break;
case Heic4CharCode.pitm: case Heif4CharCode.pitm:
this.ParsePrimaryItem(stream, length); this.ParsePrimaryItem(stream, length);
break; break;
case Heic4CharCode.hdlr: case Heif4CharCode.hdlr:
this.ParseHandler(stream, length); this.ParseHandler(stream, length);
break; break;
case Heic4CharCode.iloc: case Heif4CharCode.iloc:
this.ParseItemLocation(stream, length); this.ParseItemLocation(stream, length);
break; break;
case Heic4CharCode.dinf: case Heif4CharCode.dinf:
case Heic4CharCode.idat: case Heif4CharCode.idat:
case Heic4CharCode.grpl: case Heif4CharCode.grpl:
case Heic4CharCode.ipro: case Heif4CharCode.ipro:
case Heic4CharCode.uuid: case Heif4CharCode.uuid:
case Heic4CharCode.ipmc: case Heif4CharCode.ipmc:
// Silently skip these boxes. // Silently skip these boxes.
SkipBox(stream, length); SkipBox(stream, length);
break; break;
@ -242,8 +247,8 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// Only read the handler type, to check if this is not a movie file. // Only read the handler type, to check if this is not a movie file.
int bytesRead = 8; int bytesRead = 8;
Heic4CharCode handlerType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
if (handlerType != Heic4CharCode.pict) if (handlerType != Heif4CharCode.pict)
{ {
throw new ImageFormatException("Not a picture file."); throw new ImageFormatException("Not a picture file.");
} }
@ -266,15 +271,15 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private int ParseItemInfoEntry(Span<byte> buffer) 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]; byte version = buffer[bytesRead];
bytesRead += 4; bytesRead += 4;
HeicItem? item = null; HeifItem? item = null;
if (version is 0 or 1) if (version is 0 or 1)
{ {
uint itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]); uint itemId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2; bytesRead += 2;
item = new HeicItem(boxType, itemId); item = new HeifItem(boxType, itemId);
// Skip Protection Index, not sure what that means... // Skip Protection Index, not sure what that means...
bytesRead += 2; bytesRead += 2;
@ -312,12 +317,12 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
// Skip Protection Index, not sure what that means... // Skip Protection Index, not sure what that means...
bytesRead += 2; bytesRead += 2;
Heic4CharCode itemType = (Heic4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]); Heif4CharCode itemType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
bytesRead += 4; bytesRead += 4;
item = new HeicItem(itemType, itemId); item = new HeifItem(itemType, itemId);
item.Name = ReadNullTerminatedString(buffer[bytesRead..]); item.Name = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.Name.Length + 1; bytesRead += item.Name.Length + 1;
if (item.Type == Heic4CharCode.mime) if (item.Type == Heif4CharCode.mime)
{ {
item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]); item.ContentType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.ContentType.Length + 1; bytesRead += item.ContentType.Length + 1;
@ -329,7 +334,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bytesRead += item.ContentEncoding.Length + 1; bytesRead += item.ContentEncoding.Length + 1;
} }
} }
else if (item.Type == Heic4CharCode.uri) else if (item.Type == Heif4CharCode.uri)
{ {
item.UriType = ReadNullTerminatedString(buffer[bytesRead..]); item.UriType = ReadNullTerminatedString(buffer[bytesRead..]);
bytesRead += item.UriType.Length + 1; bytesRead += item.UriType.Length + 1;
@ -352,9 +357,9 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bytesRead += 4; bytesRead += 4;
while (bytesRead < boxLength) 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); uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead);
HeicItemLink link = new(linkType, sourceId); HeifItemLink link = new(linkType, sourceId);
int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]); int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
bytesRead += 2; bytesRead += 2;
@ -380,18 +385,18 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
private void ParseItemProperties(BufferedReadStream stream, long boxLength) private void ParseItemProperties(BufferedReadStream stream, long boxLength)
{ {
// Cannot use Dictionary here, Properties can have multiple instances with the same key. // 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; long endBoxPosition = stream.Position + boxLength;
while (stream.Position < endBoxPosition) while (stream.Position < endBoxPosition)
{ {
long containerLength = this.ReadBoxHeader(stream, out Heic4CharCode containerType); long containerLength = this.ReadBoxHeader(stream, out Heif4CharCode containerType);
EnsureBoxBoundary(containerLength, boxLength); EnsureBoxBoundary(containerLength, boxLength);
if (containerType == Heic4CharCode.ipco) if (containerType == Heif4CharCode.ipco)
{ {
// Parse Item Property Container, which is just an array of property boxes. // Parse Item Property Container, which is just an array of property boxes.
this.ParsePropertyContainer(stream, containerLength, properties); this.ParsePropertyContainer(stream, containerLength, properties);
} }
else if (containerType == Heic4CharCode.ipma) else if (containerType == Heif4CharCode.ipma)
{ {
// Parse Item Property Association // Parse Item Property Association
this.ParsePropertyAssociation(stream, containerLength, properties); 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; long endPosition = stream.Position + boxLength;
while (stream.Position < endPosition) 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); EnsureBoxBoundary(itemLength, boxLength);
Span<byte> buffer = this.ReadIntoBuffer(stream, itemLength); Span<byte> buffer = this.ReadIntoBuffer(stream, itemLength);
switch (itemType) switch (itemType)
{ {
case Heic4CharCode.ispe: case Heif4CharCode.ispe:
// Skip over version (8 bits) and flags (24 bits). // Skip over version (8 bits) and flags (24 bits).
int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
int height = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]); 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; break;
case Heic4CharCode.pasp: case Heif4CharCode.pasp:
int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer); int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer);
int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]); 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; break;
case Heic4CharCode.pixi: case Heif4CharCode.pixi:
// Skip over version (8 bits) and flags (24 bits). // Skip over version (8 bits) and flags (24 bits).
int channelCount = buffer[4]; int channelCount = buffer[4];
int offset = 5; int offset = 5;
@ -434,20 +439,20 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
bitsPerPixel += buffer[offset + i]; 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; break;
case Heic4CharCode.altt: case Heif4CharCode.altt:
case Heic4CharCode.colr: case Heif4CharCode.colr:
case Heic4CharCode.imir: case Heif4CharCode.imir:
case Heic4CharCode.irot: case Heif4CharCode.irot:
case Heic4CharCode.iscl: case Heif4CharCode.iscl:
case Heic4CharCode.hvcC: case Heif4CharCode.hvcC:
case Heic4CharCode.av1C: case Heif4CharCode.av1C:
case Heic4CharCode.rloc: case Heif4CharCode.rloc:
case Heic4CharCode.udes: case Heif4CharCode.udes:
// TODO: Implement // TODO: Implement
properties.Add(new KeyValuePair<Heic4CharCode, object>(itemType, new object())); properties.Add(new KeyValuePair<Heif4CharCode, object>(itemType, new object()));
break; break;
default: default:
throw new ImageFormatException($"Unknown item type in property box of '{PrettyPrint(itemType)}'"); 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); Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0]; byte version = buffer[0];
@ -477,16 +482,16 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
propId = buffer[bytesRead++] & 0x4FU; propId = buffer[bytesRead++] & 0x4FU;
} }
KeyValuePair<Heic4CharCode, object> prop = properties[(int)propId]; KeyValuePair<Heif4CharCode, object> prop = properties[(int)propId];
switch (prop.Key) switch (prop.Key)
{ {
case Heic4CharCode.ispe: case Heif4CharCode.ispe:
this.items[itemId].SetExtent((Size)prop.Value); this.items[itemId].SetExtent((Size)prop.Value);
break; break;
case Heic4CharCode.pasp: case Heif4CharCode.pasp:
this.items[itemId].PixelAspectRatio = (Size)prop.Value; this.items[itemId].PixelAspectRatio = (Size)prop.Value;
break; break;
case Heic4CharCode.pixi: case Heif4CharCode.pixi:
int[] values = (int[])prop.Value; int[] values = (int[])prop.Value;
this.items[itemId].ChannelCount = values[0]; this.items[itemId].ChannelCount = values[0];
this.items[itemId].BitsPerPixel = values[1]; this.items[itemId].BitsPerPixel = values[1];
@ -516,7 +521,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
for (uint i = 0; i < itemCount; i++) for (uint i = 0; i < itemCount; i++)
{ {
uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead); uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
HeicItem? item = this.FindItemById(itemId); HeifItem? item = this.FindItemById(itemId);
if (version is 1 or 2) if (version is 1 or 2)
{ {
bytesRead++; bytesRead++;
@ -538,7 +543,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
ulong extentOffset = ReadUIntVariable(buffer, offsetSize, ref bytesRead); ulong extentOffset = ReadUIntVariable(buffer, offsetSize, ref bytesRead);
ulong extentLength = ReadUIntVariable(buffer, lengthSize, 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); item?.DataLocations.Add(loc);
} }
} }
@ -591,14 +596,14 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// FIXME: No HVC decoding yet, so parse only a JPEG thumbnail. // 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) if (thumbLink == null)
{ {
throw new NotImplementedException("No thumbnail found"); throw new NotImplementedException("No thumbnail found");
} }
HeicItem? thumbItem = this.FindItemById(thumbLink.SourceId); HeifItem? thumbItem = this.FindItemById(thumbLink.SourceId);
if (thumbItem == null || thumbItem.Type != Heic4CharCode.jpeg) if (thumbItem == null || thumbItem.Type != Heif4CharCode.jpeg)
{ {
throw new NotImplementedException("No HVC decoding implemented yet"); throw new NotImplementedException("No HVC decoding implemented yet");
} }
@ -610,8 +615,8 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
Span<byte> thumbSpan = thumbMemory.GetSpan(); Span<byte> thumbSpan = thumbMemory.GetSpan();
stream.Read(thumbSpan); stream.Read(thumbSpan);
HeicMetadata meta = this.metadata.GetHeicMetadata(); HeifMetadata meta = this.metadata.GetHeifMetadata();
meta.CompressionMethod = HeicCompressionMethod.LegacyJpeg; meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg;
return Image.Load<TPixel>(thumbSpan); 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); => this.items.FirstOrDefault(item => item.Id == itemId);
private static string ReadNullTerminatedString(Span<byte> span) private static string ReadNullTerminatedString(Span<byte> span)
@ -654,7 +659,7 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
return Encoding.UTF8.GetString(bytes); return Encoding.UTF8.GetString(bytes);
} }
private static string PrettyPrint(Heic4CharCode code) private static string PrettyPrint(Heif4CharCode code)
{ {
string? pretty = Enum.GetName(code); string? pretty = Enum.GetName(code);
if (string.IsNullOrEmpty(pretty)) 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced; namespace SixLabors.ImageSharp.Formats.Heif;
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary> /// <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> /// </summary>
public sealed class HeicEncoder : ImageEncoder public sealed class HeifEncoder : ImageEncoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) 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); 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <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> /// </summary>
internal sealed class HeicEncoderCore : IImageEncoderInternals internal sealed class HeifEncoderCore : IImageEncoderInternals
{ {
/// <summary> /// <summary>
/// The global configuration. /// The global configuration.
@ -22,14 +21,14 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
/// <summary> /// <summary>
/// The encoder with options. /// The encoder with options.
/// </summary> /// </summary>
private readonly HeicEncoder encoder; private readonly HeifEncoder encoder;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeicEncoderCore"/> class. /// Initializes a new instance of the <see cref="HeifEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="encoder">The encoder with options.</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.configuration = configuration;
this.encoder = encoder; this.encoder = encoder;
@ -49,8 +48,8 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
byte[] pixels = await CompressPixels(image, cancellationToken); byte[] pixels = await CompressPixels(image, cancellationToken);
List<HeicItem> items = new(); List<HeifItem> items = new();
List<HeicItemLink> links = new(); List<HeifItemLink> links = new();
GenerateItems(image, pixels, items, links); GenerateItems(image, pixels, items, links);
// Write out the generated header and pixels. // Write out the generated header and pixels.
@ -58,25 +57,28 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
this.WriteMetadataBox(items, links, stream); this.WriteMetadataBox(items, links, stream);
this.WriteMediaDataBox(pixels, stream); this.WriteMediaDataBox(pixels, stream);
stream.Flush(); 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> where TPixel : unmanaged, IPixel<TPixel>
{ {
HeicItem primaryItem = new(Heic4CharCode.jpeg, 1u); HeifItem primaryItem = new(Heif4CharCode.jpeg, 1u);
primaryItem.DataLocations.Add(new HeicLocation(0L, pixels.LongLength)); primaryItem.DataLocations.Add(new HeifLocation(0L, pixels.LongLength));
primaryItem.BitsPerPixel = 24; primaryItem.BitsPerPixel = 24;
primaryItem.ChannelCount = 3; primaryItem.ChannelCount = 3;
primaryItem.SetExtent(image.Size); primaryItem.SetExtent(image.Size);
items.Add(primaryItem); items.Add(primaryItem);
// Create a fake thumbnail, to make our own Decoder happy. // 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); thumbnail.DestinationIds.Add(1u);
links.Add(thumbnail); links.Add(thumbnail);
} }
private static int WriteBoxHeader(Span<byte> buffer, Heic4CharCode type) private static int WriteBoxHeader(Span<byte> buffer, Heif4CharCode type)
{ {
int bytesWritten = 0; int bytesWritten = 0;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 8U); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 8U);
@ -87,7 +89,7 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten; 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; int bytesWritten = 0;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 12); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 12);
@ -106,25 +108,25 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private void WriteFileTypeBox(Stream stream) private void WriteFileTypeBox(Stream stream)
{ {
Span<byte> buffer = stackalloc byte[24]; Span<byte> buffer = stackalloc byte[24];
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.ftyp); int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.ftyp);
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.heic); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.mif1); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.mif1);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.heic); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.heic);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten); BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten);
stream.Write(buffer); 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); using AutoExpandingMemory<byte> memory = new(this.configuration, 0x1000);
Span<byte> buffer = memory.GetSpan(12); 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 += WriteHandlerBox(memory, bytesWritten);
bytesWritten += WritePrimaryItemBox(memory, bytesWritten); bytesWritten += WritePrimaryItemBox(memory, bytesWritten);
bytesWritten += WriteItemInfoBox(memory, bytesWritten, items); bytesWritten += WriteItemInfoBox(memory, bytesWritten, items);
@ -141,10 +143,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private static int WriteHandlerBox(AutoExpandingMemory<byte> memory, int memoryOffset) private static int WriteHandlerBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{ {
Span<byte> buffer = memory.GetSpan(memoryOffset, 33); 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); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], 0);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heic4CharCode.pict); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)Heif4CharCode.pict);
bytesWritten += 4; bytesWritten += 4;
for (int i = 0; i < 13; i++) 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) private static int WritePrimaryItemBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{ {
Span<byte> buffer = memory.GetSpan(memoryOffset, 14); 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); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1);
bytesWritten += 2; bytesWritten += 2;
@ -166,16 +168,16 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten; 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)); 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); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)items.Count);
bytesWritten += 2; bytesWritten += 2;
foreach (HeicItem item in items) foreach (HeifItem item in items)
{ {
int itemLengthOffset = bytesWritten; 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); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id);
bytesWritten += 2; bytesWritten += 2;
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 0); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 0);
@ -191,11 +193,11 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten; 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)))); Span<byte> buffer = memory.GetSpan(memoryOffset, 12 + (links.Count * (12 + (items.Count * 2))));
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iref, 0, 0); int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iref, 0, 0);
foreach (HeicItemLink link in links) foreach (HeifItemLink link in links)
{ {
int itemLengthOffset = bytesWritten; int itemLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], link.Type); bytesWritten += WriteBoxHeader(buffer[bytesWritten..], link.Type);
@ -216,16 +218,16 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten; 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; const ushort numPropPerItem = 1;
Span<byte> buffer = memory.GetSpan(memoryOffset, 20); Span<byte> buffer = memory.GetSpan(memoryOffset, 20);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.iprp); int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.iprp);
// Write 'ipco' box // Write 'ipco' box
int ipcoLengthOffset = bytesWritten; int ipcoLengthOffset = bytesWritten;
bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heic4CharCode.ipco); bytesWritten += WriteBoxHeader(buffer[bytesWritten..], Heif4CharCode.ipco);
foreach (HeicItem item in items) foreach (HeifItem item in items)
{ {
bytesWritten += WriteSpatialExtentPropertyBox(memory, memoryOffset + bytesWritten, item); bytesWritten += WriteSpatialExtentPropertyBox(memory, memoryOffset + bytesWritten, item);
} }
@ -235,11 +237,11 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
// Write 'ipma' box // Write 'ipma' box
int ipmaLengthOffset = bytesWritten; 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)); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)(items.Count * numPropPerItem));
bytesWritten += 4; bytesWritten += 4;
ushort propIndex = 0; ushort propIndex = 0;
foreach (HeicItem item in items) foreach (HeifItem item in items)
{ {
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)item.Id);
bytesWritten += 2; bytesWritten += 2;
@ -256,10 +258,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
return bytesWritten; 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); 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); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Width);
bytesWritten += 4; bytesWritten += 4;
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)item.Extent.Height); 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) private static int WriteItemDataBox(AutoExpandingMemory<byte> memory, int memoryOffset)
{ {
Span<byte> buffer = memory.GetSpan(memoryOffset, 10); Span<byte> buffer = memory.GetSpan(memoryOffset, 10);
int bytesWritten = WriteBoxHeader(buffer, Heic4CharCode.idat); int bytesWritten = WriteBoxHeader(buffer, Heif4CharCode.idat);
BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten); BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)bytesWritten);
return 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)); 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++] = 0x44;
buffer[bytesWritten++] = 0; buffer[bytesWritten++] = 0;
BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], 1);
@ -293,10 +295,10 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
buffer[bytesWritten++] = 0; 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()); BinaryPrimitives.WriteUInt16BigEndian(buffer[bytesWritten..], (ushort)itemLocs.Count());
bytesWritten += 2; bytesWritten += 2;
foreach (HeicLocation loc in itemLocs) foreach (HeifLocation loc in itemLocs)
{ {
BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)loc.Offset); BinaryPrimitives.WriteUInt32BigEndian(buffer[bytesWritten..], (uint)loc.Offset);
bytesWritten += 4; bytesWritten += 4;
@ -311,7 +313,7 @@ internal sealed class HeicEncoderCore : IImageEncoderInternals
private void WriteMediaDataBox(Span<byte> data, Stream stream) private void WriteMediaDataBox(Span<byte> data, Stream stream)
{ {
Span<byte> buf = stackalloc byte[12]; 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)); BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)(data.Length + bytesWritten));
stream.Write(buf[..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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Registers the image encoders, decoders and mime type detectors for the HEIC format. /// Registers the image encoders, decoders and mime type detectors for the HEIC format.
/// </summary> /// </summary>
public sealed class HeicFormat : IImageFormat<HeicMetadata> public sealed class HeifFormat : IImageFormat<HeifMetadata>
{ {
private HeicFormat() private HeifFormat()
{ {
} }
/// <summary> /// <summary>
/// Gets the shared instance. /// Gets the shared instance.
/// </summary> /// </summary>
public static HeicFormat Instance { get; } = new(); public static HeifFormat Instance { get; } = new();
/// <inheritdoc/> /// <inheritdoc/>
public string Name => "HEIC"; public string Name => "HEIC";
@ -24,11 +24,11 @@ public sealed class HeicFormat : IImageFormat<HeicMetadata>
public string DefaultMimeType => "image/heif"; public string DefaultMimeType => "image/heif";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> MimeTypes => HeicConstants.MimeTypes; public IEnumerable<string> MimeTypes => HeifConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> FileExtensions => HeicConstants.FileExtensions; public IEnumerable<string> FileExtensions => HeifConstants.FileExtensions;
/// <inheritdoc/> /// <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.Buffers.Binary;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Detects HEIC file headers. /// Detects HEIF file headers.
/// </summary> /// </summary>
public sealed class HeicImageFormatDetector : IImageFormatDetector public sealed class HeifImageFormatDetector : IImageFormatDetector
{ {
/// <inheritdoc/> /// <inheritdoc/>
public int HeaderSize => 12; public int HeaderSize => 12;
@ -17,14 +17,14 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector
/// <inheritdoc/> /// <inheritdoc/>
public bool TryDetectFormat(ReadOnlySpan<byte> header, [NotNullWhen(true)] out IImageFormat? format) 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; return format != null;
} }
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header) 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)); 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Provides definition for a HEIC Item. /// Provides definition for a HEIF Item.
/// </summary> /// </summary>
public class HeicItem(Heic4CharCode type, uint id) public class HeifItem(Heif4CharCode type, uint id)
{ {
/// <summary> /// <summary>
/// Gets the ID of this Item. /// Gets the ID of this Item.
@ -16,7 +16,7 @@ public class HeicItem(Heic4CharCode type, uint id)
/// <summary> /// <summary>
/// Gets the type of this Item. /// Gets the type of this Item.
/// </summary> /// </summary>
public Heic4CharCode Type { get; } = type; public Heif4CharCode Type { get; } = type;
/// <summary> /// <summary>
/// Gets or sets the name of this item. /// Gets or sets the name of this item.
@ -71,7 +71,7 @@ public class HeicItem(Heic4CharCode type, uint id)
/// <summary> /// <summary>
/// Gets the list of data locations for this item. /// Gets the list of data locations for this item.
/// </summary> /// </summary>
public List<HeicLocation> DataLocations { get; } = new List<HeicLocation>(); public List<HeifLocation> DataLocations { get; } = new List<HeifLocation>();
/// <summary> /// <summary>
/// Set the image extent. /// 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Link between <see cref="HeicItem"/> instances within the same HEIC file. /// Link between <see cref="HeifItem"/> instances within the same HEIF file.
/// </summary> /// </summary>
public class HeicItemLink(Heic4CharCode type, uint sourceId) public class HeifItemLink(Heif4CharCode type, uint sourceId)
{ {
/// <summary> /// <summary>
/// Gets the type of link. /// Gets the type of link.
/// </summary> /// </summary>
public Heic4CharCode Type { get; } = type; public Heif4CharCode Type { get; } = type;
/// <summary> /// <summary>
/// Gets the ID of the source item of this link. /// 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Location within the file of an <see cref="HeicItem"/>. /// Location within the file of an <see cref="HeifItem"/>.
/// </summary> /// </summary>
public class HeicLocation(long offset, long length) public class HeifLocation(long offset, long length)
{ {
/// <summary> /// <summary>
/// Gets the file offset of this location. /// 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Provides HEIC specific metadata information for the image. /// Provides HEIF specific metadata information for the image.
/// </summary> /// </summary>
public class HeicMetadata : IDeepCloneable public class HeifMetadata : IDeepCloneable
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeicMetadata"/> class. /// Initializes a new instance of the <see cref="HeifMetadata"/> class.
/// </summary> /// </summary>
public HeicMetadata() public HeifMetadata()
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeicMetadata"/> class. /// Initializes a new instance of the <see cref="HeifMetadata"/> class.
/// </summary> /// </summary>
/// <param name="other">The metadata to create an instance from.</param> /// <param name="other">The metadata to create an instance from.</param>
private HeicMetadata(HeicMetadata other) private HeifMetadata(HeifMetadata other)
=> this.CompressionMethod = other.CompressionMethod; => this.CompressionMethod = other.CompressionMethod;
/// <summary> /// <summary>
/// Gets or sets the compression method used for the primary frame. /// Gets or sets the compression method used for the primary frame.
/// </summary> /// </summary>
public HeicCompressionMethod CompressionMethod { get; set; } public HeifCompressionMethod CompressionMethod { get; set; }
/// <inheritdoc/> /// <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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic; namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary> /// <summary>
/// Configuration options for use during HEIC encoding. /// Configuration options for use during HEIF encoding.
/// </summary> /// </summary>
internal interface IHeicEncoderOptions internal interface IHeicEncoderOptions
{ {

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic; using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;
@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp;
public static partial class MetadataExtensions public static partial class MetadataExtensions
{ {
/// <summary> /// <summary>
/// Gets the pbm format specific metadata for the image. /// Gets the HEIF format specific metadata for the image.
/// </summary> /// </summary>
/// <param name="metadata">The metadata this method extends.</param> /// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="HeicMetadata"/>.</returns> /// <returns>The <see cref="HeifMetadata"/>.</returns>
public static HeicMetadata GetHeicMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(HeicFormat.Instance); 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>
<ItemGroup> <ItemGroup>
<Compile Update="Formats\Heic\Heic4CharCode.cs"> <Compile Update="Formats\Heif\Heif4CharCode.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Heic4CharCode.tt</DependentUpon> <DependentUpon>Heif4CharCode.tt</DependentUpon>
</Compile> </Compile>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs"> <Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
@ -155,9 +155,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Formats\Heic\Heic4CharCode.tt"> <None Update="Formats\Heif\Heif4CharCode.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Heic4CharCode.cs</LastGenOutput> <LastGenOutput>Heif4CharCode.cs</LastGenOutput>
</None> </None>
<None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt"> <None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator> <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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic; using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.Formats.Heic; namespace SixLabors.ImageSharp.Tests.Formats.Heif;
[Trait("Format", "Heic")] [Trait("Format", "Heif")]
[ValidateDisposedMemoryAllocations] [ValidateDisposedMemoryAllocations]
public class HeicDecoderTests public class HeifDecoderTests
{ {
[Theory] [Theory]
[InlineData(TestImages.Heic.Image1, HeicCompressionMethod.Hevc)] [InlineData(TestImages.Heif.Image1, HeifCompressionMethod.Hevc)]
[InlineData(TestImages.Heic.Sample640x427, HeicCompressionMethod.Hevc)] [InlineData(TestImages.Heif.Sample640x427, HeifCompressionMethod.Hevc)]
[InlineData(TestImages.Heic.FujiFilmHif, HeicCompressionMethod.LegacyJpeg)] [InlineData(TestImages.Heif.FujiFilmHif, HeifCompressionMethod.LegacyJpeg)]
[InlineData(TestImages.Heic.IrvineAvif, HeicCompressionMethod.Av1)] [InlineData(TestImages.Heif.IrvineAvif, HeifCompressionMethod.Av1)]
public void Identify(string imagePath, HeicCompressionMethod compressionMethod) public void Identify(string imagePath, HeifCompressionMethod compressionMethod)
{ {
TestFile testFile = TestFile.Create(imagePath); TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false); using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo imageInfo = Image.Identify(stream); ImageInfo imageInfo = Image.Identify(stream);
HeicMetadata heicMetadata = imageInfo.Metadata.GetHeicMetadata(); HeifMetadata heicMetadata = imageInfo.Metadata.GetHeifMetadata();
Assert.NotNull(imageInfo); Assert.NotNull(imageInfo);
Assert.Equal(HeicFormat.Instance, imageInfo.Metadata.DecodedImageFormat); Assert.Equal(HeifFormat.Instance, imageInfo.Metadata.DecodedImageFormat);
Assert.Equal(compressionMethod, heicMetadata.CompressionMethod); Assert.Equal(compressionMethod, heicMetadata.CompressionMethod);
} }
[Theory] [Theory]
[WithFile(TestImages.Heic.FujiFilmHif, PixelTypes.Rgba32)] [WithFile(TestImages.Heif.FujiFilmHif, PixelTypes.Rgba32)]
public void Decode<TPixel>(TestImageProvider<TPixel> provider) public void Decode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
HeicMetadata heicMetadata = image.Metadata.GetHeicMetadata(); HeifMetadata heicMetadata = image.Metadata.GetHeifMetadata();
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heic; using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; 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] [ValidateDisposedMemoryAllocations]
public class HeicEncoderTests public class HeifEncoderTests
{ {
[Theory] [Theory]
[WithFile(TestImages.Heic.Sample640x427, PixelTypes.Rgba32)] [WithFile(TestImages.Heif.Sample640x427, PixelTypes.Rgba32, HeifCompressionMethod.LegacyJpeg)]
public static void Encode<TPixel>(TestImageProvider<TPixel> provider) public static void Encode<TPixel>(TestImageProvider<TPixel> provider, HeifCompressionMethod compressionMethod)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(new MagickReferenceDecoder()); using Image<TPixel> image = provider.GetImage(new MagickReferenceDecoder());
using MemoryStream stream = new(); using MemoryStream stream = new();
HeicEncoder encoder = new(); HeifEncoder encoder = new();
image.Save(stream, encoder); image.Save(stream, encoder);
stream.Position = 0; stream.Position = 0;
using Image<TPixel> encodedImage = Image.Load<TPixel>(stream); using Image<TPixel> encodedImage = Image.Load<TPixel>(stream);
HeicMetadata heicMetadata = encodedImage.Metadata.GetHeicMetadata(); HeifMetadata heifMetadata = encodedImage.Metadata.GetHeifMetadata();
ImageComparer.Exact.CompareImages(image, encodedImage); 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;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga; 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<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<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<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<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<TiffEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<WebpEncoder>().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<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<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<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<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<TiffDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<WebpDecoder>().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 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 Image1 = "Heif/image1.heic";
public const string Image2 = "Heic/image2.heic"; public const string Image2 = "Heif/image2.heic";
public const string Image3 = "Heic/image3.heic"; public const string Image3 = "Heif/image3.heic";
public const string Image4 = "Heic/image4.heic"; public const string Image4 = "Heif/image4.heic";
public const string Sample640x427 = "Heic/dwsample-heic-640.heic"; public const string Sample640x427 = "Heif/dwsample-heic-640.heic";
// Downloaded from: https://github.com/draktable-org/darktable/issues/14473 // 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 // 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;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Heic;
using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi; using SixLabors.ImageSharp.Formats.Qoi;
@ -60,7 +60,7 @@ public static partial class TestEnvironment
Configuration cfg = new( Configuration cfg = new(
new JpegConfigurationModule(), new JpegConfigurationModule(),
new GifConfigurationModule(), new GifConfigurationModule(),
new HeicConfigurationModule(), new HeifConfigurationModule(),
new PbmConfigurationModule(), new PbmConfigurationModule(),
new TgaConfigurationModule(), new TgaConfigurationModule(),
new WebpConfigurationModule(), 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