feature: [ANDROSDK-2371] revisit image compression logic - #2699
Open
taridepaco wants to merge 2 commits into
Open
feature: [ANDROSDK-2371] revisit image compression logic#2699taridepaco wants to merge 2 commits into
taridepaco wants to merge 2 commits into
Conversation
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



The image compression added earlier was based on a PoC. This reworks it to be production ready: it no longer trusts the file extension, supports a broader set of input formats, bounds the memory it uses, preserves the EXIF orientation, and converges on the target size by measuring instead of guessing.
Format handling. The format is now resolved from the image header rather than from the file name, so a JPEG named
.pngis no longer re-encoded as a PNG (which made it grow instead of shrink). The output format is chosen by scanning whether the bitmap actually contains translucent pixels — PNG when it does, JPEG otherwise — becauseBitmap.hasAlpha()only reports the presence of an alpha channel, not its use. Formats the server rejects (webp, heif, avif) are always converted, even when they are already below the target size. Since the resulting extension may differ from the input,FileResourceCollectionRepositorynow builds the resource name from the original name with the extension actually produced, keepingcontentType— which is derived from the name — in agreement with the bytes.Memory. Decoding is subsampled through
inSampleSizeand bounded to 4096 px on the largest side, retrying with a larger sample size onOutOfMemoryError. Previously the full sized bitmap was decoded, which for a 50 MP picture means around 200 MB as ARGB_8888.EXIF orientation. Re-encoding a bitmap drops the metadata, so an image that was only upright thanks to its orientation tag was being uploaded rotated. The rotation is now baked into the pixels. This uses the AndroidX
ExifInterfacerather than the platform one, because the latter only parses Jpeg metadata and heif images — which this path converts on purpose, as the server rejects them — would silently be reported as not rotated.Convergence. The previous strategy estimated a single scale from the size ratio and then decayed it by 20% for up to 10 iterations, writing the file to disk on every attempt. Now the image is encoded once at full resolution and, if that does not fit, a scale search runs: every attempt is encoded in memory and the next scale is predicted from that measurement, using the square root of the size ratio since the encoded size grows roughly with the pixel count. The prediction aims at 95% of the target so that an attempt landing just above it still makes progress instead of converging on the boundary from above; it settles in one or two attempts and is capped at four. Only the accepted result is written to disk. The quality is fixed at 85 and never lowered to reach the target: JPEG artifacts below that survive every later downscaling, while resolution above the dimension the image is displayed at is not used, so the budget is better spent on quality than on pixels. A floor of 256 px on the largest side keeps an unreachable target from degrading the image indefinitely.
No-ops. The original file is now returned untouched when it cannot be decoded, when it is already below the target in a format the server accepts, or when compressing it would not make it any smaller. When a new file is produced it is written to the SDK cache directory with
File.createTempFile, so that two images with the same name being added concurrently cannot overwrite each other, and the repository deletes it once the add completes.FileResizerHelper. It shares the new decoding primitives, so it also gets subsampled decoding and EXIF orientation. It now raises a
D2Errorwhen the file is not a decodable image instead of throwing an NPE, and its final downscaling pass is filtered.Tests cover the format conversion, the preservation of transparency, the conversion of a webp already below the target, the EXIF rotation, the minimum dimension floor, landing close to the target rather than well under it, and the absence of leftover temporary files after an add. There are no public API signature changes, so the API dump is unchanged.
Related task: ANDROSDK-2371