-
-
Notifications
You must be signed in to change notification settings - Fork 888
APNG support #2511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
APNG support #2511
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
45f6f5b
Implement APNG decoder
Poker-sang 7b6c32d
implement APNG encoder
Poker-sang 01caebd
Add UnitTest
Poker-sang 6f9525e
Merge branch 'main' into main
Poker-sang 64a0ff0
Fix simple issues in review
Poker-sang e7eed49
Merge branch 'main' of github.com:Poker-sang/ImageSharp
Poker-sang bf308b7
Merge branch 'main' into main
Poker-sang 6e54822
Fix review
Poker-sang c253f39
Fix offset
Poker-sang 1464064
Fix: replace lambda with method
Poker-sang 316a839
Optimize code
Poker-sang a6b8abe
remove set to null from disposal
Poker-sang b0dc908
Merge branch 'main' into main
JimBobSquarePants 5a711a8
Merge remote-tracking branch 'upstream/main'
JimBobSquarePants aada974
Refactor and cleanup
JimBobSquarePants 564c3d1
Fix encoding
JimBobSquarePants 3bc12e4
Fix failing tests
JimBobSquarePants 0385ad0
Fix header bit depth assignment.
JimBobSquarePants 5ed6f24
Reintroduce scanline optimizations
JimBobSquarePants bc5b6c5
Add alpha blending support
JimBobSquarePants 8455275
Handle disposal methods.
JimBobSquarePants 56588d3
Use region for alpha blending
JimBobSquarePants 66f444d
Fix alpha blending and add tests
JimBobSquarePants 14a95a8
Rename properties and add metadata tests
JimBobSquarePants b4e9805
Update PngDecoderTests.cs
JimBobSquarePants File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; } | ||
Poker-sang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// <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])); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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]); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/ImageSharp/Formats/Png/PngTextData.cs → ...geSharp/Formats/Png/Chunks/PngTextData.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Six Labors Split License. | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Png; | ||
|
|
||
| /// <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. | ||
| /// </summary> | ||
| public enum PngBlendMethod | ||
| { | ||
| /// <summary> | ||
| /// All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. | ||
| /// </summary> | ||
| Source, | ||
|
|
||
| /// <summary> | ||
| /// The frame should be composited onto the output buffer based on its alpha, using a simple OVER operation as | ||
| /// described in the "Alpha Channel Processing" section of the PNG specification [PNG-1.2]. | ||
| /// </summary> | ||
| Over | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.