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

Make upscaling opt in for image decoding #19067

Merged
merged 11 commits into from
Jun 18, 2020

Conversation

dnfield
Copy link
Contributor

@dnfield dnfield commented Jun 16, 2020

Description

Make upscaling images using the targetWidth and targetHeight opt-in.

Users are encouraged to use this API to have better memory management of their images. They don't typically want the image to be upscaled - this patch adds a parameter for that and defaults it to false.

It would be nice to land #19062 before this to avoid merge conflicts, but this is otherwise ready for review.

Related Issues

flutter/flutter#59578
flutter/flutter#48885

Tests

I added the following tests:

C++ test for image decoding using the parameter set to true and false
Dart tests opted in and out

Breaking Change

Did any tests fail when you ran them? Please read handling breaking changes.

  • No, no existing tests failed, so this is not a breaking change.

Based on my understanding of the current compatibility policy, this is not breaking - although I did change some existing tests. However, this will not break third policies or google3 AFAICT.

@dnfield dnfield changed the title Instantiate image codec upscale Make upscaling opt in for image decoding Jun 16, 2020
@dnfield dnfield marked this pull request as ready for review June 16, 2020 22:18
@auto-assign auto-assign bot requested a review from gw280 June 16, 2020 22:19
Copy link
Member

@zanderso zanderso left a comment

Choose a reason for hiding this comment

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

lgtm w/ nits.

_ImageInfo? imageInfo,
int targetWidth,
int targetHeight,
bool allowUpscaling) native 'instantiateImageCodec';
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -42,11 +42,22 @@ static double AspectRatio(const SkISize& size) {
// intrinsic dimensions of the image.
static SkISize GetResizedDimensions(SkISize current_size,
std::optional<uint32_t> target_width,
std::optional<uint32_t> target_height) {
std::optional<uint32_t> target_height,
bool allow_upscaling) {
Copy link
Member

Choose a reason for hiding this comment

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

style nit: Consider replacing bool argument with an enum: https://google.github.io/styleguide/cppguide.html#Function_Argument_Comments

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@dnfield dnfield added perf: memory Performance issues related to memory waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. labels Jun 17, 2020
Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

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

Other than the undefined behavior around explicitly aspect-ratio overriding decodes, looks good. Thanks.

target_width = std::min(current_size.width(),
static_cast<int32_t>(target_width.value()));
}
if (target_height)
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Missing braces.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (current_size.isEmpty()) {
return SkISize::MakeEmpty();
}

if (image_upscaling == ImageUpscalingMode::kNotAllowed) {
if (target_width) {
Copy link
Member

Choose a reason for hiding this comment

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

This is a breaking change for this one (admittedly obscure) use case: If the user explicitly wants to decode the image at a non-native aspect ratio, they would specify both the target width and height. Before, the decoder would always return images at that aspect ratio. Now, that aspect ratio will not be preserved if the dimensions along either axis exceed the intrinsic value.

I think a clarification of whether the scaling will be done in an aspect ratio preserving manner is warranted in the documentation as well as a test case in image_decoder_unittests.cc (let's add a test even if decide not to preserve the aspect ratio just so the behavior is not undefined).

If you do decide to preserve scale, you can account the specification of both in another conditional check here.

I know this is an obscure case, but let's avoid undefined behavior as much as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point.

My grand plan is to follow this up with another PR to add something to control how aspect ratios are or are not preserved. I was originally thinking that this could be separated out from that, but I think you're right that it can't.

I'll take a quick look at how much more complicated this PR gets to add that extra parameter, otherwise I'll look to clarify the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, if a user wants that case they have to just set the allowUpscaling bit to true right?

But yes, we should document and test this.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we have sort of tacked on stuff to this original simple API in bits and pieces as we went along. This has understandably become a bit unwieldy.

I'll take a quick look at how much more complicated this PR gets to add that extra parameter...

Instead of doing all the design work to further update this API with additional options, I am fine if we just say "this will/won't preserve aspect ratio if both dimensions are specified" and add a test for this. As I said, my comment was more about the undefined behavior. This is still an improvement for a vast majority of the cases.

In the long run, an API that works with image descriptors without decompression is the way to go. But that is a lot more work to write and migrate to.

Copy link
Member

Choose a reason for hiding this comment

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

if a user wants that case they have to just set the allowUpscaling bit to true right?

Yeah, there is a workaround. Let's just document and test the default. My only concern still is that since the default has changed, earlier, they didn't need to specify the argument but now they have to.

How about just preserving the aspect ratio while scaling down if both width and height are specified? That ought to be really straightforward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, the more I look at this, the more I think it's just a doc and test thing. Whether or how we add other aspect ratio parameters here will still be relevant to this case.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good.

->dimensions(),
SkISize::Make(6, 2));
}

TEST(ImageDecoderTest, VerifySimpleDecodingNoUpscaling) {
Copy link
Member

@chinmaygarde chinmaygarde Jun 17, 2020

Choose a reason for hiding this comment

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

Per my comment earlier, a test case here about aspect ratio overwriting scales. So something like decode at 1200x100 with ImageUpscalingMode::kNotAllowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

int? targetWidth,
int? targetHeight,
bool allowUpscaling = false,
Copy link
Member

@chinmaygarde chinmaygarde Jun 17, 2020

Choose a reason for hiding this comment

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

This is going to be a breaking change. Not sure we want to do the dance of keeping it true and flip it later. Your call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the Flutter compatibility policy, we would have to break a contributed test (including internal). I did not do a full tgp but I believe this is not breaking by that definition. I understand that is a change in default behavior though.

I suppose to prevent any need to roll back I can default it to true for now, and then we can go back and default it to false once we can test it internally a bit more easily.

Copy link
Member

Choose a reason for hiding this comment

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

This is a new API, so there can't be a test that breaks right? Just felt it might be in violation of the "spirit" of the compatibility policy if not the letter. Either way, your call. It was just an observation about the defaults changing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There could be a test somewhere that's using the targetWidth/Height to upscale the image currently, and that test would start failing. If that test is registered in flutter/tests or in google3, it counts as a breaking change.

I'm going to see if I can run this through a TGP and make sure it doesn't break anything. If not, I'm going to avoid the dance of changing defaults around - this is clearly what most users want.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good.

@dnfield dnfield removed the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Jun 17, 2020
if (current_size.isEmpty()) {
return SkISize::MakeEmpty();
}

if (image_upscaling == ImageUpscalingMode::kNotAllowed) {
if (target_width) {
Copy link
Member

Choose a reason for hiding this comment

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

Sounds good.

@dnfield
Copy link
Contributor Author

dnfield commented Jun 18, 2020

This is failing a framework side test that explicitly upscales the image. I'm going to swap this so that it's defaulting to true, then we can opt that test in, and change this to false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes perf: memory Performance issues related to memory
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants