Skip to content

Fix Image.getSize failing for data: URIs on Android - #58353

Closed
ColeHuntley83 wants to merge 1 commit into
react:mainfrom
ColeHuntley83:export-D118657320
Closed

Fix Image.getSize failing for data: URIs on Android#58353
ColeHuntley83 wants to merge 1 commit into
react:mainfrom
ColeHuntley83:export-D118657320

Conversation

@ColeHuntley83

Copy link
Copy Markdown

Summary:
Fixes #57787.
Supersedes #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

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
@meta-codesync

meta-codesync Bot commented Sep 5, 2026

Copy link
Copy Markdown

@ColeHuntley83 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D118657320.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 5, 2026
@facebook-github-tools facebook-github-tools Bot added p: Facebook Partner: Facebook Partner labels Sep 5, 2026

@cortinico cortinico left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review automatically exported from Phabricator review in Meta.

@meta-codesync meta-codesync Bot closed this in 8cfde6d Sep 5, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Sep 5, 2026
@meta-codesync

meta-codesync Bot commented Sep 5, 2026

Copy link
Copy Markdown

This pull request has been merged in 8cfde6d.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【Android】【0.86.2】Image.getSize() fails for data: URIs on Android since v0.86

2 participants