SIGN IN SIGN UP

[Android] Fix 1px edge clipping on Platform Views by preserving floating-point precision where possible and rounding otherwise instead of truncating (#190311)

Fixes https://github.com/flutter/flutter/issues/189834

It's kind of hard to see in the screenshots that Android studio device
mirroring produces, but there's a 1px green border of the underlay box
visible pre-change clearly on device. You can kind of see it if you
click the screenshot and zoom in.

Golden test screenshots:

| | Unclipped | Clipped |
| :--- | :---: | :---: |
| **Pre-change** | <img
src="https://github.com/user-attachments/assets/e1501fa4-d825-4ae0-a4d1-3c1d173e5694"
width="250" alt="Pre-change Unclipped" /> | <img
src="https://github.com/user-attachments/assets/46b58d99-5a3f-4c6f-b8c5-c19c880d937e"
width="250" alt="Pre-change Clipped" /> |
| **Post-change** | <img
src="https://github.com/user-attachments/assets/16a3a92a-d3b3-45ea-a2ce-caf438a8dd35"
width="250" alt="Post-change Unclipped" /> | <img
src="https://github.com/user-attachments/assets/18d7926e-45b9-41a9-a255-8ed1477d1055"
width="250" alt="Post-change Clipped" /> |



Ai description of the changes, which is accurate (i checked)


On Android devices with a fractional Device Pixel Ratio (`dpr`, e.g.,
`2.625` on 420dpi screens), converting between logical and physical
pixels in floating point often yields values just below a whole integer
(e.g., `1080 / 2.625 * 2.625 = 1079.999965`). Previously, the C++ Engine
and Java embedding truncated these values to integers
(`static_cast<int32_t>` / `(int)`), systematically shaving 1 physical
pixel off the right and bottom edges of Platform Views, clip rectangles,
and texture buffer sizes. This left a visible 1px line of underlying
background content around the view.

This PR fixes precision loss across three equivalence classes in the
Android Platform View composition pipeline:

### 1. Plumb floating-point precision where native APIs accept floats
(`RectF` / MethodChannel)
* **Mutator Stack Clipping**: Updated `pushClipRect` and `pushClipRRect`
in C++ JNI
([platform_view_android_jni_impl.cc](engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc))
and Java
([FlutterMutatorsStack.java](engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorsStack.java))
from integer `(IIII)V` to floating-point `(FFFF)V`, storing `RectF` and
passing floats directly to Android `Path.addRect(RectF)`.
* **Buffer Size Reporting**: Updated `PlatformViewBufferSize`
width/height from `int` to `double` and changed
`toLogicalPixels(double)` in
([PlatformViewsController.java](engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java))
to return fractional `double` values without integer casting or
rounding.

### 2. Round onto the integer grid where Android APIs require integers
* **View Layout & Sizing**: In C++
([external_view_embedder.cc](engine/src/flutter/shell/platform/android/external_view_embedder/external_view_embedder.cc)
and
[external_view_embedder_2.cc](engine/src/flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.cc)),
replaced integer truncation with rounding:
    *   View bounding boxes use `DlIRect::Round(GetViewRect(...))`.
* Physical width/height conversion uses `std::round(size_points * dpr)`
via `ToPhysicalPixels(...)`.

### 3. Sizing & Buffer Reporting by Composition Mode
([PlatformViewsController.java](engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java))
* **Virtual Display Mode**: Directly reports the requested logical
dimensions (`request.newLogicalWidth`, `request.newLogicalHeight`),
bypassing physical-to-logical round-trip conversion entirely.
* **Texture Layer Hybrid Composition (TLHC)**: Because TLHC render
targets only grow and never shrink, the controller reports the actual
unshrunk physical render target size as an exact fractional `double`
(`physicalPixels / getDisplayDensity()`). This allows the framework to
size the texture 1:1 and clip it to the widget rather than scaling it
down.

## Testing
* **C++ Unit Test**: Added `PlatformViewSizeIsRoundedNotTruncated` to
[external_view_embedder_unittests.cc](engine/src/flutter/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc)
verifying that full-screen dimensions (`1080x2400` at `dpr = 2.625`)
round to `1080, 2400` rather than `1079, 2399`.
* **Java Unit Tests**: Added
`resizeAndroidView_reportsFractionalBufferSize` and
`resizeAndroidView_reportsUnshrunkBufferSize` to
[PlatformViewsControllerTest.java](engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java).
* **Integration / Golden Test**: Added
[platform_view_fractional_size_main_test.dart](dev/integration_tests/android_engine_test/test_driver/hcpp/platform_view_fractional_size_main_test.dart)
and
[platform_view_fractional_size_main.dart](dev/integration_tests/android_engine_test/lib/hcpp/platform_view_fractional_size_main.dart)
with a green underlay box sized to `.75` physical pixels (`200.75`
unclipped, `150.75` clipped). Truncation deterministically exposes a 1px
green outline on any device, while rounding cleanly occludes the
underlay.
G
Gray Mackall committed
a539e0ed6058bafd94d964f090e0f7e692b3db27
Parent: 9448386
Committed by GitHub <noreply@github.com> on 8/19/2026, 2:22:25 PM