Browse Source

Fix simple issues in review

pull/2511/head
Poker 3 years ago
parent
commit
64a0ff08e9
No known key found for this signature in database GPG Key ID: 720AFAD63099D9CB
  1. 2
      src/ImageSharp/Configuration.cs
  2. 4
      src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs
  3. 22
      src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
  4. 9
      src/ImageSharp/Formats/Png/MetadataExtensions.cs
  5. 2
      src/ImageSharp/Formats/Png/PngBlendOperation.cs
  6. 21
      src/ImageSharp/Formats/Png/PngConstants.cs
  7. 66
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  8. 2
      src/ImageSharp/Formats/Png/PngDisposeOperation.cs
  9. 5
      src/ImageSharp/Formats/Png/PngEncoder.cs
  10. 41
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  11. 4
      src/ImageSharp/Formats/Png/PngFormat.cs
  12. 20
      src/ImageSharp/Formats/Png/PngFrameMetadata.cs
  13. 1
      src/ImageSharp/ImageSharp.csproj

2
src/ImageSharp/Configuration.cs

@ -43,7 +43,7 @@ public sealed class Configuration
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary> /// </summary>
/// <param name="configurationModules">A collection of configuration modules to register.</param> /// <param name="configurationModules">A collection of configuration modules to register.</param>
public Configuration(params IImageFormatConfigurationModule[]? configurationModules) public Configuration(params IImageFormatConfigurationModule[] configurationModules)
{ {
if (configurationModules != null) if (configurationModules != null)
{ {

4
src/ImageSharp/Formats/Png/Chunks/APngAnimationControl.cs → src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs

@ -5,7 +5,7 @@ using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Png.Chunks; namespace SixLabors.ImageSharp.Formats.Png.Chunks;
internal record APngAnimationControl( internal record AnimationControl(
int NumberFrames, int NumberFrames,
int NumberPlays) int NumberPlays)
{ {
@ -36,7 +36,7 @@ internal record APngAnimationControl(
/// </summary> /// </summary>
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>The parsed acTL.</returns> /// <returns>The parsed acTL.</returns>
public static APngAnimationControl Parse(ReadOnlySpan<byte> data) public static AnimationControl Parse(ReadOnlySpan<byte> data)
=> new( => new(
NumberFrames: BinaryPrimitives.ReadInt32BigEndian(data[..4]), NumberFrames: BinaryPrimitives.ReadInt32BigEndian(data[..4]),
NumberPlays: BinaryPrimitives.ReadInt32BigEndian(data[4..8])); NumberPlays: BinaryPrimitives.ReadInt32BigEndian(data[4..8]));

22
src/ImageSharp/Formats/Png/Chunks/APngFrameControl.cs → src/ImageSharp/Formats/Png/Chunks/FrameControl.cs

@ -5,11 +5,11 @@ using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Png.Chunks; namespace SixLabors.ImageSharp.Formats.Png.Chunks;
internal readonly struct APngFrameControl internal readonly struct FrameControl
{ {
public const int Size = 26; public const int Size = 26;
public APngFrameControl( public FrameControl(
int sequenceNumber, int sequenceNumber,
int width, int width,
int height, int height,
@ -17,8 +17,8 @@ internal readonly struct APngFrameControl
int yOffset, int yOffset,
short delayNumber, short delayNumber,
short delayDenominator, short delayDenominator,
APngDisposeOperation disposeOperation, PngDisposeOperation disposeOperation,
APngBlendOperation blendOperation) PngBlendOperation blendOperation)
{ {
this.SequenceNumber = sequenceNumber; this.SequenceNumber = sequenceNumber;
this.Width = width; this.Width = width;
@ -69,12 +69,12 @@ internal readonly struct APngFrameControl
/// <summary> /// <summary>
/// Gets the type of frame area disposal to be done after rendering this frame /// Gets the type of frame area disposal to be done after rendering this frame
/// </summary> /// </summary>
public APngDisposeOperation DisposeOperation { get; } public PngDisposeOperation DisposeOperation { get; }
/// <summary> /// <summary>
/// Gets the type of frame area rendering for this frame /// Gets the type of frame area rendering for this frame
/// </summary> /// </summary>
public APngBlendOperation BlendOperation { get; } public PngBlendOperation BlendOperation { get; }
/// <summary> /// <summary>
/// Validates the APng fcTL. /// Validates the APng fcTL.
@ -120,9 +120,9 @@ internal readonly struct APngFrameControl
/// </summary> /// </summary>
/// <param name="frameMetadata">The metadata to parse.</param> /// <param name="frameMetadata">The metadata to parse.</param>
/// <param name="sequenceNumber">Sequence number.</param> /// <param name="sequenceNumber">Sequence number.</param>
public static APngFrameControl FromMetadata(APngFrameMetadata frameMetadata, int sequenceNumber) public static FrameControl FromMetadata(PngFrameMetadata frameMetadata, int sequenceNumber)
{ {
APngFrameControl fcTL = new( FrameControl fcTL = new(
sequenceNumber, sequenceNumber,
frameMetadata.Width, frameMetadata.Width,
frameMetadata.Height, frameMetadata.Height,
@ -158,7 +158,7 @@ internal readonly struct APngFrameControl
/// </summary> /// </summary>
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>The parsed fcTL.</returns> /// <returns>The parsed fcTL.</returns>
public static APngFrameControl Parse(ReadOnlySpan<byte> data) public static FrameControl Parse(ReadOnlySpan<byte> data)
=> new( => new(
sequenceNumber: BinaryPrimitives.ReadInt32BigEndian(data[..4]), sequenceNumber: BinaryPrimitives.ReadInt32BigEndian(data[..4]),
width: BinaryPrimitives.ReadInt32BigEndian(data[4..8]), width: BinaryPrimitives.ReadInt32BigEndian(data[4..8]),
@ -167,6 +167,6 @@ internal readonly struct APngFrameControl
yOffset: BinaryPrimitives.ReadInt32BigEndian(data[16..20]), yOffset: BinaryPrimitives.ReadInt32BigEndian(data[16..20]),
delayNumber: BinaryPrimitives.ReadInt16BigEndian(data[20..22]), delayNumber: BinaryPrimitives.ReadInt16BigEndian(data[20..22]),
delayDenominator: BinaryPrimitives.ReadInt16BigEndian(data[22..24]), delayDenominator: BinaryPrimitives.ReadInt16BigEndian(data[22..24]),
disposeOperation: (APngDisposeOperation)data[24], disposeOperation: (PngDisposeOperation)data[24],
blendOperation: (APngBlendOperation)data[25]); blendOperation: (PngBlendOperation)data[25]);
} }

9
src/ImageSharp/Formats/Png/MetadataExtensions.cs

@ -23,15 +23,14 @@ public static partial class MetadataExtensions
/// Gets the aPng format specific metadata for the image frame. /// Gets the aPng format specific metadata for the image frame.
/// </summary> /// </summary>
/// <param name="source">The metadata this method extends.</param> /// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="APngFrameMetadata"/>.</returns> /// <returns>The <see cref="PngFrameMetadata"/>.</returns>
public static APngFrameMetadata GetAPngFrameMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance); public static PngFrameMetadata GetPngFrameMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
/// <summary> /// <summary>
/// Gets the aPng format specific metadata for the image frame. /// Gets the aPng format specific metadata for the image frame.
/// </summary> /// </summary>
/// <param name="source">The metadata this method extends.</param> /// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">The metadata.</param> /// <param name="metadata">The metadata.</param>
/// <returns>The <see cref="APngFrameMetadata"/>.</returns> /// <returns>The <see cref="PngFrameMetadata"/>.</returns>
public static bool TryGetAPngFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out APngFrameMetadata? metadata) => source.TryGetFormatMetadata(PngFormat.Instance, out metadata); public static bool TryGetPngFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out PngFrameMetadata? metadata) => source.TryGetFormatMetadata(PngFormat.Instance, out metadata);
} }

2
src/ImageSharp/Formats/Png/APngBlendOperation.cs → src/ImageSharp/Formats/Png/PngBlendOperation.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Specifies whether the frame is to be alpha blended into the current output buffer content, or whether it should completely replace its region in the output buffer. /// Specifies whether the frame is to be alpha blended into the current output buffer content, or whether it should completely replace its region in the output buffer.
/// </summary> /// </summary>
public enum APngBlendOperation public enum PngBlendOperation
{ {
/// <summary> /// <summary>
/// All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. /// All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region.

21
src/ImageSharp/Formats/Png/PngConstants.cs

@ -80,5 +80,24 @@ internal static class PngConstants
/// <summary> /// <summary>
/// Gets the keyword of the XMP metadata, encoded in an iTXT chunk. /// Gets the keyword of the XMP metadata, encoded in an iTXT chunk.
/// </summary> /// </summary>
public static ReadOnlySpan<byte> XmpKeyword => "XML:com.adobe.xmp"u8; public static ReadOnlySpan<byte> XmpKeyword => new[]
{
(byte)'X',
(byte)'M',
(byte)'L',
(byte)':',
(byte)'c',
(byte)'o',
(byte)'m',
(byte)'.',
(byte)'a',
(byte)'d',
(byte)'o',
(byte)'b',
(byte)'e',
(byte)'.',
(byte)'x',
(byte)'m',
(byte)'p'
};
} }

66
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -28,11 +28,6 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// </summary> /// </summary>
internal sealed class PngDecoderCore : IImageDecoderInternals internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
/// <summary>
/// Indicate whether the file is a simple PNG.
/// </summary>
private bool isSimplePng;
/// <summary> /// <summary>
/// The general decoder options. /// The general decoder options.
/// </summary> /// </summary>
@ -66,7 +61,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// The png animation control. /// The png animation control.
/// </summary> /// </summary>
private APngAnimationControl? animationControl; private AnimationControl? animationControl;
/// <summary> /// <summary>
/// The number of bytes per pixel. /// The number of bytes per pixel.
@ -149,7 +144,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
Image<TPixel>? image = null; Image<TPixel>? image = null;
APngFrameControl? lastFrameControl = null; FrameControl? lastFrameControl = null;
ImageFrame<TPixel>? currentFrame = null; ImageFrame<TPixel>? currentFrame = null;
Span<byte> buffer = stackalloc byte[20]; Span<byte> buffer = stackalloc byte[20];
@ -170,11 +165,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan()); this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.AnimationControl: case PngChunkType.AnimationControl:
if (this.isSimplePng || this.animationControl is not null)
{
PngThrowHelper.ThrowInvalidAnimationControl();
}
this.ReadAnimationControlChunk(pngMetadata, chunk.Data.GetSpan()); this.ReadAnimationControlChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.Physical: case PngChunkType.Physical:
@ -184,11 +174,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.FrameControl: case PngChunkType.FrameControl:
if (this.isSimplePng)
{
continue;
}
currentFrame = null; currentFrame = null;
lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan());
break; break;
@ -228,11 +213,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
lastFrameControl = null; lastFrameControl = null;
break; break;
case PngChunkType.Data: case PngChunkType.Data:
if (this.animationControl is null)
{
this.isSimplePng = true;
}
if (image is null) if (image is null)
{ {
this.InitializeImage(metadata, lastFrameControl, out image); this.InitializeImage(metadata, lastFrameControl, out image);
@ -313,7 +293,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ImageMetadata metadata = new(); ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream; this.currentStream = stream;
APngFrameControl? lastFrameControl = null; FrameControl? lastFrameControl = null;
Span<byte> buffer = stackalloc byte[20]; Span<byte> buffer = stackalloc byte[20];
this.currentStream.Skip(8); this.currentStream.Skip(8);
@ -330,11 +310,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan()); this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.AnimationControl: case PngChunkType.AnimationControl:
if (this.isSimplePng || this.animationControl is not null)
{
PngThrowHelper.ThrowInvalidAnimationControl();
}
this.ReadAnimationControlChunk(pngMetadata, chunk.Data.GetSpan()); this.ReadAnimationControlChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.Physical: case PngChunkType.Physical:
@ -356,11 +331,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break; break;
case PngChunkType.FrameControl: case PngChunkType.FrameControl:
if (this.isSimplePng)
{
continue;
}
lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan());
break; break;
case PngChunkType.FrameData: case PngChunkType.FrameData:
@ -379,11 +349,6 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.SkipChunkDataAndCrc(chunk); this.SkipChunkDataAndCrc(chunk);
break; break;
case PngChunkType.Data: case PngChunkType.Data:
if (this.animationControl is null)
{
this.isSimplePng = true;
}
// Spec says tRNS must be before IDAT so safe to exit. // Spec says tRNS must be before IDAT so safe to exit.
if (this.colorMetadataOnly) if (this.colorMetadataOnly)
{ {
@ -465,7 +430,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
} }
EOF: EOF:
if (this.header is { Width: 0, Height: 0 }) if (this.header.Width == 0 && this.header.Height == 0)
{ {
PngThrowHelper.ThrowInvalidHeader(); PngThrowHelper.ThrowInvalidHeader();
} }
@ -568,7 +533,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="metadata">The metadata information for the image</param> /// <param name="metadata">The metadata information for the image</param>
/// <param name="frameControl">The frame control information for the frame</param> /// <param name="frameControl">The frame control information for the frame</param>
/// <param name="image">The image that we will populate</param> /// <param name="image">The image that we will populate</param>
private void InitializeImage<TPixel>(ImageMetadata metadata, APngFrameControl? frameControl, out Image<TPixel> image) private void InitializeImage<TPixel>(ImageMetadata metadata, FrameControl? frameControl, out Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
image = Image.CreateUninitialized<TPixel>( image = Image.CreateUninitialized<TPixel>(
@ -579,7 +544,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
if (frameControl is { } control) if (frameControl is { } control)
{ {
APngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetAPngFrameMetadata(); PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngFrameMetadata();
frameMetadata.FromChunk(control); frameMetadata.FromChunk(control);
} }
@ -603,12 +568,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <typeparam name="TPixel">The type the pixels will be</typeparam> /// <typeparam name="TPixel">The type the pixels will be</typeparam>
/// <param name="frameControl">The frame control information for the frame</param> /// <param name="frameControl">The frame control information for the frame</param>
/// <param name="image">The image that we will populate</param> /// <param name="image">The image that we will populate</param>
private void InitializeFrame<TPixel>(APngFrameControl frameControl, Image<TPixel> image, out ImageFrame<TPixel> frame) /// <param name="frame">The created frame</param>
private void InitializeFrame<TPixel>(FrameControl frameControl, Image<TPixel> image, out ImageFrame<TPixel> frame)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
frame = image.Frames.CreateFrame(); frame = image.Frames.CreateFrame();
APngFrameMetadata frameMetadata = frame.Metadata.GetAPngFrameMetadata(); PngFrameMetadata frameMetadata = frame.Metadata.GetPngFrameMetadata();
frameMetadata.FromChunk(frameControl); frameMetadata.FromChunk(frameControl);
@ -716,7 +682,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
int currentRow = Adam7.FirstRow[0]; int currentRow = Adam7.FirstRow[0];
int currentRowBytesRead = 0; int currentRowBytesRead = 0;
int height = image.Metadata.TryGetAPngFrameMetadata(out APngFrameMetadata? frameMetadata) ? frameMetadata.Height : this.header.Height; int height = image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata) ? frameMetadata.Height : this.header.Height;
while (currentRow < height) while (currentRow < height)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -763,7 +729,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.ProcessDefilteredScanline(currentRow, scanlineSpan, image, pngMetadata); this.ProcessDefilteredScanline(currentRow, scanlineSpan, image, pngMetadata);
this.SwapScanlineBuffers(); this.SwapScanlineBuffers();
++currentRow; currentRow++;
} }
} }
@ -784,7 +750,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
int pass = 0; int pass = 0;
int width = this.header.Width; int width = this.header.Width;
int height = this.header.Height; int height = this.header.Height;
if (image.Metadata.TryGetAPngFrameMetadata(out APngFrameMetadata? frameMetadata)) if (image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata))
{ {
width = frameMetadata.Width; width = frameMetadata.Width;
height = frameMetadata.Height; height = frameMetadata.Height;
@ -797,7 +763,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
if (numColumns == 0) if (numColumns == 0)
{ {
++pass; pass++;
// This pass contains no data; skip to next pass // This pass contains no data; skip to next pass
continue; continue;
@ -1124,7 +1090,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param> /// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
private void ReadAnimationControlChunk(PngMetadata pngMetadata, ReadOnlySpan<byte> data) private void ReadAnimationControlChunk(PngMetadata pngMetadata, ReadOnlySpan<byte> data)
{ {
this.animationControl = APngAnimationControl.Parse(data); this.animationControl = AnimationControl.Parse(data);
pngMetadata.NumberPlays = this.animationControl.NumberPlays; pngMetadata.NumberPlays = this.animationControl.NumberPlays;
} }
@ -1133,9 +1099,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Reads a header chunk from the data. /// Reads a header chunk from the data.
/// </summary> /// </summary>
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param> /// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
private APngFrameControl ReadFrameControlChunk(ReadOnlySpan<byte> data) private FrameControl ReadFrameControlChunk(ReadOnlySpan<byte> data)
{ {
APngFrameControl fcTL = APngFrameControl.Parse(data); FrameControl fcTL = FrameControl.Parse(data);
fcTL.Validate(this.header); fcTL.Validate(this.header);

2
src/ImageSharp/Formats/Png/APngDisposeOperation.cs → src/ImageSharp/Formats/Png/PngDisposeOperation.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Specifies how the output buffer should be changed at the end of the delay (before rendering the next frame). /// Specifies how the output buffer should be changed at the end of the delay (before rendering the next frame).
/// </summary> /// </summary>
public enum APngDisposeOperation public enum PngDisposeOperation
{ {
/// <summary> /// <summary>
/// No disposal is done on this frame before rendering the next; the contents of the output buffer are left as is. /// No disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.

5
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -19,11 +19,6 @@ public class PngEncoder : QuantizingImageEncoder
// quantizer with options appropriate to the encoding bit depth. // quantizer with options appropriate to the encoding bit depth.
this.Quantizer = null!; this.Quantizer = null!;
/// <summary>
/// Gets whether the file is a simple PNG.
/// </summary>
public bool? IsSimplePng { get; init; }
/// <summary> /// <summary>
/// Gets the number of bits per sample or per palette index (not per pixel). /// Gets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType" /> values. /// Not all values are allowed for all <see cref="ColorType" /> values.

41
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -25,7 +25,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// <summary> /// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks. /// The maximum block size, defaults at 64k for uncompressed blocks.
/// </summary> /// </summary>
private const int MaxBlockSize = (1 << 16) - 1; private const int MaxBlockSize = 65535;
/// <summary> /// <summary>
/// Used the manage memory allocations. /// Used the manage memory allocations.
@ -168,20 +168,19 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
this.WriteXmpChunk(stream, metadata); this.WriteXmpChunk(stream, metadata);
this.WriteTextChunks(stream, pngMetadata); this.WriteTextChunks(stream, pngMetadata);
if ((this.encoder.IsSimplePng is null && targetImage.Frames.Count > 1) if (targetImage.Frames.Count > 1)
|| this.encoder.IsSimplePng is false)
{ {
this.WriteAnimationControlChunk(stream, targetImage.Frames.Count, pngMetadata.NumberPlays); this.WriteAnimationControlChunk(stream, targetImage.Frames.Count, pngMetadata.NumberPlays);
this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetAPngFrameMetadata(), 0); this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetPngFrameMetadata(), 0);
_ = this.WriteDataChunks(targetImage.Frames.RootFrame, rootQuantized, stream, false); _ = this.WriteDataChunks(targetImage.Frames.RootFrame, rootQuantized, stream, false);
int index = 1; int index = 1;
foreach (ImageFrame<TPixel> imageFrame in ((IEnumerable<ImageFrame<TPixel>>)targetImage.Frames).Skip(1)) foreach (ImageFrame<TPixel> imageFrame in ((IEnumerable<ImageFrame<TPixel>>)targetImage.Frames).Skip(1))
{ {
this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetAPngFrameMetadata(), index); this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetPngFrameMetadata(), index);
++index; index++;
IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(imageFrame); IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(imageFrame);
index += this.WriteDataChunks(imageFrame, quantized, stream, true, index); index += this.WriteDataChunks(imageFrame, quantized, stream, true, index);
quantized?.Dispose(); quantized?.Dispose();
@ -225,10 +224,10 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
// TODO: We should be able to speed this up with SIMD and masking. // TODO: We should be able to speed this up with SIMD and masking.
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
Rgba32 transparent = Color.Transparent; Rgba32 transparent = Color.Transparent;
for (int y = 0; y < accessor.Height; ++y) for (int y = 0; y < accessor.Height; y++)
{ {
Span<TPixel> span = accessor.GetRowSpan(y); Span<TPixel> span = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; ++x) for (int x = 0; x < accessor.Width; x++)
{ {
span[x].ToRgba32(ref rgba32); span[x].ToRgba32(ref rgba32);
@ -278,7 +277,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
PixelOperations<TPixel>.Instance.ToL16(this.configuration, rowSpan, luminanceSpan); PixelOperations<TPixel>.Instance.ToL16(this.configuration, rowSpan, luminanceSpan);
// Can't map directly to byte array as it's big-endian. // Can't map directly to byte array as it's big-endian.
for (int x = 0, o = 0; x < luminanceSpan.Length; ++x, o += 2) for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2)
{ {
L16 luminance = Unsafe.Add(ref luminanceRef, (uint)x); L16 luminance = Unsafe.Add(ref luminanceRef, (uint)x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue);
@ -318,7 +317,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
PixelOperations<TPixel>.Instance.ToLa32(this.configuration, rowSpan, laSpan); PixelOperations<TPixel>.Instance.ToLa32(this.configuration, rowSpan, laSpan);
// Can't map directly to byte array as it's big endian. // Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < laSpan.Length; ++x, o += 4) for (int x = 0, o = 0; x < laSpan.Length; x++, o += 4)
{ {
La32 la = Unsafe.Add(ref laRef, (uint)x); La32 la = Unsafe.Add(ref laRef, (uint)x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), la.L); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), la.L);
@ -602,11 +601,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// <param name="playsCount">The number of times to loop this APNG.</param> /// <param name="playsCount">The number of times to loop this APNG.</param>
private void WriteAnimationControlChunk(Stream stream, int framesCount, int playsCount) private void WriteAnimationControlChunk(Stream stream, int framesCount, int playsCount)
{ {
APngAnimationControl acTL = new(framesCount, playsCount); AnimationControl acTL = new(framesCount, playsCount);
acTL.WriteTo(this.chunkDataBuffer.Span); acTL.WriteTo(this.chunkDataBuffer.Span);
this.WriteChunk(stream, PngChunkType.AnimationControl, this.chunkDataBuffer.Span, 0, APngAnimationControl.Size); this.WriteChunk(stream, PngChunkType.AnimationControl, this.chunkDataBuffer.Span, 0, AnimationControl.Size);
} }
/// <summary> /// <summary>
@ -643,7 +642,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPaletteSpan); ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPaletteSpan);
// Loop, assign, and extract alpha values from the palette. // Loop, assign, and extract alpha values from the palette.
for (int i = 0; i < paletteLength; ++i) for (int i = 0; i < paletteLength; i++)
{ {
Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, (uint)i); Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, (uint)i);
byte alpha = rgba.A; byte alpha = rgba.A;
@ -963,13 +962,13 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="frameMetadata">Provides APng specific metadata information for the image frame.</param> /// <param name="frameMetadata">Provides APng specific metadata information for the image frame.</param>
/// <param name="sequenceNumber">Sequence number.</param> /// <param name="sequenceNumber">Sequence number.</param>
private void WriteFrameControlChunk(Stream stream, APngFrameMetadata frameMetadata, int sequenceNumber) private void WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, int sequenceNumber)
{ {
APngFrameControl fcTL = APngFrameControl.FromMetadata(frameMetadata, sequenceNumber); FrameControl fcTL = FrameControl.FromMetadata(frameMetadata, sequenceNumber);
fcTL.WriteTo(this.chunkDataBuffer.Span); fcTL.WriteTo(this.chunkDataBuffer.Span);
this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, APngFrameControl.Size); this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, FrameControl.Size);
} }
/// <summary> /// <summary>
@ -1024,10 +1023,10 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
if (bufferLength % maxBlockSize != 0) if (bufferLength % maxBlockSize != 0)
{ {
++numChunks; numChunks++;
} }
for (int i = 0; i < numChunks; ++i) for (int i = 0; i < numChunks; i++)
{ {
int length = bufferLength - (i * maxBlockSize); int length = bufferLength - (i * maxBlockSize);
@ -1078,7 +1077,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{ {
int width = this.width; int width = this.width;
int height = this.height; int height = this.height;
if (pixels.Metadata.TryGetAPngFrameMetadata(out APngFrameMetadata? pngMetadata)) if (pixels.Metadata.TryGetAPngFrameMetadata(out PngFrameMetadata? pngMetadata))
{ {
width = pngMetadata.Width; width = pngMetadata.Width;
height = pngMetadata.Height; height = pngMetadata.Height;
@ -1095,7 +1094,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{ {
Span<byte> filter = filterBuffer.GetSpan(); Span<byte> filter = filterBuffer.GetSpan();
Span<byte> attempt = attemptBuffer.GetSpan(); Span<byte> attempt = attemptBuffer.GetSpan();
for (int y = 0; y < height; ++y) for (int y = 0; y < height; y++)
{ {
this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y); this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter); deflateStream.Write(filter);
@ -1201,7 +1200,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
col += Adam7.ColumnIncrement[pass]) col += Adam7.ColumnIncrement[pass])
{ {
block[i] = srcRow[col]; block[i] = srcRow[col];
++i; i++;
} }
// Encode data // Encode data

4
src/ImageSharp/Formats/Png/PngFormat.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format. /// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary> /// </summary>
public sealed class PngFormat : IImageFormat<PngMetadata, APngFrameMetadata> public sealed class PngFormat : IImageFormat<PngMetadata, PngFrameMetadata>
{ {
private PngFormat() private PngFormat()
{ {
@ -33,5 +33,5 @@ public sealed class PngFormat : IImageFormat<PngMetadata, APngFrameMetadata>
public PngMetadata CreateDefaultFormatMetadata() => new(); public PngMetadata CreateDefaultFormatMetadata() => new();
/// <inheritdoc/> /// <inheritdoc/>
public APngFrameMetadata CreateDefaultFormatFrameMetadata() => new(); public PngFrameMetadata CreateDefaultFormatFrameMetadata() => new();
} }

20
src/ImageSharp/Formats/Png/APngFrameMetadata.cs → src/ImageSharp/Formats/Png/PngFrameMetadata.cs

@ -8,20 +8,20 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Provides APng specific metadata information for the image frame. /// Provides APng specific metadata information for the image frame.
/// </summary> /// </summary>
public class APngFrameMetadata : IDeepCloneable public class PngFrameMetadata : IDeepCloneable
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="APngFrameMetadata"/> class. /// Initializes a new instance of the <see cref="PngFrameMetadata"/> class.
/// </summary> /// </summary>
public APngFrameMetadata() public PngFrameMetadata()
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="APngFrameMetadata"/> class. /// Initializes a new instance of the <see cref="PngFrameMetadata"/> 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 APngFrameMetadata(APngFrameMetadata other) private PngFrameMetadata(PngFrameMetadata other)
{ {
this.Width = other.Width; this.Width = other.Width;
this.Height = other.Height; this.Height = other.Height;
@ -66,18 +66,18 @@ public class APngFrameMetadata : IDeepCloneable
/// <summary> /// <summary>
/// Gets or sets the type of frame area disposal to be done after rendering this frame /// Gets or sets the type of frame area disposal to be done after rendering this frame
/// </summary> /// </summary>
public APngDisposeOperation DisposeOperation { get; set; } public PngDisposeOperation DisposeOperation { get; set; }
/// <summary> /// <summary>
/// Gets or sets the type of frame area rendering for this frame /// Gets or sets the type of frame area rendering for this frame
/// </summary> /// </summary>
public APngBlendOperation BlendOperation { get; set; } public PngBlendOperation BlendOperation { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="APngFrameMetadata"/> class. /// Initializes a new instance of the <see cref="PngFrameMetadata"/> class.
/// </summary> /// </summary>
/// <param name="frameControl">The chunk to create an instance from.</param> /// <param name="frameControl">The chunk to create an instance from.</param>
internal void FromChunk(APngFrameControl frameControl) internal void FromChunk(FrameControl frameControl)
{ {
this.Width = frameControl.Width; this.Width = frameControl.Width;
this.Height = frameControl.Height; this.Height = frameControl.Height;
@ -90,5 +90,5 @@ public class APngFrameMetadata : IDeepCloneable
} }
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new APngFrameMetadata(this); public IDeepCloneable DeepClone() => new PngFrameMetadata(this);
} }

1
src/ImageSharp/ImageSharp.csproj

@ -13,7 +13,6 @@
<PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga Tiff WebP NetCore</PackageTags> <PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga Tiff WebP NetCore</PackageTags>
<Description>A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET</Description> <Description>A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET</Description>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<!-- This enables the nullable analysis and treats all nullable warnings as error--> <!-- This enables the nullable analysis and treats all nullable warnings as error-->

Loading…
Cancel
Save