-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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: prevent PNG to JPG conversion on image crop #17840
base: main
Are you sure you want to change the base?
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.
should handle webp while we're in here I think. If we're going from "one to many", and the maximum number of "many" is 3, going from 1->3 cases instead of just 1->2 isn't too much of a stretch
cropImageView.croppedImageAsync() | ||
if (isImagePng()) { | ||
cropImageView.croppedImageAsync( | ||
saveCompressFormat = Bitmap.CompressFormat.PNG, |
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.
as long as we are in here, there is also Bitmap.CompressFormat.WEBP
(it is deprecated but the WEBP_LOSSY / WEBP_LOSSLESS symbols are only available in API30+ and WEBP is since 14 - so we could start by at least just supporting the WEBP variant and accepting deprecation fix in another PR
I think it would be instructive to see if a WEBP with transparency had the transparency preserved if you altered the method below from isImagePng()
to getImageCompressFormat()
and had it return the Bitmap.CompressFormat
type or similar, so it could handle jpg, png, and webp at the same time.
Ideally that would preserve transparency in WEBP as well
learning:
https://developer.android.com/reference/android/graphics/Bitmap.CompressFormat#WEBP
appears that our cropper is aware of it https://github.com/search?q=repo%3ACanHub%2FAndroid-Image-Cropper%20webp&type=code
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 support for output compression types appears fine, but the testing seems completely inadequate.
I used pngs and webps from this gallery here to test transparency preservation https://developers.google.com/speed/webp/gallery2
After crop they appear to come back as jpegs
I must admit - and this is some very pointed criticism I need to make: the original comment where you duplicated the research effort, and this PR that you requested my time to review but failed a basic "I looked at it at least" check prior to requesting my review - they both look very low quality to me and they have wasted my time
untitled.mp4
In combination with the 45 back-and-forths on the auto-scroll PR where QA was repeatedly lacking, this is a strong low-quality impression I've got now. I'm very hesitant to spend more time reviewing anything
"image/jpeg" -> Bitmap.CompressFormat.JPEG | ||
"image/webp" -> { | ||
if (Build.VERSION.SDK_INT >= 30) { | ||
Bitmap.CompressFormat.WEBP_LOSSLESS |
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.
having read all the associated discussion and docs just a bit ago, I think this (using WEBP_LOSSLESS alone vs WEBP_LOSSY or trying to use both) is the safest choice.
- there is no good way to easily determine if a webp is lossless or lossy, so choosing one is best
- there is not a huge expansion in size taken if a webp is lossless vs lossy, so choosing LOSSLESS as the single one to use does not have much of a downside but conservatively preserves quality, which respects the user
so I think this is a reasonable choice despite it seeming like there should be support for both of the new API30+ symbols (LOSSLESS and LOSSY)
Thank you for the feedback, but I’d like to address a few points: Testing and Results: I did thorough testing on my side, and the changes were working fine, including transparency preservation. I’ll attach a video to demonstrate the results shortly. However, I request you to ensure that the latest changes from my branch were pulled into your local environment before testing, as I built upon them and confirmed their functionality. On QA and Effort: Your feedback about the auto-scroll PR and the recycler view crash issue is noted. For the auto-scroll, I iterated based on DavidA's suggestions and put in significant effort to address all concerns. Regarding the recycler view crash, I accept that I couldn’t reproduce the error, but i tested and tried as you said , i did followed the constraints and all . I’ll keep working to improve my QA process and testing rigor. Regarding Tone: I must admit that your message felt very hurtful and unnecessarily harsh. While I fully accept constructive criticism and want to learn from mistakes, such language can discourage collaboration. I genuinely value your time and feedback and have no intention of wasting it. WhatsApp.Video.2025-01-18.at.07.03.53_8114ff52.mp4 |
I did, I compared the commit hash carefully, because I couldn't believe it and immediately suspected a problem with my testing methodology or tools. That definitely happens. I'm sorry if my comment had a harsh tone, but I was quite shocked to pull the branch, run it and immediately see that it failed to solve the issue. That's a very worrisome thing. The trust we have between ourselves as developers is built on the quality of our code. I'm not seeing that and you need to know it. |
my debug info
and also tested on idk why it is not working in your device , please share your debug info |
Same git SHA running. It's the API35 emulator for 16k stack pages (I test that occasionally as it is the future for a lot of devices, it doesn't have any known problems with AnkiDroid and hasn't for months though...)
You can see in my video the steps I took, I thought it was telling that the image kept coming back after crop as jpg Is it a different pathway perhaps? Android files app - select image - share to ankidroid as "add image" ? |
@mikehardy This is what issue raiser wanted in #17817 "What are the steps to reproduce this bug? When Sharing from the Gallery: Appologies if i am misundertanding anything, thanks. |
Don't deduce and make assertions based on the deduction. Make a hypothesis and test it Hypothesis: the image is converted to JPEG during the share and AnkiDroid only ever sees it as JPEG Test: find the code pathway that accepts the image when shared to AnkIDroid and log it's type. Follow that same path through to where the cropper is called on it and log it's type Report findings That's the process I use, except I usually don't "report findings" exactly, I just include them in the PR description / commit messages that contain the fix(es), along with all steps taken to reproduce my findings while testing and that justify the various changes I made |
PNG.test.mp4this has been tested. I am sorry for earlier inconvinience, i was testing by the steps user produced the bug and did not thought of the case when image is shared form android file apps, so when the tests were sucessful i thought it was good to push, did not wanted to waste your time. |
one more case I forgot about until now, "paste image from clipboard":
At this point you should have a webp with transparency saved in the clipboard
It appears to paste as a JPEG? There may be nothing we can do to handle this case, I'm not sure how the paste code works - I don't know if it can only paste JPGs or if it can paste PNGs and WEBPs as well, but it seems like it should paste the image without modification into the field, which then allows cropping without losing transparency At this point if it is getting into the paste code, it is starting to get a bit far away from the original bug - but the point of software that really tries to help users vs just makes money is to solve the problem the user faced plus all related problems if you "generalize" the user request. And the general user request is "AnkiDroid should respect image transparency" - so...pasting a transparent image really should work 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.
-
possible issue getting type from content resolver - left a comment pointing at the utility to use in place of the type resolution proposed here
-
does not appear to handle pasting transparent images, unsure if that can be handled, needs investigation
-
there was some mention of tests? not sure if they were automated or not, however if ContentResolverUtil is used, then the rest of the automated tests would be about picking or pasting or sharing transparent images and I'm not sure how to do that myself, so manual testing may be the only way
This does handle both picking and sharing transparent images in the basic cases which is good. Complex sharing cases (like from google photos when content is in the cloud) will probably fail with ContentResolver problems until the util code is swapped in here
private fun getImageCompressFormat(uri: Uri?): Bitmap.CompressFormat { | ||
Timber.d("Original image URI: $uri") | ||
val mimeType = uri?.let { requireContext().contentResolver.getType(it) } | ||
|
||
val fileExtension = | ||
if (uri?.scheme == "file") { | ||
uri.path?.let { getFileExtensionFromPath(it) } | ||
} else { | ||
mimeType | ||
} |
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.
Before adding code, definitely ask "has anyone done this before? if so, is there existing code already?"
The answer in this case is yes and yes! You would not believe how hard it is to get the extension from a content resolver. We have had crashes in this area, and bugs, it is very subtle, and you only see the problems enough to even know there are problems when you have millions of users.
This is the method to use - ContentResolverUtil.getFileName
, then grab the extension off that. Note that it can throw an exception so it should be in a try/catch with some sort of "I give up but I won't crash" handling and a Timber.w
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.
seems minimal and complete now
appears to work in my testing when using the picker to select a webp with transparency, or to send a webp with transparency to ankidroid from android files app
Purpose / Description
Fixes PNG to JPG conversion issue during image cropping, preserving the original format (PNG) for transparency and quality retention. Ensures the cropping process doesn't alter the image format unintentionally.
Fixes
Approach
This change checks the original image format (PNG) before cropping and preserves it during the process. It ensures that PNG images are cropped without being converted to JPG, maintaining transparency and quality.
How Has This Been Tested?
Samsung F23 5G
Checklist
Please, go through these checks before submitting the PR.