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

Fix scaling for Bitmap resources with original size #1072

Merged
merged 5 commits into from Jan 30, 2022

Conversation

chao2zhang
Copy link
Contributor

Resolves #1053

When the requested size is Original, previously it would fall into (outWidth > 0 && outHeight > 0), where val dstWidth = width.pxOrElse { srcWidth } would always be evaluated to srcWidth, and srcWidth ignores the screen density.

This PR addresses the case of android resources specifically by applying the same logic handled inside BitmapFactory.decodeResourceStream().

Testing

https://github.com/chao2zhang/coil/tree/chao/fixresourcescale-test
Tested on 440dpi and 560dpi devices using the code snippet in the original issue. Coil loaded image and android native BitmapFactory displays the image of the same size.

Copy link
Member

@colinrtwhite colinrtwhite left a comment

Choose a reason for hiding this comment

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

Thanks for the fix! You'll need to run ./gradlew apiDump to update the public API file. Also do we need to make similar changes in the other decoders? Is it possible to avoid adding special cases to each decoder?

@@ -78,7 +79,15 @@ class BitmapFactoryDecoder @JvmOverloads constructor(
// Always create immutable bitmaps as they have performance benefits.
inMutable = false

if (outWidth > 0 && outHeight > 0) {
if (options.size.isOriginal && source.metadata is ResourceMetadata) {
Copy link
Member

Choose a reason for hiding this comment

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

This won't account for the case when only one dimension is Dimension.Original; also I'm not keen on adding a special case in BitmapFactoryDecoder only for resources. Maybe we can set the density after the bitmap is decoded and avoid creating a special case in BitmapFactoryDecoder?

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 won't account for the case when only one dimension is Dimension.Original
Good callout when one dimension is Dimension.Original. Do you think we need to support mixed Dimension.Original with Dimension.Pixel? Is this something we can regard as invalid?

Also do we need to make similar changes in the other decoders
I would need to check the behavior of ImageDecoderDecoder. Other decoders are probably not density-aware.

Is it possible to avoid adding special cases to each decoder? Maybe we can set the density after the bitmap is decoded and avoid creating a special case in BitmapFactoryDecoder?

  1. The problem with resetting the density after BitmapFactory.decode() is that, we probably need another bitmap transformation to scale the previous image to meet the dimension need.
  2. For additional special case, it might be necessary because Android BitmapFactory and ImageDecoder both expose specific API to decode native Resource. Resource is unique from other sources in that it is density-aware.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I played around with ImageDecoderDecoder and GifDecoder. They seem to be very different from the default BitmapFactory decoder - Therefore this PR might be better to scoped to only handle Bitmap. I have updated the PR title accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we need to support mixed Dimension.Original with Dimension.Pixel?

My colleague @CordulaMW created the issue #1053 for this PR, so I can comment on at least what we need: We don't need mixed Dimension values. We would either use Dimension.Original or Dimension.Pixel, never both at the same time.

I generally don't think it would make sense to support this, as it's afaik only for scaling. Why would someone want the height dpi-scaled but the width not?
As a counter argument, the android layout system does support it. Not sure.

@chao2zhang chao2zhang changed the title Fix scaling for resources with original size Fix scaling for Bitmap resources with original size Dec 31, 2021
@carstenhag
Copy link
Contributor

Hello there, I have tried out the PR and it works great for me at least :)

Copy link
Member

@colinrtwhite colinrtwhite left a comment

Choose a reason for hiding this comment

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

Sorry for the delay on this. I agree let's tackle this only for the case where both dimensions are Dimension.Original. We can "fix" the behaviour for single dimension later if someone has a strong argument that it should be handled differently than it currently is. Thanks again!

@colinrtwhite colinrtwhite merged commit ff7d52b into coil-kt:main Jan 30, 2022
colinrtwhite pushed a commit that referenced this pull request Oct 5, 2022
* Fix scaling for resources with original size

* API dump

* Refactor exif related from BitmapFactoryDecoder

* Extract scaling into a separate function

* Fix ktlint
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.

Png image size differs between different screen densities
3 participants