Skip to content

Commit

Permalink
Merge branch 'main' into MakerProfileResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseEliasSantos authored Nov 6, 2023
2 parents c7f3aa4 + afe2133 commit 412e451
Show file tree
Hide file tree
Showing 130 changed files with 3,407 additions and 1,809 deletions.
124 changes: 124 additions & 0 deletions src/ImageSharp/Common/Helpers/RiffHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Buffers.Binary;
using System.Text;

namespace SixLabors.ImageSharp.Common.Helpers;

internal static class RiffHelper
{
/// <summary>
/// The header bytes identifying RIFF file.
/// </summary>
private const uint RiffFourCc = 0x52_49_46_46;

public static void WriteRiffFile(Stream stream, string formType, Action<Stream> func) =>
WriteChunk(stream, RiffFourCc, s =>
{
s.Write(Encoding.ASCII.GetBytes(formType));
func(s);
});

public static void WriteChunk(Stream stream, uint fourCc, Action<Stream> func)
{
Span<byte> buffer = stackalloc byte[4];

// write the fourCC
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
stream.Write(buffer);

long sizePosition = stream.Position;
stream.Position += 4;

func(stream);

long position = stream.Position;

uint dataSize = (uint)(position - sizePosition - 4);

// padding
if (dataSize % 2 == 1)
{
stream.WriteByte(0);
position++;
}

BinaryPrimitives.WriteUInt32LittleEndian(buffer, dataSize);
stream.Position = sizePosition;
stream.Write(buffer);
stream.Position = position;
}

public static void WriteChunk(Stream stream, uint fourCc, ReadOnlySpan<byte> data)
{
Span<byte> buffer = stackalloc byte[4];

// write the fourCC
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
stream.Write(buffer);
uint size = (uint)data.Length;
BinaryPrimitives.WriteUInt32LittleEndian(buffer, size);
stream.Write(buffer);
stream.Write(data);

// padding
if (size % 2 is 1)
{
stream.WriteByte(0);
}
}

public static unsafe void WriteChunk<TStruct>(Stream stream, uint fourCc, in TStruct chunk)
where TStruct : unmanaged
{
fixed (TStruct* ptr = &chunk)
{
WriteChunk(stream, fourCc, new Span<byte>(ptr, sizeof(TStruct)));
}
}

public static long BeginWriteChunk(Stream stream, uint fourCc)
{
Span<byte> buffer = stackalloc byte[4];

// write the fourCC
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
stream.Write(buffer);

long sizePosition = stream.Position;
stream.Position += 4;

return sizePosition;
}

public static void EndWriteChunk(Stream stream, long sizePosition)
{
Span<byte> buffer = stackalloc byte[4];

long position = stream.Position;

uint dataSize = (uint)(position - sizePosition - 4);

// padding
if (dataSize % 2 is 1)
{
stream.WriteByte(0);
position++;
}

BinaryPrimitives.WriteUInt32LittleEndian(buffer, dataSize);
stream.Position = sizePosition;
stream.Write(buffer);
stream.Position = position;
}

public static long BeginWriteRiffFile(Stream stream, string formType)
{
long sizePosition = BeginWriteChunk(stream, RiffFourCc);
stream.Write(Encoding.ASCII.GetBytes(formType));
return sizePosition;
}

public static void EndWriteRiffFile(Stream stream, long sizePosition) => EndWriteChunk(stream, sizePosition);
}
8 changes: 4 additions & 4 deletions src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ public override int ReadByte()
/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count)
{
if (this.currentDataRemaining == 0)
if (this.currentDataRemaining is 0)
{
// Last buffer was read in its entirety, let's make sure we don't actually have more in additional IDAT chunks.
this.currentDataRemaining = this.getData();

if (this.currentDataRemaining == 0)
if (this.currentDataRemaining is 0)
{
return 0;
}
Expand All @@ -142,11 +142,11 @@ public override int Read(byte[] buffer, int offset, int count)
// Keep reading data until we've reached the end of the stream or filled the buffer.
int bytesRead = 0;
offset += totalBytesRead;
while (this.currentDataRemaining == 0 && totalBytesRead < count)
while (this.currentDataRemaining is 0 && totalBytesRead < count)
{
this.currentDataRemaining = this.getData();

if (this.currentDataRemaining == 0)
if (this.currentDataRemaining is 0)
{
return totalBytesRead;
}
Expand Down
47 changes: 47 additions & 0 deletions src/ImageSharp/Formats/Png/Chunks/AnimationControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Buffers.Binary;

namespace SixLabors.ImageSharp.Formats.Png.Chunks;

internal readonly struct AnimationControl
{
public const int Size = 8;

public AnimationControl(int numberFrames, int numberPlays)
{
this.NumberFrames = numberFrames;
this.NumberPlays = numberPlays;
}

/// <summary>
/// Gets the number of frames
/// </summary>
public int NumberFrames { get; }

/// <summary>
/// Gets the number of times to loop this APNG. 0 indicates infinite looping.
/// </summary>
public int NumberPlays { get; }

/// <summary>
/// Writes the acTL to the given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public void WriteTo(Span<byte> buffer)
{
BinaryPrimitives.WriteInt32BigEndian(buffer[..4], this.NumberFrames);
BinaryPrimitives.WriteInt32BigEndian(buffer[4..8], this.NumberPlays);
}

/// <summary>
/// Parses the APngAnimationControl from the given data buffer.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed acTL.</returns>
public static AnimationControl Parse(ReadOnlySpan<byte> data)
=> new(
numberFrames: BinaryPrimitives.ReadInt32BigEndian(data[..4]),
numberPlays: BinaryPrimitives.ReadInt32BigEndian(data[4..8]));
}
160 changes: 160 additions & 0 deletions src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Buffers.Binary;

namespace SixLabors.ImageSharp.Formats.Png.Chunks;

internal readonly struct FrameControl
{
public const int Size = 26;

public FrameControl(uint width, uint height)
: this(0, width, height, 0, 0, 0, 0, default, default)
{
}

public FrameControl(
uint sequenceNumber,
uint width,
uint height,
uint xOffset,
uint yOffset,
ushort delayNumerator,
ushort delayDenominator,
PngDisposalMethod disposeOperation,
PngBlendMethod blendOperation)
{
this.SequenceNumber = sequenceNumber;
this.Width = width;
this.Height = height;
this.XOffset = xOffset;
this.YOffset = yOffset;
this.DelayNumerator = delayNumerator;
this.DelayDenominator = delayDenominator;
this.DisposeOperation = disposeOperation;
this.BlendOperation = blendOperation;
}

/// <summary>
/// Gets the sequence number of the animation chunk, starting from 0
/// </summary>
public uint SequenceNumber { get; }

/// <summary>
/// Gets the width of the following frame
/// </summary>
public uint Width { get; }

/// <summary>
/// Gets the height of the following frame
/// </summary>
public uint Height { get; }

/// <summary>
/// Gets the X position at which to render the following frame
/// </summary>
public uint XOffset { get; }

/// <summary>
/// Gets the Y position at which to render the following frame
/// </summary>
public uint YOffset { get; }

/// <summary>
/// Gets the X limit at which to render the following frame
/// </summary>
public uint XMax => this.XOffset + this.Width;

/// <summary>
/// Gets the Y limit at which to render the following frame
/// </summary>
public uint YMax => this.YOffset + this.Height;

/// <summary>
/// Gets the frame delay fraction numerator
/// </summary>
public ushort DelayNumerator { get; }

/// <summary>
/// Gets the frame delay fraction denominator
/// </summary>
public ushort DelayDenominator { get; }

/// <summary>
/// Gets the type of frame area disposal to be done after rendering this frame
/// </summary>
public PngDisposalMethod DisposeOperation { get; }

/// <summary>
/// Gets the type of frame area rendering for this frame
/// </summary>
public PngBlendMethod BlendOperation { get; }

public Rectangle Bounds => new((int)this.XOffset, (int)this.YOffset, (int)this.Width, (int)this.Height);

/// <summary>
/// Validates the APng fcTL.
/// </summary>
/// <param name="header">The header.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the image does pass validation.
/// </exception>
public void Validate(PngHeader header)
{
if (this.Width == 0)
{
PngThrowHelper.ThrowInvalidParameter(this.Width, "Expected > 0");
}

if (this.Height == 0)
{
PngThrowHelper.ThrowInvalidParameter(this.Height, "Expected > 0");
}

if (this.XMax > header.Width)
{
PngThrowHelper.ThrowInvalidParameter(this.XOffset, this.Width, $"The x-offset plus width > {nameof(PngHeader)}.{nameof(PngHeader.Width)}");
}

if (this.YMax > header.Height)
{
PngThrowHelper.ThrowInvalidParameter(this.YOffset, this.Height, $"The y-offset plus height > {nameof(PngHeader)}.{nameof(PngHeader.Height)}");
}
}

/// <summary>
/// Writes the fcTL to the given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public void WriteTo(Span<byte> buffer)
{
BinaryPrimitives.WriteUInt32BigEndian(buffer[..4], this.SequenceNumber);
BinaryPrimitives.WriteUInt32BigEndian(buffer[4..8], this.Width);
BinaryPrimitives.WriteUInt32BigEndian(buffer[8..12], this.Height);
BinaryPrimitives.WriteUInt32BigEndian(buffer[12..16], this.XOffset);
BinaryPrimitives.WriteUInt32BigEndian(buffer[16..20], this.YOffset);
BinaryPrimitives.WriteUInt16BigEndian(buffer[20..22], this.DelayNumerator);
BinaryPrimitives.WriteUInt16BigEndian(buffer[22..24], this.DelayDenominator);

buffer[24] = (byte)this.DisposeOperation;
buffer[25] = (byte)this.BlendOperation;
}

/// <summary>
/// Parses the APngFrameControl from the given data buffer.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed fcTL.</returns>
public static FrameControl Parse(ReadOnlySpan<byte> data)
=> new(
sequenceNumber: BinaryPrimitives.ReadUInt32BigEndian(data[..4]),
width: BinaryPrimitives.ReadUInt32BigEndian(data[4..8]),
height: BinaryPrimitives.ReadUInt32BigEndian(data[8..12]),
xOffset: BinaryPrimitives.ReadUInt32BigEndian(data[12..16]),
yOffset: BinaryPrimitives.ReadUInt32BigEndian(data[16..20]),
delayNumerator: BinaryPrimitives.ReadUInt16BigEndian(data[20..22]),
delayDenominator: BinaryPrimitives.ReadUInt16BigEndian(data[22..24]),
disposeOperation: (PngDisposalMethod)data[24],
blendOperation: (PngBlendMethod)data[25]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using System.Buffers.Binary;

namespace SixLabors.ImageSharp.Formats.Png;
namespace SixLabors.ImageSharp.Formats.Png.Chunks;

/// <summary>
/// Represents the png header chunk.
Expand Down
Loading

0 comments on commit 412e451

Please sign in to comment.