Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/ImageSharp/Common/InlineArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ internal struct InlineArray8<T>
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 14 elements.
/// </summary>
[InlineArray(14)]
internal struct InlineArray14<T>
{
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 16 elements.
/// </summary>
Expand All @@ -35,4 +44,47 @@ internal struct InlineArray16<T>
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 18 elements.
/// </summary>
[InlineArray(18)]
internal struct InlineArray18<T>
{
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 19 elements.
/// </summary>
[InlineArray(19)]
internal struct InlineArray19<T>
{
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 26 elements.
/// </summary>
[InlineArray(26)]
internal struct InlineArray26<T>
{
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 36 elements.
/// </summary>
[InlineArray(36)]
internal struct InlineArray36<T>
{
private T t;
}

/// <summary>
/// Represents a safe, fixed sized buffer of 256 elements.
/// </summary>
[InlineArray(256)]
internal struct InlineArray256<T>
{
private T t;
}
2 changes: 1 addition & 1 deletion src/ImageSharp/Common/InlineArray.tt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp;
<#GenerateInlineArrays();#>

<#+
private static int[] Lengths = [4, 8, 16 ];
private static int[] Lengths = [4, 8, 14, 16, 18, 19, 26, 36, 256];
Comment thread
JimBobSquarePants marked this conversation as resolved.

void GenerateInlineArrays()
{
Expand Down
15 changes: 8 additions & 7 deletions src/ImageSharp/Compression/Zlib/ChunkedReadStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ public override int ReadByte()
}

/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count)
public override int Read(byte[] buffer, int offset, int count) => this.Read(buffer.AsSpan(offset, count));

/// <inheritdoc/>
public override int Read(Span<byte> buffer)
{
// Decrement currentDataRemaining only by bytes actually returned by
// innerStream.Read; a short read otherwise underflows the segment
// counter and triggers getData() before the segment is truly drained.
// Decrement currentDataRemaining only by bytes actually returned by innerStream.Read; a short read otherwise advances segments too early.
int totalBytesRead = 0;
while (totalBytesRead < count)
while (totalBytesRead < buffer.Length)
{
if (this.currentDataRemaining is 0)
{
Expand All @@ -94,8 +95,8 @@ public override int Read(byte[] buffer, int offset, int count)
}
}

int bytesToRead = Math.Min(count - totalBytesRead, this.currentDataRemaining);
int bytesRead = this.innerStream.Read(buffer, offset + totalBytesRead, bytesToRead);
int bytesToRead = Math.Min(buffer.Length - totalBytesRead, this.currentDataRemaining);
int bytesRead = this.innerStream.Read(buffer.Slice(totalBytesRead, bytesToRead));
if (bytesRead is 0)
{
break;
Expand Down
11 changes: 3 additions & 8 deletions src/ImageSharp/Compression/Zlib/ZlibInflateReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib;
/// </summary>
internal sealed class ZlibInflateReader : IDisposable
{
/// <summary>
/// Used to read the Adler-32 and Crc-32 checksums.
/// We don't actually use this for anything so it doesn't
/// have to be threadsafe.
/// </summary>
private static readonly byte[] ChecksumBuffer = new byte[4];

private readonly ChunkedReadStream segmentStream;

public ZlibInflateReader(BufferedReadStream innerStream)
Expand Down Expand Up @@ -112,7 +105,9 @@ private bool InitializeInflateStream(bool isCriticalChunk)
{
// We don't need this for inflate so simply skip by the next four bytes.
// https://tools.ietf.org/html/rfc1950#page-6
if (this.segmentStream.Read(ChecksumBuffer, 0, 4) != 4)
InlineArray4<byte> checksumBuffer = default;

if (this.segmentStream.Read(checksumBuffer) != 4)
{
return false;
}
Expand Down
5 changes: 4 additions & 1 deletion src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Ani;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Cur;
using SixLabors.ImageSharp.Formats.Exr;
Expand Down Expand Up @@ -224,6 +225,7 @@ public void Configure(IImageFormatConfigurationModule configuration)
/// <see cref="WebpConfigurationModule"/>.
/// <see cref="ExrConfigurationModule"/>.
/// <see cref="QoiConfigurationModule"/>.
/// <see cref="AniConfigurationModule"/>.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance() => new(
Expand All @@ -238,5 +240,6 @@ public void Configure(IImageFormatConfigurationModule configuration)
new ExrConfigurationModule(),
new QoiConfigurationModule(),
new IcoConfigurationModule(),
new CurConfigurationModule());
new CurConfigurationModule(),
new AniConfigurationModule());
}
73 changes: 73 additions & 0 deletions src/ImageSharp/Formats/Ani/AniChunkType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Ani;

/// <summary>
/// Identifies top-level ANI RIFF chunks.
/// </summary>
internal enum AniChunkType : uint
{
/// <summary>
/// The animation header chunk, "anih".
/// </summary>
Header = 0x68_69_6E_61,

/// <summary>
/// The frame sequence chunk, "seq ".
/// </summary>
Sequence = 0x20_71_65_73,

/// <summary>
/// The per-step display-rate chunk, "rate".
/// </summary>
Rate = 0x65_74_61_72,

/// <summary>
/// A RIFF list chunk, "LIST".
/// </summary>
List = 0x54_53_49_4C
}

/// <summary>
/// Identifies ANI RIFF list types.
/// </summary>
internal enum AniListType : uint
{
/// <summary>
/// The information list, "INFO".
/// </summary>
Info = 0x4F_46_4E_49,

/// <summary>
/// The embedded frame-resource list, "fram".
/// </summary>
Frames = 0x6D_61_72_66
}

/// <summary>
/// Identifies chunks stored in an ANI information list.
/// </summary>
internal enum AniInfoChunkType : uint
{
/// <summary>
/// The animation name, "INAM".
/// </summary>
Name = 0x4D_41_4E_49,

/// <summary>
/// The animation artist, "IART".
/// </summary>
Artist = 0x54_52_41_49
}

/// <summary>
/// Identifies chunks stored in an ANI frame list.
/// </summary>
internal enum AniFrameChunkType : uint
{
/// <summary>
/// An embedded frame resource, "icon".
/// </summary>
Icon = 0x6E_6F_63_69
}
25 changes: 25 additions & 0 deletions src/ImageSharp/Formats/Ani/AniConfigurationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Ani;

/// <summary>
/// Registers the image encoder, decoder, and format detector for the ANI format.
/// </summary>
public sealed class AniConfigurationModule : IImageFormatConfigurationModule
{
/// <summary>
/// Initializes a new instance of the <see cref="AniConfigurationModule"/> class.
/// </summary>
public AniConfigurationModule()
{
}

/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(AniFormat.Instance, new AniEncoder());
configuration.ImageFormatsManager.SetDecoder(AniFormat.Instance, AniDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new AniImageFormatDetector());
}
}
54 changes: 54 additions & 0 deletions src/ImageSharp/Formats/Ani/AniConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Ani;

/// <summary>
/// Defines constants used by the ANI format.
/// </summary>
internal static class AniConstants
{
/// <summary>
/// The number of bytes in the RIFF identifier, size, and form type.
/// </summary>
public const int RiffHeaderSize = 12;

/// <summary>
/// The number of bytes in a RIFF chunk identifier and size.
/// </summary>
public const int ChunkHeaderSize = 8;

/// <summary>
/// The number of bytes required to identify an embedded ICO or CUR resource.
/// </summary>
public const int IconDirHeaderSize = 6;

/// <summary>
/// The maximum number of bytes retained from an ancillary chunk.
/// </summary>
/// <remarks>
/// Control arrays and information strings come from untrusted input. Bounding them independently of the allocator
/// prevents a physically large RIFF chunk from consuming an unreasonable amount of memory.
/// </remarks>
public const int MaxAncillaryChunkSize = 8 * 1024 * 1024;

/// <summary>
/// The list of MIME types that identify ANI data.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = ["application/x-navi-animation"];

/// <summary>
/// The list of file extensions that identify ANI data.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = ["ani"];

/// <summary>
/// Gets the RIFF container identifier.
/// </summary>
public static ReadOnlySpan<byte> RiffFourCc => "RIFF"u8;

/// <summary>
/// Gets the ANI RIFF form type.
/// </summary>
public static ReadOnlySpan<byte> AniFormTypeFourCc => "ACON"u8;
}
52 changes: 52 additions & 0 deletions src/ImageSharp/Formats/Ani/AniDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.ImageSharp.PixelFormats;

namespace SixLabors.ImageSharp.Formats.Ani;

/// <summary>
/// Decodes Windows animated cursor images.
/// </summary>
public sealed class AniDecoder : ImageDecoder
{
/// <summary>
/// Prevents a default instance of the <see cref="AniDecoder"/> class from being created.
/// </summary>
private AniDecoder()
{
}

/// <summary>
/// Gets the shared instance.
/// </summary>
public static AniDecoder Instance { get; } = new();

/// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

using AniDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);

ScaleToTargetSize(options, image);

return image;
}

/// <inheritdoc/>
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);

/// <inheritdoc/>
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

using AniDecoderCore decoder = new(options);
return decoder.Identify(options.Configuration, stream, cancellationToken);
}
}
Loading
Loading