Repository navigation
[vector_graphics_compiler] Fix tspan transform applied twice - #13129
Conversation
A tspan without its own x/y inside a translated group kept the group transform, even though its parent text had already applied it to its position. The transform is no longer applied again in that case. Fixes flutter/flutter#189925
There was a problem hiding this comment.
Code Review
This pull request resolves an issue where a tspan without its own position is drawn with its parent group transform applied twice. It updates ResolvingVisitor to track the active text position and passes it as a parent parameter to computeTextPosition to determine if the transform was already applied. Feedback on the changes highlights a potential bug where a tspan's local transform could be discarded if it has no position, and suggests ensuring continuesTransformedParent is only true when attributes.transform is null.
| final bool continuesTransformedParent = | ||
| parent != null && | ||
| parent.transform == null && | ||
| x == null && | ||
| y == null && | ||
| dx == null && | ||
| dy == null; |
There was a problem hiding this comment.
If the tspan has its own local transform (i.e., attributes.transform != null) but no position of its own, continuesTransformedParent will evaluate to true. This causes consumeTransform to be true (if the computed transform is encodable in a rect), which sets the TextPosition's transform to null, completely discarding the tspan's local transform. To prevent discarding the local transform of the tspan, we should ensure continuesTransformedParent is only true when the tspan does not have its own local transform.
| final bool continuesTransformedParent = | |
| parent != null && | |
| parent.transform == null && | |
| x == null && | |
| y == null && | |
| dx == null && | |
| dy == null; | |
| final bool continuesTransformedParent = | |
| parent != null && | |
| parent.transform == null && | |
| attributes.transform == null && | |
| x == null && | |
| y == null && | |
| dx == null && | |
| dy == null; |
There was a problem hiding this comment.
Thanks for the suggestion! I tried it, but attributes.transform is never null (it defaults to identity), so this check would always be false and the fix would stop working. A transform on a tspan isn't valid SVG anyway, so I'll keep it as is.
| // A tspan without its own position continues from the parent's position. | ||
| // If the parent already applied the transform to its position, the | ||
| // transform must not be applied again. | ||
| final bool continuesTransformedParent = |
There was a problem hiding this comment.
nit: I think the name is a bit confusing, because TransformedParent reads as "a parent that has a transform", but the condition is parent.transform == null. It makes sense once you realize that parent.transform == null because the parent consumed its transform, but it's not intuitive from the naming. What do you think about naming it something like inheritsConsumedTransform? And maybe updating the comment to explain that the part about "If the parent already applied the transform to its position" means "parent.transform == null".
There was a problem hiding this comment.
Good point, thanks! Renamed it to inheritsConsumedTransform and updated the comment to say the parent's transform is null in that case.
|
This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled. |
b-luk
left a comment
There was a problem hiding this comment.
Please merge from upstream to resolve the CHANGELOG conflict.
| final bool consumeTransform = | ||
| computedTransform == AffineMatrix.identity || | ||
| (computedTransform.encodableInRect && (hasXY || hasDxDy)); | ||
| (computedTransform.encodableInRect && (hasXY || hasDxDy || inheritsConsumedTransform)); |
There was a problem hiding this comment.
Maybe this is me being pedantic, but I don't think inheritsConsumedTransform should be a condition that makes consumeTransform true. If we're inheriting from the parent, we don't want to consume the current transform (i.e. we don't want to apply our current transform to our x/y/dx/dy.) It just so happens that if we're inheriting from the parent, consuming the current transform is a no-op because we have no x/y/dx/dy, so this doesn't do anything incorrect. But this is not immediately clear from the code, so I think it makes the code harder to follow and it makes the naming misleading.
If the parent already consumed the transform, it just means we don't want to pass the transform to the returned TextPosition. So instead of making inheritsConsumedTransform a condition for consumeTransform, I think it's clearer if we just use it below as transform: (inheritsConsumedTransform || consumeTransform) ? null : computedTransform,. What do you think?
There was a problem hiding this comment.
Makes sense, that's clearer. Changed it to transform: (inheritsConsumedTransform || consumeTransform) ? null : computedTransform and left consumeTransform as it was.
# Conflicts: # packages/vector_graphics_compiler/CHANGELOG.md
|
Merged upstream. Since 1.3.1 is now the alpha mask change, I moved this entry to 1.3.2. |
…r#194564) flutter/packages@da40eaf...93eede7 2026-10-16 41930132+hellohuanlin@users.noreply.github.com [camera]fix a crash when switching camera while recording due to disconnected capturePhotoOputput (flutter/packages#13248) 2026-10-16 okorohelijah@google.com [various] Update iOS example apps to the 15.0 deployment target (flutter/packages#13223) 2026-10-16 36861262+QuncCccccc@users.noreply.github.com [material_ui] Add M3E `IconButton` example (flutter/packages#13085) 2026-10-15 magder@google.com [ci] Increase deprecation check minimum to iOS 16 (flutter/packages#13211) 2026-10-15 victor.orozco@cloudsufi.com [google_sign_in] Remove the public iOS handleOpen helper (flutter/packages#13273) 2026-10-15 okorohelijah@google.com [google_fonts] Make the pendingFonts test deterministic (flutter/packages#13262) 2026-10-15 48776784+mackings@users.noreply.github.com [vector_graphics_compiler] Fix tspan transform applied twice (flutter/packages#13129) 2026-10-15 engine-flutter-autoroll@skia.org Roll Flutter from 426be96 to 5c7e45a (21 revisions) (flutter/packages#13270) 2026-10-15 37883572+edwin-hollen@users.noreply.github.com [flutter_svg] Add golden widget tests for SVG alpha masks (flutter/packages#13245) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC flutter-ecosystem@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
A
tspanwithout its ownx/yinside a translatedgwas drawn with the translation applied twice.The parent
texthasx/y, so the compiler applies the transform to its position. Thetspanhas no position, so it kept the transform as well. The renderer then applied that transform on top of a position that already had it.Now a
tspanwithout its own position follows its parent: if the parent already applied the transform, thetspandoes not apply it again. The bug is invector_graphics_compiler, whichflutter_svguses to parse SVGs.Fixes flutter/flutter#189925
Pre-Review Checklist
[shared_preferences]///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot 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.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2