diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index 3b5f134f32411..7fa819ed78870 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 0c1a793bfd49e30fccdd7ad64329a114a9cb32f7 +revision: b38daf293027908861f16362b378ca6865518ded diff --git a/lib/web_ui/lib/src/engine/shader.dart b/lib/web_ui/lib/src/engine/shader.dart index 24542a17750cc..a73e413b88a1d 100644 --- a/lib/web_ui/lib/src/engine/shader.dart +++ b/lib/web_ui/lib/src/engine/shader.dart @@ -80,17 +80,21 @@ void _validateColorStops(List colors, List colorStops) { class GradientLinear extends EngineGradient { GradientLinear( - this.from, - this.to, - this.colors, - this.colorStops, - this.tileMode, + this.from, + this.to, + this.colors, + this.colorStops, + this.tileMode, + Float64List matrix, ) : assert(_offsetIsValid(from)), assert(_offsetIsValid(to)), assert(colors != null), assert(tileMode != null), + this.matrix4 = matrix == null ? null : _FastMatrix64(matrix), super._() { - _validateColorStops(colors, colorStops); + if (assertionsEnabled) { + _validateColorStops(colors, colorStops); + } } final ui.Offset from; @@ -98,11 +102,25 @@ class GradientLinear extends EngineGradient { final List colors; final List colorStops; final ui.TileMode tileMode; + final _FastMatrix64 matrix4; @override html.CanvasGradient createPaintStyle(html.CanvasRenderingContext2D ctx) { - final html.CanvasGradient gradient = - ctx.createLinearGradient(from.dx, from.dy, to.dx, to.dy); + final bool hasMatrix = matrix4 != null; + html.CanvasGradient gradient; + if (hasMatrix) { + final centerX = (from.dx + to.dx) / 2.0; + final centerY = (from.dy + to.dy) / 2.0; + matrix4.transform(from.dx - centerX, from.dy - centerY); + final double fromX = matrix4.transformedX + centerX; + final double fromY = matrix4.transformedY + centerY; + matrix4.transform(to.dx - centerX, to.dy - centerY); + gradient = ctx.createLinearGradient(fromX, fromY, + matrix4.transformedX + centerX, matrix4.transformedY + centerY); + } else { + gradient = ctx.createLinearGradient(from.dx, from.dy, to.dx, to.dy); + } + if (colorStops == null) { assert(colors.length == 2); gradient.addColorStop(0, colorToCssString(colors[0])); @@ -154,7 +172,9 @@ class GradientLinear extends EngineGradient { } // TODO(flutter_web): For transforms and tile modes implement as webgl -// shader instead. See https://github.com/flutter/flutter/issues/32819 +// For now only GradientRotation is supported in flutter which is implemented +// for linear gradient. +// See https://github.com/flutter/flutter/issues/32819 class GradientRadial extends EngineGradient { GradientRadial(this.center, this.radius, this.colors, this.colorStops, this.tileMode, this.matrix4) @@ -170,11 +190,6 @@ class GradientRadial extends EngineGradient { @override Object createPaintStyle(html.CanvasRenderingContext2D ctx) { if (!experimentalUseSkia) { - // The DOM backend does not (yet) support all parameters. - if (matrix4 != null && !Matrix4.fromFloat32List(matrix4).isIdentity()) { - throw UnimplementedError( - 'matrix4 not supported in GradientRadial shader'); - } if (tileMode != ui.TileMode.clamp) { throw UnimplementedError( 'TileMode not supported in GradientRadial shader'); diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 2979838112854..30771054efbc7 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -481,3 +481,15 @@ FutureOr sendFontChangeMessage() async { }); } } + +// Stores matrix in a form that allows zero allocation transforms. +class _FastMatrix64 { + final Float64List matrix; + double transformedX = 0, transformedY = 0; + _FastMatrix64(this.matrix); + + void transform(double x, double y) { + transformedX = matrix[12] + (matrix[0] * x) + (matrix[4] * y); + transformedY = matrix[13] + (matrix[1] * x) + (matrix[5] * y); + } +} diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index dc501681b68e8..833f2e96a741c 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1092,10 +1092,9 @@ abstract class Gradient extends Shader { List colors, [ List colorStops, TileMode tileMode = TileMode.clamp, - // TODO(flutter_web): see https://github.com/flutter/flutter/issues/32819 Float64List matrix4, ]) => - engine.GradientLinear(from, to, colors, colorStops, tileMode); + engine.GradientLinear(from, to, colors, colorStops, tileMode, matrix4); /// Creates a radial gradient centered at `center` that ends at `radius` /// distance from the center. diff --git a/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart b/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart index 35b7cae8f9ecf..b4b059597d194 100644 --- a/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart @@ -4,6 +4,7 @@ // @dart = 2.6 import 'dart:html' as html; +import 'dart:math' as math; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart'; @@ -46,7 +47,7 @@ void main() async { test('Should draw linear gradient using rectangle.', () async { final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); final Paint paint = Paint()..shader = Gradient.linear( Offset(shaderRect.left, shaderRect.top), @@ -57,10 +58,36 @@ void main() async { await _checkScreenshot(rc, 'linear_gradient_rect'); }); + test('Should draw linear gradient with transform.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + List angles = [0.0, 90.0, 180.0]; + double yOffset = 0; + for (double angle in angles) { + final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); + final Paint paint = Paint() + ..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.right, shaderRect.bottom), + [Color(0xFFFF0000), Color(0xFF042a85)], + null, + TileMode.clamp, + Matrix4 + .rotationZ((angle / 180) * math.pi) + .toFloat64()); + rc.drawRect(shaderRect, Paint() + ..color = Color(0xFF000000)); + rc.drawOval(shaderRect, paint); + yOffset += 120; + } + expect(rc.hasArbitraryPaint, isTrue); + await _checkScreenshot(rc, 'linear_gradient_oval_matrix'); + }); + // Regression test for https://github.com/flutter/flutter/issues/50010 test('Should draw linear gradient using rounded rect.', () async { final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); final Paint paint = Paint()..shader = Gradient.linear( Offset(shaderRect.left, shaderRect.top),