Fix Image.getSize failing for data: URIs on Android - #58353
Closed
ColeHuntley83 wants to merge 1 commit into
Closed
Conversation
Summary: Fixes react#57787. Supersedes react#57788. ## Context On Android, `Image.getSize()` and `Image.getSizeWithHeaders()` reject for every `data:` URI. Both resolve dimensions through Fresco's encoded-image pipeline. That pipeline has producer sequences only for network, local-file and local-content URIs; every other scheme falls through to a throw: Unsupported uri scheme for encoded image fetch! Uri is: data:image/jpg;base64,... Fresco's decoded-image pipeline does handle the scheme (`SOURCE_TYPE_DATA -> dataFetchSequence`), so the capability exists — the encoded entry point simply does not expose it. A previous change added a fast path for `res://` URIs for exactly this reason; `data:` was never given one. Any app that gates rendering on `getSize` therefore cannot display an inline base64 image on Android at all, while iOS is unaffected. ## This Diff - Adds a `data:` fast path to `getSize` and `getSizeWithHeaders`, mirroring the existing resource-drawable fast path, routed through `fetchDecodedImage` rather than `fetchEncodedImage`. - Pins the request to auto-rotate so it produces the same Fresco bitmap cache key that `ReactImageView` builds for the same URI. Rotation options are part of that key, so a mismatch here would decode into a key nothing reads and force a second decode at render time. - Sets `DownsampleMode.NEVER` so the reported dimensions stay intrinsic rather than post-downsample, preserving the behaviour the encoded path was originally adopted for. That option is absent from the bitmap cache key, so it does not disturb the parity above. - Reads the visible dimensions straight off the decoded image, which has already had its EXIF rotation applied, rather than repeating the axis swap the encoded subscriber performs by hand. `BaseCloseableStaticBitmap` applies that same predicate internally, so repeating it here would double-apply it. - Adds unit coverage for the `data:` scheme, which had none: decoded-pipeline routing, cache-key parity with `ReactImageView`, intrinsic dimensions, the headers variant, and both failure paths. - Adds two `data:` rows to the RNTester `Image.getSize` platform test — one plain, one tagged EXIF orientation 6 — so a real decode, rather than a mocked pipeline, proves the reported dimensions are the visible ones. Both are inline base64, so they need no network. Because the decode now warms the bitmap memory cache under the key the `<Image>` subsequently reads, a caller that sets its image source from the `getSize` success callback paints from cache instead of decoding at render time. ## Alternatives Considered **Parse the dimensions out of the base64 header.** Satisfies the `getSize` contract and is cheaper, but warms nothing. Callers that relied on the decode side effect for smooth playback would keep re-decoding every frame at render time — it fixes the rejection without fixing the regression that accompanied it. **Add a `data:` arm to Fresco's encoded producer sequence.** Pushes a behavioural change into shared image infrastructure used by every app, to serve a caller that wants a decoded result anyway. The narrower fix belongs on this side. **Have callers stop gating rendering on `getSize`.** Makes the image appear, but permanently discards the decode-then-render ordering, leaving the surface visibly flickering. Changelog: [Android][Fixed] - Fix `Image.getSize()` and `Image.getSizeWithHeaders()` rejecting `data:` URIs Reviewed By: Abbondanzo, javache, cortinico Differential Revision: D118657320
|
@ColeHuntley83 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D118657320. |
cortinico
approved these changes
Sep 5, 2026
cortinico
left a comment
Contributor
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
|
This pull request has been merged in 8cfde6d. |
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.
Summary:
Fixes #57787.
Supersedes #57788.
Context
On Android,
Image.getSize()andImage.getSizeWithHeaders()reject for everydata:URI.Both resolve dimensions through Fresco's encoded-image pipeline. That pipeline has producer
sequences only for network, local-file and local-content URIs; every other scheme falls through to
a throw:
Unsupported uri scheme for encoded image fetch! Uri is: data:image/jpg;base64,...
Fresco's decoded-image pipeline does handle the scheme (
SOURCE_TYPE_DATA -> dataFetchSequence), sothe capability exists — the encoded entry point simply does not expose it. A previous change added a
fast path for
res://URIs for exactly this reason;data:was never given one.Any app that gates rendering on
getSizetherefore cannot display an inline base64 image onAndroid at all, while iOS is unaffected.
This Diff
data:fast path togetSizeandgetSizeWithHeaders, mirroring the existingresource-drawable fast path, routed through
fetchDecodedImagerather thanfetchEncodedImage.ReactImageViewbuilds for the same URI. Rotation options are part of that key, so a mismatchhere would decode into a key nothing reads and force a second decode at render time.
DownsampleMode.NEVERso the reported dimensions stay intrinsic rather thanpost-downsample, preserving the behaviour the encoded path was originally adopted for. That
option is absent from the bitmap cache key, so it does not disturb the parity above.
rotation applied, rather than repeating the axis swap the encoded subscriber performs by hand.
BaseCloseableStaticBitmapapplies that same predicate internally, so repeating it here woulddouble-apply it.
data:scheme, which had none: decoded-pipeline routing, cache-keyparity with
ReactImageView, intrinsic dimensions, the headers variant, and both failure paths.data:rows to the RNTesterImage.getSizeplatform test — one plain, one tagged EXIForientation 6 — so a real decode, rather than a mocked pipeline, proves the reported dimensions
are the visible ones. Both are inline base64, so they need no network.
Because the decode now warms the bitmap memory cache under the key the
<Image>subsequentlyreads, a caller that sets its image source from the
getSizesuccess callback paints from cacheinstead of decoding at render time.
Alternatives Considered
Parse the dimensions out of the base64 header. Satisfies the
getSizecontract and is cheaper,but warms nothing. Callers that relied on the decode side effect for smooth playback would keep
re-decoding every frame at render time — it fixes the rejection without fixing the regression that
accompanied it.
Add a
data:arm to Fresco's encoded producer sequence. Pushes a behavioural change into sharedimage infrastructure used by every app, to serve a caller that wants a decoded result anyway. The
narrower fix belongs on this side.
Have callers stop gating rendering on
getSize. Makes the image appear, but permanentlydiscards the decode-then-render ordering, leaving the surface visibly flickering.
Changelog:
[Android][Fixed] - Fix
Image.getSize()andImage.getSizeWithHeaders()rejectingdata:URIsReviewed By: Abbondanzo, javache, cortinico
Differential Revision: D118657320