-
-
Notifications
You must be signed in to change notification settings - Fork 887
Introduce Shared General Decoder Options plus Specialization #2180
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
Changes from 29 commits
4099205
dfe04e3
d453922
c9beb02
3917b12
48a8106
22421db
653351c
d53ad0e
1c2d1f4
8d50996
6dbc5ec
730b215
ae2fe1a
688b9ef
a42101e
204fddd
c823323
1d9b209
8826e87
2355de6
01e497f
91a6413
37d020c
085ff1a
0472ffd
d29cf8a
07e8578
ac4fb62
0e13e6c
f78ec51
75b0e07
c5c1575
a4c14e7
acbc7bc
6242723
3b6b30a
efb2511
751bf48
52a26d1
92ac2d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Six Labors Split License. | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Bmp | ||
| { | ||
| /// <summary> | ||
| /// Configuration options for decoding Windows Bitmap images. | ||
| /// </summary> | ||
| public sealed class BmpDecoderOptions : ISpecializedDecoderOptions | ||
| { | ||
| /// <inheritdoc/> | ||
| public DecoderOptions GeneralOptions { get; set; } = new(); | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the value indicating how to deal with skipped pixels, | ||
| /// which can occur during decoding run length encoded bitmaps. | ||
| /// </summary> | ||
| public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } | ||
| } | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Six Labors Split License. | ||
|
|
||
| using System; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats | ||
| { | ||
| /// <summary> | ||
| /// Provides general configuration options for decoding image formats. | ||
| /// </summary> | ||
| public sealed class DecoderOptions | ||
| { | ||
| private static readonly Lazy<DecoderOptions> LazyOptions = new(() => new()); | ||
|
|
||
| private uint maxFrames = int.MaxValue; | ||
|
|
||
| /// <summary> | ||
| /// Gets the shared default general decoder options instance. | ||
| /// </summary> | ||
| internal static DecoderOptions Default { get; } = LazyOptions.Value; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. | ||
| /// </summary> | ||
| public Configuration Configuration { get; set; } = Configuration.Default; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the target size to decode the image into. | ||
| /// </summary> | ||
| public Size? TargetSize { get; set; } = null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we are missing tests validating various usages of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean validating the result of given dimensions? I would have thought that would already be covered by
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean end-to-end testing resize-decode the way users will work with the feature, including aspect-ratio preservation. The only setter call I see for Even though currently it's implemented with a simple
I only see unit tests for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, I've left actual image tests to the moment when API is ready as jpeg resize-decode entry point was somewhat of a stub. As that was internal we didn't risk exposing invalid code to users but this PR is a game changer. I know this solution isn't 'synchronized' but I'll write proper test suite for jpeg after API is set in stone.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine delaying it for jpeg, the implementation is somewhat special anyways, but we could add test for another decoder (eg. PNG) in this PR to validate the feature. Then later we can extend as we optimize decoders. |
||
|
|
||
| /// <summary> | ||
| /// Gets or sets a value indicating whether to ignore encoded metadata when decoding. | ||
| /// </summary> | ||
| public bool SkipMetadata { get; set; } = false; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the maximum number of image frames to decode, inclusive. | ||
| /// </summary> | ||
| public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a new feature. Are we sure it's valuable enough? (= were there any user asks and discussions about it?) To me it looks like asking for the first 10 frames of a gif/webp/tiff is a very specific use-case. (Why the first 10, why not eg. every second frame etc.)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is from the initial discussion at #2091 I wasn't 100% sure on it but nobody had argued against it and it was easy enough to implement. A
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like a tiny detail now, would come back to it when we have consensus about the big picture. |
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
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.
This is the only specialized decoder option class that actually contains a feature. All the others are dummies, their only purpose is to satisfy the pattern introduced. I believe this is alarming.
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 wanted something that was guaranteed to handle some of the more exotic codes that we'll end up implementing so that we don't have to further breaks. I can imagine both AVIF and Jpeg-XL requiring customization.
I'm actually very surprised we don't already have something for Tiff and I believe that is most likely an oversight thus far.
Uh oh!
There was an error while loading. Please reload this page.
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.
Even with Bmp + Tiff + AVIF we would have too much of boilerplate of generics and unnecessary empty classes IMO. And Tiff + AVIF are purely hypothetical at the moment, we don't have a proof this feature really needed & will be in fact implemented in the near future. I would prefer a design, where we could YAGNI this, and postpone creating specialized types until the moment they are proven to be actually needed for a format.
On top of that, I realized now, that we are creating an API disparity between decoders and encoders:
Decoders are becoming stateless objects with this PR with separate types for decoding options, while encoder objects carry the encoding options.
I know that I was an advocate of stateless decoders/encoders, but now I disagree with my past self and it feels like the simplest thing would be to move the format specific options back to the decoder objects, so users could do specialized decoding the following way:
Alternatively, we can also consider turning the current abstaction into a pattern, but this would not solve the decoder-encoder API disparity:
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'll end up doing something similar to encoders anyway.
How would you enforce the options pattern for specialized decoders? I really want to ensure that for specialization we are forced into following something.
The problem I see with the pattern above is that now you actually have more boilerplate for non-specialized decoding since you now have to implement the resize functionality for each concrete
Decodeimplementation.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.
Maybe we can revert the specialized options and make the decoder stateful again?
DecodeSpecializedis simply the concrete implementation and each decoder then becomes responsible for passing addition options to the core.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.
Actually, it can be useful as a potential solution to the problem described in #2180 (comment).
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, I guess that could be simply an interface unless we implement overloads. The
ImageDecoderactually handles the meat now.The reason it's verbose is so I can do this with the
JpegDecoder. We need a method that allows IDCT resize only and creating aJpegDecoderOptionstype as you've identified is not great.Uh oh!
There was an error while loading. Please reload this page.
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.
To me it looks very unfortunate to use the general
DecoderOptionsas specialized options just to tell that a user wants anIDCTresize only. If I would look for such API the last thing I would expect that I just simply need to call a method calledDecodeSpcializedwith the same args.I would rather introduce
JpegDecoderOptionswith abool IdctResizeproperty, or create another, jpeg-spcificDecodeIdctResizemethod, or simply keep the "IDCT resize only" feature out of the public API. It's a niche use-case users would only use if they are unhappy with our defaultTargetSizedecoding and need better control on the Resize phase. (Which IMHO would mean that we are doing something wrong and maybe we should reconsider exposingResizeOptions.)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.
If we want maximum flexibility, I would consider something like this for Jpeg:
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.
OK. I love this!