diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader.dart b/lib/web_ui/lib/src/engine/html/shaders/shader.dart
index 9a8fb3ac9874f..8faa06617c309 100644
--- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart
+++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart
@@ -98,13 +98,23 @@ class GradientSweep extends EngineGradient {
final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width);
final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height);
gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)),
- 2 * (shaderBounds.height * (centerY - 0.5)));
+ 2 * (shaderBounds.height * (0.5 - centerY)));
final Object angleRange = gl.getUniformLocation(glProgram.program, 'angle_range');
gl.setUniform2f(angleRange, startAngle, endAngle);
normalizedGradient.setupUniforms(gl, glProgram);
+
final Object gradientMatrix =
gl.getUniformLocation(glProgram.program, 'm_gradient');
- gl.setUniformMatrix4fv(gradientMatrix, false, matrix4 ?? Matrix4.identity().storage);
+ final Matrix4 gradientTransform = Matrix4.identity();
+ if (matrix4 != null) {
+ final Matrix4 m4 = Matrix4.zero()
+ ..copyInverse(Matrix4.fromFloat32List(matrix4!));
+ gradientTransform.translate(-center.dx, -center.dy);
+ gradientTransform.multiply(m4);
+ gradientTransform.translate(center.dx, center.dy);
+ }
+ gl.setUniformMatrix4fv(gradientMatrix, false, gradientTransform.storage);
+
final Object result = () {
if (createDataUrl) {
return glRenderer!.drawRectToImageUrl(
@@ -149,7 +159,7 @@ class GradientSweep extends EngineGradient {
// Sweep gradient
method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);');
method.addStatement(
- 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;');
+ 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);');
method.addStatement(
'float angle = atan(-localCoord.y, -localCoord.x) + ${math.pi};');
method.addStatement('float sweep = angle_range.y - angle_range.x;');
@@ -317,14 +327,12 @@ class GradientLinear extends EngineGradient {
// with flipped y axis.
// We flip y axis, translate to center, multiply matrix and translate
// and flip back so it is applied correctly.
- final Matrix4 m4 = Matrix4.fromFloat32List(matrix4!.matrix);
- gradientTransform.scale(1, -1);
- gradientTransform.translate(
- -shaderBounds.center.dx, -shaderBounds.center.dy);
+ final Matrix4 m4 = Matrix4.zero()
+ ..copyInverse(Matrix4.fromFloat32List(matrix4!.matrix));
+ final ui.Offset center = shaderBounds.center;
+ gradientTransform.translate(-center.dx, -center.dy);
gradientTransform.multiply(m4);
- gradientTransform.translate(
- shaderBounds.center.dx, shaderBounds.center.dy);
- gradientTransform.scale(1, -1);
+ gradientTransform.translate(center.dx, center.dy);
}
gradientTransform.multiply(rotationZ);
@@ -465,6 +473,12 @@ String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method,
sourcePrefix: 'threshold',
biasName: 'bias',
scaleName: 'scale');
+ if (tileMode == ui.TileMode.decal) {
+ method.addStatement('if (st < 0.0 || st > 1.0) {');
+ method.addStatement(' ${builder.fragmentColor.name} = vec4(0, 0, 0, 0);');
+ method.addStatement(' return;');
+ method.addStatement('}');
+ }
return probeName;
}
@@ -483,7 +497,7 @@ class GradientRadial extends EngineGradient {
@override
Object createPaintStyle(DomCanvasRenderingContext2D? ctx,
ui.Rect? shaderBounds, double density) {
- if (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) {
+ if (matrix4 == null && (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal)) {
return _createCanvasGradient(ctx, shaderBounds, density);
} else {
return _createGlGradient(ctx, shaderBounds, density);
@@ -533,15 +547,24 @@ class GradientRadial extends EngineGradient {
final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width);
final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height);
gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)),
- 2 * (shaderBounds.height * (centerY - 0.5)));
+ 2 * (shaderBounds.height * (0.5 - centerY)));
final Object radiusUniform = gl.getUniformLocation(glProgram.program, 'u_radius');
gl.setUniform1f(radiusUniform, radius);
normalizedGradient.setupUniforms(gl, glProgram);
final Object gradientMatrix =
gl.getUniformLocation(glProgram.program, 'm_gradient');
- gl.setUniformMatrix4fv(gradientMatrix, false,
- matrix4 == null ? Matrix4.identity().storage : matrix4!);
+
+ final Matrix4 gradientTransform = Matrix4.identity();
+
+ if (matrix4 != null) {
+ final Matrix4 m4 = Matrix4.zero()
+ ..copyInverse(Matrix4.fromFloat32List(matrix4!));
+ gradientTransform.translate(-center.dx, -center.dy);
+ gradientTransform.multiply(m4);
+ gradientTransform.translate(center.dx, center.dy);
+ }
+ gl.setUniformMatrix4fv(gradientMatrix, false, gradientTransform.storage);
final Object result = () {
if (createDataUrl) {
@@ -587,7 +610,7 @@ class GradientRadial extends EngineGradient {
// Sweep gradient
method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);');
method.addStatement(
- 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;');
+ 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);');
method.addStatement('float dist = length(localCoord);');
method.addStatement(
'float st = abs(dist / u_radius);');
@@ -666,7 +689,7 @@ class GradientConical extends GradientRadial {
// Sweep gradient
method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);');
method.addStatement(
- 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;');
+ 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);');
method.addStatement('float dist = length(localCoord);');
final String f = (focalRadius /
(math.min(shaderBounds.width, shaderBounds.height) / 2.0))
diff --git a/lib/web_ui/test/html/shaders/gradient_golden_test.dart b/lib/web_ui/test/html/shaders/gradient_golden_test.dart
index 8dfa8fc66503f..e56de2d87be7e 100644
--- a/lib/web_ui/test/html/shaders/gradient_golden_test.dart
+++ b/lib/web_ui/test/html/shaders/gradient_golden_test.dart
@@ -352,7 +352,7 @@ Future testMain() async {
RenderStrategy());
canvas.endRecording();
canvas.apply(engineCanvas, screenRect);
- });
+ }, skip: isFirefox);
test("Creating lots of gradients doesn't create too many webgl contexts",
() async {
@@ -431,7 +431,7 @@ Future testMain() async {
canvas.restore();
await canvasScreenshot(canvas, 'linear_gradient_rect_clamp_rotated', canvasRect: screenRect, region: region);
- });
+ }, skip: isFirefox);
test('Paints linear gradient properly when within svg context', () async {
final RecordingCanvas canvas =
@@ -465,7 +465,176 @@ Future testMain() async {
canvas.restore();
await canvasScreenshot(canvas, 'linear_gradient_in_svg_context', canvasRect: screenRect, region: region);
- });
+ }, skip: isFirefox);
+
+ test('Paints transformed linear gradient', () async {
+ final RecordingCanvas canvas =
+ RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
+ canvas.save();
+
+ const List colors = [
+ Color(0xFF000000),
+ Color(0xFFFF3C38),
+ Color(0xFFFF8C42),
+ Color(0xFFFFF275),
+ Color(0xFF6699CC),
+ Color(0xFF656D78),
+ ];
+
+ const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0];
+
+ final Matrix4 transform = Matrix4.identity()
+ ..translate(50, 50)
+ ..scale(0.3, 0.7)
+ ..rotateZ(0.5);
+
+ final GradientLinear linearGradient = GradientLinear(
+ const Offset(5, 5),
+ const Offset(200, 130),
+ colors,
+ stops,
+ TileMode.clamp,
+ transform.storage,
+ );
+
+ const double kBoxWidth = 150;
+ const double kBoxHeight = 80;
+
+ Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight);
+ canvas.drawRect(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineLinearGradientToShader(linearGradient, rectBounds),
+ );
+
+ rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight);
+ canvas.drawOval(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineLinearGradientToShader(linearGradient, rectBounds),
+ );
+
+ canvas.restore();
+ await canvasScreenshot(
+ canvas,
+ 'linear_gradient_clamp_transformed',
+ canvasRect: screenRect,
+ region: region,
+ );
+ }, skip: isFirefox);
+
+ test('Paints transformed sweep gradient', () async {
+ final RecordingCanvas canvas =
+ RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
+ canvas.save();
+
+ const List colors = [
+ Color(0xFF000000),
+ Color(0xFFFF3C38),
+ Color(0xFFFF8C42),
+ Color(0xFFFFF275),
+ Color(0xFF6699CC),
+ Color(0xFF656D78),
+ ];
+
+ const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0];
+
+ final Matrix4 transform = Matrix4.identity()
+ ..translate(100, 150)
+ ..scale(0.3, 0.7)
+ ..rotateZ(0.5);
+
+ final GradientSweep sweepGradient = GradientSweep(
+ const Offset(0.5, 0.5),
+ colors,
+ stops,
+ TileMode.clamp,
+ 0.0,
+ 2 * math.pi,
+ transform.storage,
+ );
+
+ const double kBoxWidth = 150;
+ const double kBoxHeight = 80;
+
+ Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight);
+ canvas.drawRect(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineGradientToShader(sweepGradient, rectBounds),
+ );
+
+ rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight);
+ canvas.drawOval(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineGradientToShader(sweepGradient, rectBounds),
+ );
+
+ canvas.restore();
+ await canvasScreenshot(
+ canvas,
+ 'sweep_gradient_clamp_transformed',
+ canvasRect: screenRect,
+ region: region,
+ );
+ }, skip: isFirefox);
+
+ test('Paints transformed radial gradient', () async {
+ final RecordingCanvas canvas =
+ RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
+ canvas.save();
+
+ const List colors = [
+ Color(0xFF000000),
+ Color(0xFFFF3C38),
+ Color(0xFFFF8C42),
+ Color(0xFFFFF275),
+ Color(0xFF6699CC),
+ Color(0xFF656D78),
+ ];
+
+ const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0];
+
+ final Matrix4 transform = Matrix4.identity()
+ ..translate(50, 50)
+ ..scale(0.3, 0.7)
+ ..rotateZ(0.5);
+
+ final GradientRadial radialGradient = GradientRadial(
+ const Offset(0.5, 0.5),
+ 400,
+ colors,
+ stops,
+ TileMode.clamp,
+ transform.storage,
+ );
+
+ const double kBoxWidth = 150;
+ const double kBoxHeight = 80;
+
+ Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight);
+ canvas.drawRect(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineRadialGradientToShader(radialGradient, rectBounds),
+ );
+
+ rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight);
+ canvas.drawOval(
+ rectBounds,
+ SurfacePaint()
+ ..shader = engineRadialGradientToShader(radialGradient, rectBounds),
+ );
+
+ canvas.restore();
+ await canvasScreenshot(
+ canvas,
+ 'radial_gradient_clamp_transformed',
+ canvasRect: screenRect,
+ region: region,
+ );
+ }, skip: isFirefox);
}
Shader engineGradientToShader(GradientSweep gradient, Rect rect) {
@@ -488,6 +657,18 @@ Shader engineLinearGradientToShader(GradientLinear gradient, Rect rect) {
);
}
+Shader engineRadialGradientToShader(GradientRadial gradient, Rect rect) {
+ return Gradient.radial(
+ Offset(rect.left + gradient.center.dx * rect.width,
+ rect.top + gradient.center.dy * rect.height),
+ gradient.radius,
+ gradient.colors,
+ gradient.colorStops,
+ gradient.tileMode,
+ gradient.matrix4 == null ? null : Float64List.fromList(gradient.matrix4!),
+ );
+}
+
Path samplePathFromRect(Rect rectBounds) =>
Path()
..moveTo(rectBounds.center.dx, rectBounds.top)
diff --git a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart
index bf1ab3752cebd..433d697348535 100644
--- a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart
+++ b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart
@@ -84,7 +84,7 @@ Future testMain() async {
}
expect(rc.renderStrategy.hasArbitraryPaint, isTrue);
await canvasScreenshot(rc, 'linear_gradient_oval_matrix');
- });
+ }, skip: isFirefox);
// Regression test for https://github.com/flutter/flutter/issues/50010
test('Should draw linear gradient using rounded rect.', () async {