Skip to content

UberSDF rect handling for thin (line-like) rectangles#188821

Merged
auto-submit[bot] merged 18 commits into
flutter:masterfrom
b-luk:ubersdfthinrect
Jul 9, 2026
Merged

UberSDF rect handling for thin (line-like) rectangles#188821
auto-submit[bot] merged 18 commits into
flutter:masterfrom
b-luk:ubersdfthinrect

Conversation

@b-luk

@b-luk b-luk commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Implements a custom filledRectSDF function in UberSDF with some custom behavior in order to properly handle dimensions of around 1 pixel and less.

Similar thin-shape logic may also be applicable for other shapes in UberSDF. But I'm limiting this PR to just change the behavior for filled rectangle behavior. This is the most common use of very thin shapes (thin rects are often used for lines). Other shapes being drawn thinly would be rare corner cases, I'd expect. If we want to consider other shapes, that should be a follow-up.

This change adds gamma correction to the alpha of all SDF shapes. So I expect a lot of golden changes. There will be changes small color changes to the AA fringe of every shape.

Factors out common code in the existing HairlinePath and HairlineDrawLine playground/golden tests. Expands these with new versions that draw thin (width=0.3), rotated thin, and one pixel lines. And adds "Rect" versions which draw the same shape with drawRect instead of drawPath or drawLine.

Fixes #188591

Screenshot

UberSDF drawRect with this PR, using the demo app from #188590 which draws lines at every incremental 0.01 offset::
image

Compare with the screenshots of various drawing lining methods in #188590. The new lines have very consistent brightness, and have no disappearing issues.

Videos

Using the hairline/thinline drawing playground tests.

Note that github's UI seems to not allow showing the video at actual size, so what you see inline is not completely accurate. But it's pretty close. If you want to see what it looks like at full size, I think you'll have to download the videos.

UberSDF drawRect with this PR:

drawrect.-.ubersdf.new.mov

UberSDF drawRect existing behavior:

drawrect.-.ubersdf.old.mov

LineContents AA drawline:

drawline.-.linecontents.mov

Tesselation drawRect

drawrect.-.tesselation.mov

Tesselation drawLine

drawline.-.tesselation.mov

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jul 1, 2026
@b-luk
b-luk force-pushed the ubersdfthinrect branch from 9e8bd5c to edd8fe8 Compare July 1, 2026 00:04

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request refactors line-drawing unit tests in aiks_dl_path_unittests.cc using a new helper function and adds support for rendering sub-pixel filled rectangles and gamma-corrected alpha in uber_sdf.frag. The review feedback highlights critical issues in the shader, including potential division by zero and NaN propagation in filledRectSDF when the rectangle size is zero, and a potential NaN propagation in gammaCorrectedAlpha if the alpha value exceeds 1.0. Additionally, the feedback points out that the refactored test helper is missing rotation and translation transformations, which prevents the rotated test cases from functioning correctly.

Comment thread engine/src/flutter/impeller/entity/shaders/uber_sdf.frag Outdated
Comment thread engine/src/flutter/impeller/entity/shaders/uber_sdf.frag
Comment thread engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. e: impeller Impeller rendering backend issues and features requests labels Jul 1, 2026
@b-luk
b-luk requested review from flar and gaaclarke July 1, 2026 00:30
@b-luk

b-luk commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Goldens have completed: https://flutter-gold.skia.org/search?issue=188821&crs=github&patchsets=2&corpus=flutter

As expected, there are lots of changes

  • The entire first page of 50 results are the new line drawing variations that I added.
    • Do we want all of these? I'm open to removing some if people think this is too much.
  • The rest of the changes are all very subtle changes to AA edges, with a couple exceptions.

There are two goldens where I noticed changes which are less subtle:

StrokedRectsRenderCorrectly

image

After this PR, the yellow line is noticeably much brighter than both the before and the non-sdf versions.

I think the brighter version after this PR is more correct than the other versions.

The test draws the yellow strokes as hairline (width = 0.0, which becomes exactly 1 pixel) with the color 0xFFFFFF00 (full brightness yellow). The stroke should look like a 1-pixel wide bright yellow line.

The test positions these draws at integer offsets. At an integer offset, the 1-pixel-wide strokes are positioned exactly between two pixel centers. It ends up being drawn across 2 pixels with a lowered opacity. Ideally the opacity adjustment makes the 2-pixel-wide line look similar to a full opacity 1-pixel-wide line. I think the brighter version looks much more like a full opacity 1-pixel-wide full-brightness-yellow line.

To demonstrate this more clearly, I ran the test modified with one adjustment: adding builder.Translate(0, 0.5) at the top of the test. This shifts all the draws vertically by half a pixel. This makes the horizontal strokes of the rectangle at 0.5 pixel offsets, which is aligned with pixel centers. The vertical strokes remain at integer offsets. The results:

image

I recommend clicking to view the image at full resolution. Also using a native (non-hi-dpi-scaled) resolution gives a more accurate view of how it really loooks.

The horizontal strokes are now aligned with pixel centers, they are drawn as single pixel full brightness yellow lines. Now compare the 2-pixel-wide vertical strokes with the 1-pixel-wide horizontal strokes. In my perception, the brightened vertical strokes after this PR look much more visually similar to the 1-pixel horizontal stroke.

StrokedArcsCoverFullArcWithButtEnds

The test description:

