Skip to content

Commit e7ee47d

Browse files
authored
[web] Implement matrix parameter for linear gradient (flutter#18208)
* Implement linear gradient matrix * update golden locks * Remove matrix check in RadialGradient
1 parent 28d9985 commit e7ee47d

File tree

5 files changed

+72
-19
lines changed

5 files changed

+72
-19
lines changed

lib/web_ui/dev/goldens_lock.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
repository: https://github.com/flutter/goldens.git
2-
revision: 0c1a793bfd49e30fccdd7ad64329a114a9cb32f7
2+
revision: b38daf293027908861f16362b378ca6865518ded

lib/web_ui/lib/src/engine/shader.dart

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,47 @@ void _validateColorStops(List<ui.Color> colors, List<double> colorStops) {
8080

8181
class GradientLinear extends EngineGradient {
8282
GradientLinear(
83-
this.from,
84-
this.to,
85-
this.colors,
86-
this.colorStops,
87-
this.tileMode,
83+
this.from,
84+
this.to,
85+
this.colors,
86+
this.colorStops,
87+
this.tileMode,
88+
Float64List matrix,
8889
) : assert(_offsetIsValid(from)),
8990
assert(_offsetIsValid(to)),
9091
assert(colors != null),
9192
assert(tileMode != null),
93+
this.matrix4 = matrix == null ? null : _FastMatrix64(matrix),
9294
super._() {
93-
_validateColorStops(colors, colorStops);
95+
if (assertionsEnabled) {
96+
_validateColorStops(colors, colorStops);
97+
}
9498
}
9599

96100
final ui.Offset from;
97101
final ui.Offset to;
98102
final List<ui.Color> colors;
99103
final List<double> colorStops;
100104
final ui.TileMode tileMode;
105+
final _FastMatrix64 matrix4;
101106

102107
@override
103108
html.CanvasGradient createPaintStyle(html.CanvasRenderingContext2D ctx) {
104-
final html.CanvasGradient gradient =
105-
ctx.createLinearGradient(from.dx, from.dy, to.dx, to.dy);
109+
final bool hasMatrix = matrix4 != null;
110+
html.CanvasGradient gradient;
111+
if (hasMatrix) {
112+
final centerX = (from.dx + to.dx) / 2.0;
113+
final centerY = (from.dy + to.dy) / 2.0;
114+
matrix4.transform(from.dx - centerX, from.dy - centerY);
115+
final double fromX = matrix4.transformedX + centerX;
116+
final double fromY = matrix4.transformedY + centerY;
117+
matrix4.transform(to.dx - centerX, to.dy - centerY);
118+
gradient = ctx.createLinearGradient(fromX, fromY,
119+
matrix4.transformedX + centerX, matrix4.transformedY + centerY);
120+
} else {
121+
gradient = ctx.createLinearGradient(from.dx, from.dy, to.dx, to.dy);
122+
}
123+
106124
if (colorStops == null) {
107125
assert(colors.length == 2);
108126
gradient.addColorStop(0, colorToCssString(colors[0]));
@@ -154,7 +172,9 @@ class GradientLinear extends EngineGradient {
154172
}
155173

156174
// TODO(flutter_web): For transforms and tile modes implement as webgl
157-
// shader instead. See https://github.com/flutter/flutter/issues/32819
175+
// For now only GradientRotation is supported in flutter which is implemented
176+
// for linear gradient.
177+
// See https://github.com/flutter/flutter/issues/32819
158178
class GradientRadial extends EngineGradient {
159179
GradientRadial(this.center, this.radius, this.colors, this.colorStops,
160180
this.tileMode, this.matrix4)
@@ -170,11 +190,6 @@ class GradientRadial extends EngineGradient {
170190
@override
171191
Object createPaintStyle(html.CanvasRenderingContext2D ctx) {
172192
if (!experimentalUseSkia) {
173-
// The DOM backend does not (yet) support all parameters.
174-
if (matrix4 != null && !Matrix4.fromFloat32List(matrix4).isIdentity()) {
175-
throw UnimplementedError(
176-
'matrix4 not supported in GradientRadial shader');
177-
}
178193
if (tileMode != ui.TileMode.clamp) {
179194
throw UnimplementedError(
180195
'TileMode not supported in GradientRadial shader');

lib/web_ui/lib/src/engine/util.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,15 @@ FutureOr<void> sendFontChangeMessage() async {
481481
});
482482
}
483483
}
484+
485+
// Stores matrix in a form that allows zero allocation transforms.
486+
class _FastMatrix64 {
487+
final Float64List matrix;
488+
double transformedX = 0, transformedY = 0;
489+
_FastMatrix64(this.matrix);
490+
491+
void transform(double x, double y) {
492+
transformedX = matrix[12] + (matrix[0] * x) + (matrix[4] * y);
493+
transformedY = matrix[13] + (matrix[1] * x) + (matrix[5] * y);
494+
}
495+
}

lib/web_ui/lib/src/ui/painting.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,10 +1092,9 @@ abstract class Gradient extends Shader {
10921092
List<Color> colors, [
10931093
List<double> colorStops,
10941094
TileMode tileMode = TileMode.clamp,
1095-
// TODO(flutter_web): see https://github.com/flutter/flutter/issues/32819
10961095
Float64List matrix4,
10971096
]) =>
1098-
engine.GradientLinear(from, to, colors, colorStops, tileMode);
1097+
engine.GradientLinear(from, to, colors, colorStops, tileMode, matrix4);
10991098

11001099
/// Creates a radial gradient centered at `center` that ends at `radius`
11011100
/// distance from the center.

lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
// @dart = 2.6
66
import 'dart:html' as html;
7+
import 'dart:math' as math;
78

89
import 'package:ui/ui.dart' hide TextStyle;
910
import 'package:ui/src/engine.dart';
@@ -46,7 +47,7 @@ void main() async {
4647

4748
test('Should draw linear gradient using rectangle.', () async {
4849
final RecordingCanvas rc =
49-
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
50+
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
5051
Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300);
5152
final Paint paint = Paint()..shader = Gradient.linear(
5253
Offset(shaderRect.left, shaderRect.top),
@@ -57,10 +58,36 @@ void main() async {
5758
await _checkScreenshot(rc, 'linear_gradient_rect');
5859
});
5960

61+
test('Should draw linear gradient with transform.', () async {
62+
final RecordingCanvas rc =
63+
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
64+
List<double> angles = [0.0, 90.0, 180.0];
65+
double yOffset = 0;
66+
for (double angle in angles) {
67+
final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100);
68+
final Paint paint = Paint()
69+
..shader = Gradient.linear(
70+
Offset(shaderRect.left, shaderRect.top),
71+
Offset(shaderRect.right, shaderRect.bottom),
72+
[Color(0xFFFF0000), Color(0xFF042a85)],
73+
null,
74+
TileMode.clamp,
75+
Matrix4
76+
.rotationZ((angle / 180) * math.pi)
77+
.toFloat64());
78+
rc.drawRect(shaderRect, Paint()
79+
..color = Color(0xFF000000));
80+
rc.drawOval(shaderRect, paint);
81+
yOffset += 120;
82+
}
83+
expect(rc.hasArbitraryPaint, isTrue);
84+
await _checkScreenshot(rc, 'linear_gradient_oval_matrix');
85+
});
86+
6087
// Regression test for https://github.com/flutter/flutter/issues/50010
6188
test('Should draw linear gradient using rounded rect.', () async {
6289
final RecordingCanvas rc =
63-
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
90+
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
6491
Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300);
6592
final Paint paint = Paint()..shader = Gradient.linear(
6693
Offset(shaderRect.left, shaderRect.top),

0 commit comments

Comments
 (0)