Visitar URL original
correctly handle opacity and blending when in a saveLayer (#187) · flutter/packages@7e7e127 · GitHub
Skip to content

Commit 7e7e127

Browse files
authored
correctly handle opacity and blending when in a saveLayer (#187)
1 parent f43051b commit 7e7e127

4 files changed

Lines changed: 102 additions & 5 deletions

File tree

‎packages/vector_graphics_compiler/lib/src/svg/parser.dart‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,39 @@ class SvgAttributes {
19691969
/// The y translation.
19701970
final double? y;
19711971

1972+
/// A copy of these attributes after absorbing a saveLayer.
1973+
///
1974+
/// Specifically, this will null out `blendMode` and any opacity related
1975+
/// attributes, since those have been applied in a saveLayer call.
1976+
///
1977+
/// The [raw] map preserves old values.
1978+
SvgAttributes forSaveLayer() {
1979+
return SvgAttributes._(
1980+
raw: raw,
1981+
id: id,
1982+
href: href,
1983+
transform: transform,
1984+
color: color,
1985+
stroke: stroke?.forSaveLayer(),
1986+
fill: fill?.forSaveLayer(),
1987+
fillRule: fillRule,
1988+
clipRule: clipRule,
1989+
clipPathId: clipPathId,
1990+
blendMode: blendMode,
1991+
fontFamily: fontFamily,
1992+
fontWeight: fontWeight,
1993+
fontSize: fontSize,
1994+
textDecoration: textDecoration,
1995+
textDecorationStyle: textDecorationStyle,
1996+
textDecorationColor: textDecorationColor,
1997+
x: x,
1998+
textAnchorMultiplier: textAnchorMultiplier,
1999+
y: y,
2000+
width: width,
2001+
height: height,
2002+
);
2003+
}
2004+
19722005
/// Creates a new set of attributes as if this inherited from `parent`.
19732006
///
19742007
/// If `includePosition` is true, the `x`/`y` coordinates are also inherited. This
@@ -2066,6 +2099,25 @@ class SvgStrokeAttributes {
20662099
/// The opacity to apply to a default color, if [color] is null.
20672100
final double? opacity;
20682101

2102+
/// A copy of these attributes after absorbing a saveLayer.
2103+
///
2104+
/// Specifically, this will null out any opacity related
2105+
/// attributes, since those have been applied in a saveLayer call.
2106+
SvgStrokeAttributes forSaveLayer() {
2107+
return SvgStrokeAttributes._(
2108+
_definitions,
2109+
color: color,
2110+
shaderId: shaderId,
2111+
join: join,
2112+
cap: cap,
2113+
miterLimit: miterLimit,
2114+
width: width,
2115+
dashArray: dashArray,
2116+
dashOffset: dashOffset,
2117+
hasPattern: hasPattern,
2118+
);
2119+
}
2120+
20692121
/// Inherits attributes in this from parent.
20702122
SvgStrokeAttributes applyParent(SvgStrokeAttributes? parent) {
20712123
return SvgStrokeAttributes._(
@@ -2156,6 +2208,19 @@ class SvgFillAttributes {
21562208
/// If there is a pattern a default fill will be returned.
21572209
final bool? hasPattern;
21582210

2211+
/// A copy of these attributes after absorbing a saveLayer.
2212+
///
2213+
/// Specifically, this will null out any opacity related
2214+
/// attributes, since those have been applied in a saveLayer call.
2215+
SvgFillAttributes forSaveLayer() {
2216+
return SvgFillAttributes._(
2217+
_definitions,
2218+
color: color,
2219+
shaderId: shaderId,
2220+
hasPattern: hasPattern,
2221+
);
2222+
}
2223+
21592224
/// Inherits attributes in this from parent.
21602225
SvgFillAttributes applyParent(SvgFillAttributes? parent) {
21612226
return SvgFillAttributes._(

‎packages/vector_graphics_compiler/lib/src/svg/resolver.dart‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class ResolvingVisitor extends Visitor<Node, AffineMatrix> {
7676
children: <Node>[
7777
for (Node child in parentNode.children)
7878
child
79-
.applyAttributes(parentNode.attributes)
79+
.applyAttributes(parentNode.attributes.forSaveLayer())
8080
.accept(this, nextTransform),
8181
],
8282
);

‎packages/vector_graphics_compiler/test/parser_test.dart‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,32 @@ import 'package:vector_graphics_compiler/vector_graphics_compiler.dart';
44
import 'test_svg_strings.dart';
55

66
void main() {
7+
test('Opacity with a save layer does not continue to inherit', () {
8+
final VectorInstructions instructions = parseWithoutOptimizers('''
9+
<svg width="283" height="180" viewBox="0 0 283 180" fill="none" xmlns="http://www.w3.org/2000/svg">
10+
<path opacity=".3" d="M61.72 181.636L187.521 0H0v181.636h61.72z" fill="#202124" fill-opacity=".08"/>
11+
<g opacity=".04">
12+
<path d="M0 0l283.728 90.818V0H0z" fill="black"/>
13+
</g>
14+
</svg>
15+
''');
16+
17+
expect(instructions.paints, const <Paint>[
18+
Paint(blendMode: BlendMode.srcOver, fill: Fill(color: Color(0x06202124))),
19+
// The paint for the saveLayer.
20+
Paint(blendMode: BlendMode.srcOver, fill: Fill(color: Color(0x0a000000))),
21+
// The paint for the path drawn in the saveLayer - must not be the same as
22+
// the saveLayer otherwise the path will be drawn almost completely transparent.
23+
Paint(blendMode: BlendMode.srcOver, fill: Fill(color: Color(0xff000000))),
24+
]);
25+
expect(instructions.commands, const <DrawCommand>[
26+
DrawCommand(DrawCommandType.path, objectId: 0, paintId: 0),
27+
DrawCommand(DrawCommandType.saveLayer, paintId: 1),
28+
DrawCommand(DrawCommandType.path, objectId: 1, paintId: 2),
29+
DrawCommand(DrawCommandType.restore),
30+
]);
31+
});
32+
733
test('Opacity on a default fill', () {
834
final VectorInstructions instructions = parseWithoutOptimizers('''
935
<svg viewBox="0 0 10 10">

‎packages/vector_graphics_compiler/test/resolver_test.dart‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,29 @@ void main() {
4040
final Node node = parseToNodeTree('''
4141
<svg viewBox="0 0 200 200">
4242
<g opacity=".5" fill="red">
43-
<rect x="0" y="0" width="10" height="10" />
44-
<rect x="5" y="5" width="10" height="10" />
43+
<rect x="0" y="0" width="100" height="100" />
44+
<rect x="50" y="50" width="100" height="100" />
4545
</g>
4646
</svg>''');
4747
final Node resolvedNode =
4848
node.accept(ResolvingVisitor(), AffineMatrix.identity);
4949
final List<ResolvedPathNode> nodes =
5050
queryChildren<ResolvedPathNode>(resolvedNode);
51+
final SaveLayerNode saveLayerNode =
52+
queryChildren<SaveLayerNode>(resolvedNode).single;
53+
54+
expect(saveLayerNode.paint.fill!.color, const Color(0x7FFF0000));
5155

5256
expect(nodes.length, 2);
57+
58+
// Opacity is not inherited since it is applied in a saveLayer.
5359
expect(
5460
nodes.first.paint,
55-
const Paint(fill: Fill(color: Color(0x7FFF0000))),
61+
const Paint(fill: Fill(color: Color(0xFFFF0000))),
5662
);
5763
expect(
5864
nodes.last.paint,
59-
const Paint(fill: Fill(color: Color(0x7FFF0000))),
65+
const Paint(fill: Fill(color: Color(0xFFFF0000))),
6066
);
6167
});
6268

0 commit comments

Comments
 (0)