Skip to content
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

Preserve color profile when encoding PNG images #2110

Merged
merged 10 commits into from
May 12, 2022
Merged

Preserve color profile when encoding PNG images #2110

merged 10 commits into from
May 12, 2022

Conversation

brianpopow
Copy link
Collaborator

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

This PR changes the PNG encoder to preserve the color profile when encoding an image.

Related issue: #2107


using (var memoryStream = new MemoryStream(compressedData.ToArray()))
using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStream))
using (var inflateStream = new ZlibInflateStream(bufferedStream))
Copy link
Member

Choose a reason for hiding this comment

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

I think we can avoid the allocation here.

dotnet/runtime#24622 (comment)

src/ImageSharp/Formats/Png/PngDecoderCore.cs Outdated Show resolved Hide resolved
src/ImageSharp/Formats/Png/PngEncoderCore.cs Outdated Show resolved Hide resolved
Comment on lines +730 to +731
outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression.
Copy link
Contributor

Choose a reason for hiding this comment

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

Here the above "trick" doesn't make sense, as for slicing there's an argument validation, so we just would exchange one comparison for the bound-check against the argument validation. So no net-win.


var uncompressedBytes = new List<byte>(compressedData.Length);

// Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should we use the memory allocator to to create a bigger buffer to read the uncompressed data into here? using the scratch buffer feels like it could be very inefficient.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, lot's of small allocations. I would use the allocator to allocate a buffer of length Configuration.StreamProcessingBufferSize.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was thinking about this for a while now, but if we always use the allocator this could be also inefficient, if we have alot of small compressed text string's.

Maybe something like this:

Span<byte> destBuffer = this.buffer.AsSpan();
using IMemoryOwner<byte> foo = compressedData.Length > 1024 ? this.memoryAllocator.Allocate<byte>(1024) : null;
if (compressedData.Length > 1024)
{
   destBuffer = foo.GetSpan();
}

For the the buffer to uncomress into?

Copy link
Member

Choose a reason for hiding this comment

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

uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray());

It's actually this line that concerns me the most. Zlib compression is pretty good and you could end up with a large number of ToArray() calls. That's why I favour a much larger array and simply slicing it. Pooling is pretty cheap.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I dont see how this ToArray call can be avoided, though. We cannot pass a Span to AddRange, see dotnet1530

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe MemoryStream?

Can be used with spans and can expand like a list.
No virt calls as we would work with direct type.
Should be relatively fast as it uses Buffer.InternalBlockCopy for big buffers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@br3aker good idea, that will work: a0e38c8

Copy link
Member

@JimBobSquarePants JimBobSquarePants left a comment

Choose a reason for hiding this comment

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

Very cool 👍

@brianpopow
Copy link
Collaborator Author

brianpopow commented May 11, 2022

I wanted to merge this now, but two tests for net7.0 are failing now with the CI. Tried to restart two times the test run's, but its still failing.

I kind of doubt this is related to the PR changes.

  • Skew_IsNotBoundToSinglePixelType<Bgra32>(provider: TestPattern100x50[Bgra32], x: 20, y: 10)
  • Skew_IsNotBoundToSinglePixelType<Bgra32>(provider: TestPattern100x50[Bgra32], x: -20, y: -10)

@JimBobSquarePants
Copy link
Member

Definitely not us. There’s been a new .NET7 preview release. We should check the output and report any issues.

@brianpopow
Copy link
Collaborator Author

brianpopow commented May 12, 2022

Definitely not us. There’s been a new .NET7 preview release. We should check the output and report any issues.

I will merge this PR and create a tracking issue for the .NET7 issue: #2117

@brianpopow brianpopow merged commit 5634228 into main May 12, 2022
@brianpopow brianpopow deleted the bp/png-iccp branch May 12, 2022 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants