Skip to content

Improvements to Apple CgBI PNG handling#3137

Draft
Erik-White wants to merge 12 commits into
SixLabors:mainfrom
Erik-White:png-cgbi-improvements
Draft

Improvements to Apple CgBI PNG handling#3137
Erik-White wants to merge 12 commits into
SixLabors:mainfrom
Erik-White:png-cgbi-improvements

Conversation

@Erik-White

@Erik-White Erik-White commented May 27, 2026

Copy link
Copy Markdown
Contributor

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Following on from #3136 , this attempts to streamline and improve the related code as well as improve test coverage.

  • CgBI images that are not supported are now explicitly rejected (i.e. bit depth != 8 or truecolor)
  • Broke out ChunkedReadStream from ZlibInflateStream to separate the concerns instead of mixing modes in ZlibInflateStream
  • Broke out CgBI transform from PngDecoderCore into new PngCgbiProcessor static class
  • Added tests for PngCgbiProcessor
  • Small improvements to the CgBI shuffle methods (e.g. construct the shuffle patterns only once, remove redundant floor(), replace magic numbers where possible)

@Erik-White

Copy link
Copy Markdown
Contributor Author

Curious to hear your thoughts. The changes should all be relatively self-contained per commit in case there are sections that you don't think should be included.

@JimBobSquarePants

Copy link
Copy Markdown
Member

This looks like a lot of changes. Will take time to review

@Erik-White

Copy link
Copy Markdown
Contributor Author

Understood. As I mentioned in the description, each commit is fairly self contained but I could also break up the PR into a couple of smaller units if that would be helpful?

Comment thread tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs Outdated
Comment thread tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs Outdated
[MemberNotNullWhen(true, nameof(CompressedStream))]
private bool InitializeInflateStream(bool isCriticalChunk)
{
// Apple CgBI IDATs omit the zlib CMF/FLG header and the Adler-32 trailer,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we no longer return early?

@Erik-White Erik-White Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my first CgBI PR I had added the noHeader parameter as a workaround for streams without headers that came from the CgBI images. It worked fine but I didn't like that it mixed concerns in ZLibInflateStream.

This PR now separates out the concerns so ZlibInflateStream always handles streams with headers, and ChunkedReadStream can be used for those without.

So that early exist logic now lives in PngDecoderCore instead of imposing on ZlibInflateStream:
https://github.com/SixLabors/ImageSharp/pull/3137/changes/BASE..4869c344b3e5c7df9e8b696b6cd6433230178a65#diff-e626c30d90afb58e53b15876a4ee894c9581334c8ddbb2fa2be18759a60aea59R780

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That means ChunkedReadStream is now a misnomer as it’s still reading zlib compressed stream is it not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a stream wrapper and is fairly generic so in theory it could be used for other cases. But yes, right now it's only being used for zlib

/// Adler-32 trailer is not validated.
/// </summary>
internal sealed class ZlibInflateStream : Stream
internal sealed class ZlibInflateStream : IDisposable

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'm comfortable with a type suffixed Stream that is no longer inherits Stream. This feels like we're violating runtime design guidelines.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was to use composition over inheritance here, especially since most the of the stream methods on ZlibInflateStream just throw NotSupportedException anyway.

But I'm sure ZlibInflateStream could just inherit from ChunkedReadStream (which inherits from Stream) if that would be preferrable? Or would it make sense to just rename this to something like ZlibInflateReader?

@JimBobSquarePants

Copy link
Copy Markdown
Member

replace magic numbers where possible
What magic numbers?

@Erik-White

Copy link
Copy Markdown
Contributor Author

replace magic numbers where possible
What magic numbers?

See this commit: e6f09d6
Just tried to use existing values where possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants