-
-
Notifications
You must be signed in to change notification settings - Fork 844
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
Normalize and cleanup encoders #2269
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have time for a proper look but it definitely looks interesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just eye-rolled product code changes.
src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Günther Foidl <gue@korporal.at>
…SamplingStrategy.cs Co-authored-by: Günther Foidl <gue@korporal.at>
Will take a look at this within a day or two! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My review assumes that we are OK with the general design direction, pointing out a few minor issues, while the PR looks mostly good.
However:
While I do understand the differences in the requirements, I'm still not happy with the decoder-encoder API asymmetry. It feels weird, and I still think we can do better. We should re-evaluate the tradeoffs of different decoder/encoder design strategies.
That should be another iteration/discussion though, since this PR contains way too many changes better to be merged sooner than later.
public interface IEncoderOptions | ||
{ | ||
/// <summary> | ||
/// Gets a value indicating whether to ignore decoded metadata when encoding. | ||
/// </summary> | ||
bool SkipMetadata { get; init; } | ||
} | ||
|
||
/// <summary> | ||
/// Defines the contract for encoder options that allow color palette generation via quantization. | ||
/// </summary> | ||
public interface IQuantizingEncoderOptions : IEncoderOptions | ||
{ | ||
/// <summary> | ||
/// Gets the quantizer used to generate the color palette. | ||
/// </summary> | ||
IQuantizer Quantizer { get; init; } | ||
|
||
/// <summary> | ||
/// Gets the <see cref="IPixelSamplingStrategy"/> used for quantization when building color palettes. | ||
/// </summary> | ||
IPixelSamplingStrategy PixelSamplingStrategy { get; init; } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find any code utilizing these abstractions in a meaningful way. Wouldn't it be better to drop them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, happy to use the base classes instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
/// <summary> | ||
/// The base class for all image encoders that allow color palette generation via quantization. | ||
/// </summary> | ||
public abstract class QuantizingImageEncoder : ImageEncoder, IQuantizingEncoderOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Note on asymmetry, not to be addressed.] Also weird that we have subclassing while with decoders the structure is flat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually want to subclass in the decoders. Just trying to figure out a nice way.
@@ -181,7 +173,7 @@ public new ImageFrameCollection<TPixel> Frames | |||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the provided (x,y) coordinates are outside the image boundary.</exception> | |||
public TPixel this[int x, int y] | |||
{ | |||
[MethodImpl(InliningOptions.ShortMethod)] | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for the InliningOptions -> MethodImplOptions changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should be using the custom methods more sparingly on areas we're actively profiling. (I recall you saying similar a few years back now ;))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense for these 2 methods actually.
{ | ||
var encoder = new BmpEncoderCore(this, image.GetMemoryAllocator()); | ||
BmpEncoderCore encoder = new(this, image.GetMemoryAllocator()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easy to overlook important semantic changes if there are so many style refactorings in the same PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but every time I open a file just now there's a heap of warnings from the new rules. I'm adopting the boy scout rule.
src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs
Outdated
Show resolved
Hide resolved
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image.Frames.RootFrame); | ||
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand it correctly that this introduces an optimization for single-frame gifs? Can't remember why didn't I add it originally single-frame, but would be nice to see a performance comparison. I assume there is no impact on the output quality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. It is but it only affects 4K or greater images by default. I didn't introduce a change to the default dimension limit as I didn't want to affect quality on smaller images.
@@ -101,4 +103,55 @@ Buffer2DRegion<TPixel> GetRow(int pos) | |||
} | |||
} | |||
} | |||
|
|||
/// <inheritdoc /> | |||
public IEnumerable<Buffer2DRegion<TPixel>> EnumeratePixelRegions<TPixel>(ImageFrame<TPixel> frame) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The multi-frame overload has some unit tests, might be worth to test this one too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Prerequisites
Description
There's no point trying to copy the options pattern that we introduced in #2180 as the requirements are different.
Instead, I've refactored the encoders to do the following
ImageEncoder
andQuantizingImageEncoder
with shared, preconfigured options properties. Two new shared options propertiesSkipMetadata
andPixelSamplingStrategy
have been introduced into the respective types.QuantizingImageEncoder
instance to use the sampling strategy from the options. This applies to local and global palette generation.