Browse Source

Add AniChunk

pull/2899/head
Betta_Fish 1 year ago
parent
commit
1a51a5c23b
  1. 38
      src/ImageSharp/Formats/Ani/AniChunk.cs
  2. 11
      src/ImageSharp/Formats/Ani/AniChunkType.cs
  3. 21
      src/ImageSharp/Formats/Ani/AniConstants.cs
  4. 196
      src/ImageSharp/Formats/Ani/AniDecoderCore.cs
  5. 34
      src/ImageSharp/Formats/Ani/AniFormat.cs
  6. 20
      src/ImageSharp/Formats/Ani/AniMetadata.cs
  7. 2
      src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs
  8. 21
      src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs

38
src/ImageSharp/Formats/Ani/AniChunk.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Formats.Png;
namespace SixLabors.ImageSharp.Formats.Ani;
internal readonly struct AniChunk
{
public AniChunk(int length, AniChunkType type, IMemoryOwner<byte> data = null)
{
this.Length = length;
this.Type = type;
this.Data = data;
}
/// <summary>
/// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the chunk type.
/// The value is the equal to the UInt32BigEndian encoding of its 4 ASCII characters.
/// </summary>
public AniChunkType Type { get; }
/// <summary>
/// Gets the data bytes appropriate to the chunk type, if any.
/// This field can be of zero length or null.
/// </summary>
public IMemoryOwner<byte> Data { get; }
}

11
src/ImageSharp/Formats/Ani/AniChunkType.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Ani;
internal enum AniChunkType : uint
{
Seq = 0x73_65_71_20,
Rate = 0x72_61_74_65,
List = 0x4C_49_53_54
}

21
src/ImageSharp/Formats/Ani/AniConstants.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Ani;
internal static class AniConstants
{
/// <summary>
/// The list of mime types that equate to an ani.
/// </summary>
/// <remarks>
/// See <see href="https://en.wikipedia.org/wiki/ICO_(file_format)#MIME_type"/>
/// </remarks>
public static readonly IEnumerable<string> MimeTypes = [];
/// <summary>
/// The list of file extensions that equate to an ani.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = ["ani"];
}

196
src/ImageSharp/Formats/Ani/AniDecoderCore.cs

@ -1,50 +1,206 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Icon;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory.Internals;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Ani;
internal class AniDecoderCore : ImageDecoderCore
{
/// <summary>
/// The general decoder options.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The stream to decode from.
/// </summary>
private BufferedReadStream currentStream = null!;
private AniHeader header;
public AniDecoderCore(DecoderOptions options)
: base(options)
{
}
: base(options) => this.configuration = options.Configuration;
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ReadHeader(stream);
Span<byte> buffer = stackalloc byte[4];
_ = stream.Read(buffer);
uint type = BitConverter.ToUInt32(buffer);
switch (type)
this.currentStream = stream;
this.ReadHeader();
ImageMetadata metadata = new();
AniMetadata aniMetadata = metadata.GetAniMetadata();
Image<TPixel>? image = null;
Span<byte> buffer = stackalloc byte[20];
try
{
case 0x73_65_71_20: // seq
break;
case 0x72_61_74_65: // rate
break;
case 0x4C_49_53_54: // list
break;
default:
break;
while (this.TryReadChunk(buffer, out AniChunk chunk))
{
try
{
switch (chunk.Type)
{
case AniChunkType.Seq:
break;
case AniChunkType.Rate:
break;
case AniChunkType.List:
break;
default:
break;
}
}
finally
{
chunk.Data?.Dispose();
}
}
}
catch
{
image?.Dispose();
throw;
}
throw new NotImplementedException();
}
private void ReadSeq()
{
}
private bool TryReadChunk(Span<byte> buffer, out AniChunk chunk)
{
if (!this.TryReadChunkLength(buffer, out int length))
{
// IEND
chunk = default;
return false;
}
while (length < 0)
{
// Not a valid chunk so try again until we reach a known chunk.
if (!this.TryReadChunkLength(buffer, out length))
{
// IEND
chunk = default;
return false;
}
}
AniChunkType type = this.ReadChunkType(buffer);
// A chunk might report a length that exceeds the length of the stream.
// Take the minimum of the two values to ensure we don't read past the end of the stream.
long position = this.currentStream.Position;
chunk = new AniChunk(
length: (int)Math.Min(length, this.currentStream.Length - position),
type: type,
data: this.ReadChunkData(length));
return true;
}
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
private void ReadHeader(Stream stream)
private void ReadHeader()
{
// Skip the identifier
stream.Skip(12);
this.currentStream.Skip(12);
Span<byte> buffer = stackalloc byte[36];
_ = stream.Read(buffer);
AniHeader header = AniHeader.Parse(buffer);
_ = this.currentStream.Read(buffer);
this.header = AniHeader.Parse(buffer);
}
private void ReadSeq(Stream stream)
{
Span<byte> buffer = stackalloc byte[4];
int length = BinaryPrimitives.ReadInt32BigEndian(buffer);
}
/// <summary>
/// Attempts to read the length of the next chunk.
/// </summary>
/// <param name="buffer">Temporary buffer.</param>
/// <param name="result">The result length. If the return type is <see langword="false"/> this parameter is passed uninitialized.</param>
/// <returns>
/// Whether the length was read.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
private bool TryReadChunkLength(Span<byte> buffer, out int result)
{
if (this.currentStream.Read(buffer, 0, 4) == 4)
{
result = BinaryPrimitives.ReadInt32BigEndian(buffer);
return true;
}
result = 0;
return false;
}
/// <summary>
/// Identifies the chunk type from the chunk.
/// </summary>
/// <param name="buffer">Temporary buffer.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
private AniChunkType ReadChunkType(Span<byte> buffer)
{
if (this.currentStream.Read(buffer, 0, 4) == 4)
{
return (AniChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
PngThrowHelper.ThrowInvalidChunkType();
// The IDE cannot detect the throw here.
return default;
}
/// <summary>
/// Reads the chunk data from the stream.
/// </summary>
/// <param name="length">The length of the chunk data to read.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private IMemoryOwner<byte> ReadChunkData(int length)
{
if (length == 0)
{
return new BasicArrayBuffer<byte>([]);
}
// We rent the buffer here to return it afterwards in Decode()
// We don't want to throw a degenerated memory exception here as we want to allow partial decoding
// so limit the length.
length = (int)Math.Min(length, this.currentStream.Length - this.currentStream.Position);
IMemoryOwner<byte> buffer = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
this.currentStream.Read(buffer.GetSpan(), 0, length);
return buffer;
}
}

34
src/ImageSharp/Formats/Ani/AniFormat.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Ico;
namespace SixLabors.ImageSharp.Formats.Ani;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
public sealed class AniFormat : IImageFormat<AniMetadata>
{
/// <summary>
/// Gets the shared instance.
/// </summary>
public static AniFormat Instance { get; } = new();
/// <inheritdoc/>
public AniMetadata CreateDefaultFormatMetadata() => throw new NotImplementedException();
/// <inheritdoc/>
public string Name => "ANI";
/// <inheritdoc/>
public string DefaultMimeType { get; }
/// <inheritdoc/>
public IEnumerable<string> MimeTypes { get; }
/// <inheritdoc/>
public IEnumerable<string> FileExtensions { get; }
}

20
src/ImageSharp/Formats/Ani/AniMetadata.cs

@ -1,23 +1,39 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Ico;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Ani;
internal class AniMetadata : IFormatMetadata<AniMetadata>
/// <summary>
/// Provides Ani specific metadata information for the image.
/// </summary>
public class AniMetadata : IFormatMetadata<AniMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="AniMetadata"/> class.
/// </summary>
public AniMetadata()
{
}
/// <inheritdoc/>
public static AniMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException();
/// <inheritdoc/>
public void AfterImageApply<TPixel>(Image<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel> => throw new NotImplementedException();
/// <inheritdoc/>
public IDeepCloneable DeepClone() => throw new NotImplementedException();
/// <inheritdoc/>
public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
/// <inheritdoc/>
public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException();
/// <inheritdoc/>
AniMetadata IDeepCloneable<AniMetadata>.DeepClone() => throw new NotImplementedException();
}

2
src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs

@ -17,7 +17,7 @@ internal enum BmpRenderingIntent
/// <summary>
/// Maintains saturation. Used for business charts and other situations in which undithered colors are required.
/// </summary>
LCS_GM_BUSINESS = 1,
LCS_GM_BUSINESS = 1,
/// <summary>
/// Maintains colorimetric match. Used for graphic designs and named colors.

21
src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs

@ -14,6 +14,7 @@ using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Ani;
namespace SixLabors.ImageSharp;
@ -102,6 +103,26 @@ public static class ImageMetadataExtensions
/// <returns>The new <see cref="IcoMetadata"/></returns>
public static IcoMetadata CloneIcoMetadata(this ImageMetadata source) => source.CloneFormatMetadata(IcoFormat.Instance);
/// <summary>
/// Gets the <see cref="AniMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="AniMetadata"/>
/// </returns>
public static AniMetadata GetAniMetadata(this ImageMetadata source) => source.GetFormatMetadata(AniFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="IcoMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetIcoMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="IcoMetadata"/></returns>
public static AniMetadata CloneAniMetadata(this ImageMetadata source) => source.CloneFormatMetadata(AniFormat.Instance);
/// <summary>
/// Gets the <see cref="JpegMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata

Loading…
Cancel
Save