Skip to content

fix(android): build renderInto's destination Rect from edges, not size - #164

Open
dazakdev wants to merge 1 commit into
mrousavy:mainfrom
dazakdev:fix/render-into-destination-rect
Open

fix(android): build renderInto's destination Rect from edges, not size#164
dazakdev wants to merge 1 commit into
mrousavy:mainfrom
dazakdev:fix/render-into-destination-rect

Conversation

@dazakdev

@dazakdev dazakdev commented Aug 10, 2026

Copy link
Copy Markdown

What

HybridImage.renderInto() builds its destination rectangle as Rect(x, y, width, height). android.graphics.Rect takes (left, top, right, bottom), so width/height land in the edge slots: the drawn size becomes (width βˆ’ x) Γ— (height βˆ’ y), and once x >= width the rect inverts and nothing is drawn at all. Only x = y = 0 is unaffected, which is why existing usage looks fine.

These arguments are a size, not edges:

  • Image.nitro.ts documents them as one β€” "at the given x and y position, scaled to the given width and height"
  • iOS implements them that way β€” NativeImage.swift uses CGRect(x:y:width:height:)
  • the API that does take edges is named for it β€” crop(startX, startY, endX, endY)

Drawing a 200x200 sprite into a 400x400 image at (100, 100): documented 200x200, Android drew 100x100. At (300, 300), nothing was drawn.

Fix

val rect = Rect(x.toInt(), y.toInt(), (x + width).toInt(), (y + height).toInt())

No API change, iOS untouched.

Test

image-transforms.harness.ts covered resize, crop, rotate and mirrorHorizontally but had no renderInto case, which is why this went unnoticed. Added one: a red sprite drawn at (20, 20) sized 60x60, three sampled pixels. The pixel reader is local to the test β€” the equivalent helper in image-raw-pixel-data.harness.ts is file-local and not exported.

On main it fails on the far corner (expected false to be true β€” the pixel at (75, 75) is still background); with the fix applied it passes. Verified by stashing the Kotlin change, rebuilding the app and re-running, so the test is known to exercise the changed code path.

Run locally on an arm64 API 35 emulator rather than CI's x86_64. No iOS code is touched, so the iOS harness is left to CI.

Note for consumers

This is a behaviour fix. Code that compensated for it on Android by passing (x + width, y + height) must drop the compensation.

Standalone repro, without cloning the monorepo: https://github.com/dazakdev/nitro-image-renderinto-repro

Right now it looks like this:

image image

πŸ€– Generated with Claude Code

android.graphics.Rect takes (left, top, right, bottom), so passing `width`
and `height` into the right/bottom slots drew the image at (width - x) by
(height - y), and drew nothing at all once x >= width. The spec documents
these arguments as a size ("scaled to the given width and height") and iOS
implements them that way with CGRect(x:y:width:height:), so Android was the
outlier.

Adds the renderInto coverage that would have caught it: image-transforms had
no test for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dazakdev
dazakdev marked this pull request as ready for review August 10, 2026 10:30
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.

1 participant