TEST_P(AiksTest, StrokedArcsCoverFullArcWithButtEnds) {
// This test compares the rendering of a full circle arc against a partial
// arc by drawing a one over the other in high contrast. If the partial
// arc misses any pixels that were drawn by the full arc, there will be
// some "pixel dirt" around the missing "erased" parts of the arcs. This
// case arises while rendering a CircularProgressIndicator with a background
// color where we want the rendering of the background full arc to hit the
// same pixels around the edges as the partial arc that covers it.
//
// In this case we draw a full blue circle and then draw a partial arc
// over it in the background color (white).

After this PR, the "pixel dirt" around the "erased" part of the arcs is noticeably lessened, especially for the last column with full arcs. I don't really know what this test is supposed to look like. But I assume less "pixel dirt" is better?

@gaaclarke

Copy link
Copy Markdown
Member

@b-luk looking at the videos, did you preserve the behavior that drawing a line of 0px width draws a hairline instead of nothing? That's a magical value that we are supposed to maintain. That's why the LineContents clamped at that width too since it's kind of weird to animate to nothing then jump to what 0px means.

@gaaclarke gaaclarke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We need to special case 0px to drawing a hairline too.

Comment thread engine/src/flutter/impeller/entity/shaders/uber_sdf.frag
@b-luk

b-luk commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@b-luk looking at the videos, did you preserve the behavior that drawing a line of 0px width draws a hairline instead of nothing? That's a magical value that we are supposed to maintain. That's why the LineContents clamped at that width too since it's kind of weird to animate to nothing then jump to what 0px means.

This PR does not have the behavior that drawing a 0px rectangle draws a hairline. drawLine with width 0 does draw a hairline, but drawRect with a width 0 draws nothing. This PR only deals with drawRect, so it doesn't have any special handling for width 0 and will draw nothing.

When I go back to working on #188514, I will need to add some special case handling for 0 for hairlines.

it's kind of weird to animate to nothing then jump to what 0px means

It is kind of weird. But I think that's the correct behavior. A width 0 line is a special case that behaves differently than non-zero widths. They are always physically 1 pixel, regardless of any transform. Non-zero widths have their specific widths, which is valid between 1 and 0 to animate to nothing. See https://dartpad.dev/?id=8f2c76261e86434860688eaadfd7b500 for an example in Skia/web. Also note that you can zoom in this dartpad, to see the behavior for non-zero lines vs the 0 hairline at different scaling transforms.

@b-luk
b-luk requested a review from gaaclarke July 1, 2026 21:57
@flar

flar commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Looking at the goldens, I only got about halfway through but the curves on the new code looked a little smoother than before.

The yellow outlines on the purple rects are supposed to be a reference, but it's odd that they have gaps in them. They are drawn with DrawRect and should not have gaps. The fact that they are twice as wide isn't ideal for their crispness, but I think the test is more about whether we get the joins right than about specific pixelization? They should probably use overdraw-rejection, though, which might help with the overdraw on the corners.

@b-luk

b-luk commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Looking at the goldens, I only got about halfway through but the curves on the new code looked a little smoother than before.

The yellow outlines on the purple rects are supposed to be a reference, but it's odd that they have gaps in them. They are drawn with DrawRect and should not have gaps. The fact that they are twice as wide isn't ideal for their crispness, but I think the test is more about whether we get the joins right than about specific pixelization? They should probably use overdraw-rejection, though, which might help with the overdraw on the corners.

The yellow outlines on the purple rects aren't the main purpose of those specific goldens. But it does demonstrate the gamma correction change from this PR.

Some of the new golden tests that I add do specifically test how these lines look. Unfortunately we don't see any results for the existing UberSDF behavior, because the tests are added in this PR which also changes them to the new behavior. But you can compare with the non-SDF versions. For example:

OnePixelDrawRect

New UberSDF:
https://flutter-gold.skia.org/img/images/1c3fd940988ddd247d37bdada8bbcbef.png

Non-SDF:
https://flutter-gold.skia.org/img/images/8c178e039318d52d5ad6f6424ee122c8.png

The new UberSDF behavior looks much more like a consistent 1 pixel white line across different subpixel offsets.

ThinDrawRectRotated

New UberSDF:
https://flutter-gold.skia.org/img/images/b03434b96987b46a8b6f8dd69c5378c5.png

Non-SDF:
https://flutter-gold.skia.org/img/images/10cc20535c0589b583de9bd8693a0d3b.png

This draws a rectangle with width 0.3 and with some rotation. The new UberSDF behavior is clearly much more consistent looking.

edit: inlined images get compressed or something when inlined through github, so I replaced the inlined images with links directly to the images on flutter-gold

@gaaclarke gaaclarke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code looks good, the before and after video looks good, the malioc diff looks fine. I didn't dig through the goldens. Assuming you and jim are happy with them I'm fine with this change. My biggest concern was the additional performance cost versus the problem we are addressing. These fixes are kind of nice to have in a world where we don't use the ubersdf for line drawing, but that is where we are wanting to go which raises their value.

@flutter-dashboard

Copy link
Copy Markdown

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #188821 at sha a05a1c8

@flutter-dashboard flutter-dashboard Bot added the will affect goldens Changes to golden files label Jul 2, 2026
@b-luk

b-luk commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@flar PTAL. As I discussed with you, I moved the logic for 1-pixel-minimum rectangle sizing and alpha scaling from the shader to the CPU. This did indeed simplify a lot of the shader code.

if (renderer_.GetContext()->GetFlags().use_sdfs &&
IsCompatibleWithSDFRendering(paint)) {
if (rect.IsEmpty()) {
// Empty rect doesn't need to be drawn.

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.

Is this true for stroked rects?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tested it out with Skia, and it does draw a rect even with a size of 0. So this was wrong. I removed it. I think it could still apply to filled rectangles, but I'll just keep the code simpler and not try to make optimizations that aren't directly related to what I'm fixing.

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.

Filled rectangles could just be eliminated if the alpha scale is 0 (or <epsilon) without specifically testing for 0 scale or 0 dimension.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, we can exit early if alpha scale is close to 0. I added this.

Comment thread engine/src/flutter/impeller/display_list/canvas.cc
Comment thread engine/src/flutter/impeller/entity/shaders/uber_sdf.frag
@b-luk
b-luk requested a review from flar July 6, 2026 21:54

@flar flar 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.

Mostly questions to answer, but I also want to take another look at the goldens. I'm thinking there are too many new goldens, but I want to understand each one and how it might be just a variation on another.

Also, I'm assuming that most of the older golden diffs are due to the new gamma function?

Comment thread engine/src/flutter/impeller/display_list/canvas.cc
Comment thread engine/src/flutter/impeller/entity/shaders/uber_sdf.frag

// Converts linear coverage alpha to perceptual alpha.
float gammaCorrectedAlpha(float alpha, vec3 foreground_rgb) {
// Gamma corrected alpha used for dark colors.

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.

Is there a source for how sqrt compares to 2.2 gamma?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's what Gemini said was standard industry practice to avoid pow, which requires the SFU pipeline and can cause shaders to be slow. From a bit of searching, I can't find any definitive resource about this technique of using sqrt to approximate pow(1/2.2). But there are some stackoverflow/reddit/forum posts, like this.

As for how it actually compares to 2.2 gamma, sqrt would be pow(1/2) compared to pow(1/2.2). Here's a graph for how they compare. pow(1/2.2) is in blue, and sqrt is in green. The difference between the two is shown in dotted green. I also show the non-corrected linear curve and its difference with pow(1/2.2) in purple and dotted purple. The largest difference between the sqrt and 2.2 gamma curve is about 0.035. So at these alpha values, the alpha will be off by 3.5%. We only use this for AA fringes that go across 1-2 pixels, so I think it's good enough to get this close to gamma 2.2. Compare with the purple lines that shows the current non-corrected behavior, where the alpha can be off by about 0.28 or 28%.

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.

That sounds good. I try to include a pointer to a (non-AI) reference that investigates or demonstrates the point when I can.

@flutter-dashboard

Copy link
Copy Markdown

Golden file changes are available for triage from new commit, Click here to view.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #188821 at sha da9caf8

flar
flar previously approved these changes Jul 8, 2026

@flar flar 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.

LGTM

// Most tests scale the builder with
// builder.Scale(GetContentScale().x, GetContentScale().y) in order to
// maintain the same visual size on different screens.
// These DrawLines tests explicitly doesn't invoke this scaling, because

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.

grammar "tests ... don't"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. PTAL. Annoying that every small change like this requires re-approval.

@b-luk
b-luk requested a review from flar July 8, 2026 16:11
@flutter-dashboard

Copy link
Copy Markdown

Golden file changes are available for triage from new commit, Click here to view.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #188821 at sha 5060897

@flar flar 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.

LGTM

@b-luk b-luk added the autosubmit Merge PR when tree becomes green via auto submit App label Jul 8, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jul 9, 2026
Merged via the queue into flutter:master with commit 899764b Jul 9, 2026
202 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jul 9, 2026
@b-luk
b-luk deleted the ubersdfthinrect branch July 9, 2026 16:41
@b-luk b-luk mentioned this pull request Jul 9, 2026
10 tasks
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jul 10, 2026
…12169)

Manual roll Flutter from 91939cc4db78 to dc2a8703e12b (50 revisions)

Manual roll requested by [email protected]

flutter/flutter@91939cc...dc2a870

2026-07-09 [email protected] [ios,macos] Update swiftc.py flags to match swiftc (flutter/flutter#189174)
2026-07-09 [email protected] [AGP 9] Update Warn Version to AGP 9+ (flutter/flutter#189109)
2026-07-09 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189203)
2026-07-09 [email protected] [web] Roll Chrome to 145 (framework) (flutter/flutter#182861)
2026-07-09 [email protected] Roll Packages from 52d84d6 to 20928d5 (6 revisions) (flutter/flutter#189194)
2026-07-09 [email protected] [web] Avoid absolute positioning for base CanvasKit canvas (flutter/flutter#188337)
2026-07-09 [email protected] Roll Dart SDK from cdb7217e65aa to a11fb7ed40a5 (6 revisions) (flutter/flutter#189195)
2026-07-09 [email protected] Fix dereference of nullptr in the moved-to-rect signal in the Linux embedder (flutter/flutter#189152)
2026-07-09 [email protected] Fix data for design packages (flutter/flutter#189140)
2026-07-09 [email protected] Roll Skia from 7b42d1251d54 to ab3a7b98c94d (2 revisions) (flutter/flutter#189181)
2026-07-09 [email protected] Roll Skia from 05d9d214e0b7 to 7b42d1251d54 (2 revisions) (flutter/flutter#189175)
2026-07-09 [email protected] Roll Skia from 542c8bdd7f4f to 05d9d214e0b7 (4 revisions) (flutter/flutter#189169)
2026-07-09 [email protected] UberSDF rect handling for thin (line-like) rectangles (flutter/flutter#188821)
2026-07-09 [email protected] Roll Skia from dd572c07f63c to 542c8bdd7f4f (4 revisions) (flutter/flutter#189160)
2026-07-09 [email protected] [flutter_tools] Fix hot restart for WASM web builds (flutter/flutter#187898)
2026-07-08 [email protected] Split FlViewRenderer into OpenGL and software backends (flutter/flutter#188824)
2026-07-08 [email protected] Promote android_hardware_smoke_tests out of bringup in CI (flutter/flutter#189081)
2026-07-08 [email protected] Roll Skia from 8df24be66531 to dd572c07f63c (4 revisions) (flutter/flutter#189150)
2026-07-08 [email protected] Expose LinuxWindowRegistrar on _window_linux.dart in order to better support out of tree LinuxWindowingOwners (flutter/flutter#188917)
2026-07-08 [email protected] Roll pub packages (flutter/flutter#189149)
2026-07-08 [email protected] fix(ci): harden some workflows (flutter/flutter#189087)
2026-07-08 [email protected] Roll Skia from 51a62da33da0 to 8df24be66531 (1 revision) (flutter/flutter#189139)
2026-07-08 [email protected] Roll Dart SDK to Dart 3.13 beta3 (flutter/flutter#189122)
2026-07-08 [email protected] [flutter_tools] Don't crash on non-UTF-8 plugin pubspec.yaml (flutter/flutter#188976)
2026-07-08 [email protected] [flutter_tools] Watch transitive #include headers for FragmentProgram hot reload (flutter/flutter#187945)
2026-07-08 [email protected] Roll Skia from 040d9f55de00 to 51a62da33da0 (1 revision) (flutter/flutter#189135)
2026-07-08 [email protected] Roll Packages from 92525f5 to 52d84d6 (7 revisions) (flutter/flutter#189134)
2026-07-08 [email protected] [flutter_tools] Forcefully kill hung subprocesses 5 seconds after timeout (flutter/flutter#187178)
2026-07-08 [email protected] Expose the app's build name and number as compile-time constants (flutter/flutter#187935)
2026-07-08 [email protected] Roll Skia from 1ff92f879815 to 040d9f55de00 (1 revision) (flutter/flutter#189131)
2026-07-08 [email protected] Roll Skia from 6137414bef5c to 1ff92f879815 (6 revisions) (flutter/flutter#189126)
2026-07-08 [email protected] [test cross imports] More test/rendering + flutter_test/test fixes (flutter/flutter#188954)
2026-07-08 [email protected] engine: explain why each candidate build was skipped in Flutter web loader (flutter/flutter#186254)
2026-07-08 [email protected] vscode: add missing unicode.h (flutter/flutter#189102)
2026-07-08 [email protected] Roll Fuchsia Linux SDK from 7RjQJBW3m-3Jl-7jr... to QcRFUtvCw2EobfJ8s... (flutter/flutter#189104)
2026-07-08 [email protected] Roll Skia from 075fbe4778d9 to 6137414bef5c (10 revisions) (flutter/flutter#189106)
2026-07-08 [email protected] engine: warn on WASM load failure when not crossOriginIsolated (flutter/flutter#186252)
2026-07-08 [email protected] Roll Dart SDK from c9bccc09e733 to db2155f56bf3 (2 revisions) (flutter/flutter#189105)
2026-07-08 [email protected] [flutter_tools] Prevent interactive device selection in machine mode (flutter/flutter#188267)
2026-07-08 [email protected] [flutter_tools] Fix wireless ADB device discovery when serial contains spaces (flutter/flutter#187943)
2026-07-07 [email protected] [web] Fix grouped autofill on iOS Chrome (flutter/flutter#187459)
2026-07-07 [email protected] Fix TextSelectionOverlay crash when layout is degenerate (flutter/flutter#188672)
2026-07-07 [email protected] [flutter_tools] Provision Android NDK in the main Gradle invocation (flutter/flutter#186337)
2026-07-07 [email protected] Android_hardware_smoke_test: Migrate to AGP 9 (flutter/flutter#189082)
...
jesswrd pushed a commit to jesswrd/flutter that referenced this pull request Jul 10, 2026
This fixes thin UberSDF roundrect drawing, in the same way that
flutter#188821 fixed it for normal
rects.

Prerequisite for using UberSDF for lines.

- Factors out some of the rect-upscaling code in DrawRect to an
UpscaleRect helper function, so it can be shared by DrawRect and
DrawRoundRect.
- Adds a roundRectPixelSize function in uber_sdf.frag, similar to
rectPixelSize but for round rects.
- Adds unit tests for UpscaleRect, and a new golden tests for
"DrawLinesWithFilledRoundRects"

## Screenshot

### DrawLinesWithFilledRoundRects

Before:
<img width="1024" height="800" alt="roundrect ubersdf before"
src="https://github.com/user-attachments/assets/c490318f-0579-4425-82ec-ca5a7fd51d89"
/>

After:
<img width="1024" height="800" alt="roundrect ubersdf after"
src="https://github.com/user-attachments/assets/cd7bc780-fbc8-46aa-a034-6a9c1db69ad9"
/>

## Video

### DrawLinesWithFilledRoundRects

Before:


https://github.com/user-attachments/assets/612af65d-bce5-45d2-a1aa-7636a60f3254

After:


https://github.com/user-attachments/assets/4090d72c-eba9-4e28-8c8a-495bcce9b82b


## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

If this change needs to override an active code freeze, provide a
comment explaining why. The code freeze workflow can be overridden by
code reviewers. See pinned issues for any active code freezes with
guidance.

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@b-luk b-luk added the cp: beta cherry pick this pull request to beta release candidate branch label Jul 15, 2026
auto-submit Bot pushed a commit that referenced this pull request Jul 15, 2026
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

This is the first in a set of 3 PRs (#188821, #189224, and #188514) that will be cherry picked in order to fix a set of line-related issues when Impeller with SDF rendering is enabled.

### Issue Link:

What is the link to the issue this cherry-pick is addressing?

#188329
#188911
#188593

### Impact Description:

What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)?
Does it impact development (ex. flutter doctor crashes when Android Studio is installed),
or the shipping of production apps (the app crashes on launch).
This information is for domain experts and release engineers to understand the consequences of saying yes or no to the cherry pick.

Thin ines (including lines rendered with `canvas.drawLine`, thin line-like rectangles rendered with `canvas.drawRect`, and paths drawn with `canvas.drawPath` that consist of a single line or thin rectangle) have various highly visible rendering issues:
- opacity is ignored
- lines with stroke width less than 1 pixel are incorrectly clamped up to be ~2 pixels wide
- lines with stroke width more than 1 pixel are overly thick, with an added thickness proportional to their intended width

This issue is present when impeller is enabled Impeller with SDF rendering. In the beta channel, Impeller with SDF rendering is the default when building apps for desktop platforms: macOS, Linux, and Windows.

### Changelog Description:
Explain this cherry pick:
* In one line that is accessible to most Flutter developers.
* That describes the state prior to the fix.
* That includes which platforms are impacted.
See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples.

[flutter/188329](#188329), [flutter/188911](#188911), [flutter/188593](#188593): Multiple line rendering issues when Impeller with SDF rendering is enabled (default enabled on desktop platforms)

### Workaround:
Is there a workaround for this issue?

Disable Impeller, falling back to Skia rendering.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
What are the steps to validate that this fix works?

After all 3 PRs are cherry picked, verify the sample apps in the relevant issues (#188329 and #188911) render with expected opacity and with expected line widths.
auto-submit Bot pushed a commit that referenced this pull request Jul 15, 2026
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

This is the second in a set of 3 PRs (#188821, #189224, and #188514) that will be cherry picked in order to fix a set of line-related issues when Impeller with SDF rendering is enabled.

The first PR was cherry picked and merged with #189517

### Issue Link:

What is the link to the issue this cherry-pick is addressing?

#188329
#188911
#188593

### Impact Description:

What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)?
Does it impact development (ex. flutter doctor crashes when Android Studio is installed),
or the shipping of production apps (the app crashes on launch).
This information is for domain experts and release engineers to understand the consequences of saying yes or no to the cherry pick.

Thin ines (including lines rendered with `canvas.drawLine`, thin line-like rectangles rendered with `canvas.drawRect`, and paths drawn with `canvas.drawPath` that consist of a single line or thin rectangle) have various highly visible rendering issues:
- opacity is ignored
- lines with stroke width less than 1 pixel are incorrectly clamped up to be ~2 pixels wide
- lines with stroke width more than 1 pixel are overly thick, with an added thickness proportional to their intended width

This issue is present when impeller is enabled Impeller with SDF rendering. In the beta channel, Impeller with SDF rendering is the default when building apps for desktop platforms: macOS, Linux, and Windows.

### Changelog Description:
Explain this cherry pick:
* In one line that is accessible to most Flutter developers.
* That describes the state prior to the fix.
* That includes which platforms are impacted.
See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples.

[flutter/188329](#188329), [flutter/188911](#188911), [flutter/188593](#188593): Multiple line rendering issues when Impeller with SDF rendering is enabled (default enabled on desktop platforms)

### Workaround:
Is there a workaround for this issue?

Disable Impeller, falling back to Skia rendering.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
What are the steps to validate that this fix works?

After all 3 PRs are cherry picked, verify the sample apps in the relevant issues (#188329 and #188911) render with expected opacity and with expected line widths.
auto-submit Bot pushed a commit that referenced this pull request Jul 16, 2026
…es (#189537)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

This is the third in a set of 3 PRs (#188821, #189224, and #188514) that will be cherry picked in order to fix a set of line-related issues when Impeller with SDF rendering is enabled.

The first PR was cherry picked and merged with #189517
The second PR was cherry picked and merged with #189529

### Issue Link:

What is the link to the issue this cherry-pick is addressing?

#188329
#188911
#188593

### Impact Description:

What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)?
Does it impact development (ex. flutter doctor crashes when Android Studio is installed),
or the shipping of production apps (the app crashes on launch).
This information is for domain experts and release engineers to understand the consequences of saying yes or no to the cherry pick.

Thin ines (including lines rendered with `canvas.drawLine`, thin line-like rectangles rendered with `canvas.drawRect`, and paths drawn with `canvas.drawPath` that consist of a single line or thin rectangle) have various highly visible rendering issues:
- opacity is ignored
- lines with stroke width less than 1 pixel are incorrectly clamped up to be ~2 pixels wide
- lines with stroke width more than 1 pixel are overly thick, with an added thickness proportional to their intended width

This issue is present when impeller is enabled Impeller with SDF rendering. In the beta channel, Impeller with SDF rendering is the default when building apps for desktop platforms: macOS, Linux, and Windows.

### Changelog Description:
Explain this cherry pick:
* In one line that is accessible to most Flutter developers.
* That describes the state prior to the fix.
* That includes which platforms are impacted.
See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples.

[flutter/188329](#188329), [flutter/188911](#188911), [flutter/188593](#188593): Multiple line rendering issues when Impeller with SDF rendering is enabled (default enabled on desktop platforms)

### Workaround:
Is there a workaround for this issue?

Disable Impeller, falling back to Skia rendering.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
What are the steps to validate that this fix works?

After all 3 PRs are cherry picked, verify the sample apps in the relevant issues (#188329 and #188911) render with expected opacity and with expected line widths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD cp: beta cherry pick this pull request to beta release candidate branch e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels. will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Thin UberSDF rects are sometimes invisible

3 participants