diff --git a/lib/web_ui/lib/src/engine/html/dom_canvas.dart b/lib/web_ui/lib/src/engine/html/dom_canvas.dart
index 0d3250e27586b..a07a15534664f 100644
--- a/lib/web_ui/lib/src/engine/html/dom_canvas.dart
+++ b/lib/web_ui/lib/src/engine/html/dom_canvas.dart
@@ -11,7 +11,6 @@ import 'package:ui/ui.dart' as ui;
import '../browser_detection.dart';
import '../engine_canvas.dart';
-import '../html_image_codec.dart';
import '../text/canvas_paragraph.dart';
import '../util.dart';
import '../vector_math.dart';
@@ -19,6 +18,7 @@ import 'painting.dart';
import 'path/path.dart';
import 'path/path_to_svg.dart';
import 'shaders/image_shader.dart';
+import 'shaders/shader.dart';
/// A canvas that renders to DOM elements and CSS properties.
class DomCanvas extends EngineCanvas with SaveElementStackTracking {
@@ -162,7 +162,6 @@ ui.Color blurColor(ui.Color color, double sigma) {
html.HtmlElement buildDrawRectElement(
ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) {
- assert(paint.shader == null);
final html.HtmlElement rectangle =
html.document.createElement(tagName) as html.HtmlElement;
assert(() {
@@ -226,19 +225,28 @@ html.HtmlElement buildDrawRectElement(
style
..width = '${right - left}px'
..height = '${bottom - top}px'
- ..backgroundColor = cssColor;
-
- if (paint.shader != null && paint.shader is EngineImageShader) {
- _applyImageShaderToElement(rectangle, paint.shader! as EngineImageShader);
- }
+ ..backgroundColor = cssColor
+ ..backgroundImage = _getBackgroundImageCssValue(paint.shader, rect);
}
return rectangle;
}
-void _applyImageShaderToElement(html.HtmlElement targetElement,
- EngineImageShader imageShader) {
- final HtmlImage image = imageShader.image;
- targetElement.style.backgroundImage = image.imgElement.src;
+String _getBackgroundImageCssValue(ui.Shader? shader, ui.Rect bounds) {
+ final String url = _getBackgroundImageUrl(shader, bounds);
+ return (url != '') ? "url('$url'": '';
+}
+
+String _getBackgroundImageUrl(ui.Shader? shader, ui.Rect bounds) {
+ if(shader != null) {
+ if(shader is EngineImageShader) {
+ return shader.image.imgElement.src ?? '';
+ }
+
+ if(shader is EngineGradient) {
+ return shader.createImageBitmap(bounds, 1, true) as String;
+ }
+ }
+ return '';
}
void applyRRectBorderRadius(html.CssStyleDeclaration style, ui.RRect rrect) {
diff --git a/lib/web_ui/lib/src/engine/html/shader_mask.dart b/lib/web_ui/lib/src/engine/html/shader_mask.dart
index 7f27cd7b22fa6..977f0f9c70bd4 100644
--- a/lib/web_ui/lib/src/engine/html/shader_mask.dart
+++ b/lib/web_ui/lib/src/engine/html/shader_mask.dart
@@ -108,8 +108,13 @@ class PersistedShaderMask extends PersistedContainerSurface
void _applyGradientShader() {
if (shader is EngineGradient) {
final EngineGradient gradientShader = shader as EngineGradient;
+
+ // The gradient shader's bounds are in the context of the element itself,
+ // rather than the global position, so translate it back to the origin.
+ final ui.Rect translatedRect =
+ maskRect.translate(-maskRect.left, -maskRect.top);
final String imageUrl =
- gradientShader.createImageBitmap(maskRect, 1, true) as String;
+ gradientShader.createImageBitmap(translatedRect, 1, true) as String;
ui.BlendMode blendModeTemp = blendMode;
switch (blendModeTemp) {
case ui.BlendMode.clear:
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 c80cdfde046b7..9d54123b5e8c6 100644
--- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart
+++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart
@@ -234,14 +234,6 @@ class GradientLinear extends EngineGradient {
_createLinearFragmentShader(normalizedGradient, tileMode));
gl.useProgram(glProgram);
- /// When creating an image to apply to a dom element, render
- /// contents at 0,0 and adjust gradient vector for shaderBounds.
- final bool translateToOrigin = createDataUrl;
-
- if (translateToOrigin) {
- shaderBounds = shaderBounds.translate(-shaderBounds.left, -shaderBounds.top);
- }
-
// Setup from/to uniforms.
//
// From/to is relative to shaderBounds.
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 599ae80754026..a04c7c859f680 100644
--- a/lib/web_ui/test/html/shaders/gradient_golden_test.dart
+++ b/lib/web_ui/test/html/shaders/gradient_golden_test.dart
@@ -430,6 +430,40 @@ Future testMain() async {
canvas.restore();
await _checkScreenshot(canvas, 'linear_gradient_rect_clamp_rotated');
});
+
+ test('Paints linear gradient properly when within svg context', () async {
+ final RecordingCanvas canvas =
+ RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 240));
+ canvas.save();
+
+ canvas.renderStrategy.isInsideSvgFilterTree = true;
+
+ final SurfacePaint borderPaint = SurfacePaint()
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 1
+ ..color = const Color(0xFF000000);
+
+ const List colors = [
+ Color(0xFFFF0000),
+ Color(0xFF0000FF),
+ ];
+
+ final GradientLinear linearGradient = GradientLinear(const Offset(125, 75),
+ const Offset(175, 125),
+ colors, null, TileMode.clamp,
+ Matrix4.identity().storage);
+
+ const double kBoxWidth = 150;
+ const double kBoxHeight = 100;
+ // Gradient with default center.
+ const Rect rectBounds = Rect.fromLTWH(100, 50, kBoxWidth, kBoxHeight);
+ canvas.drawRect(rectBounds,
+ SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds));
+ canvas.drawRect(rectBounds, borderPaint);
+
+ canvas.restore();
+ await _checkScreenshot(canvas, 'linear_gradient_in_svg_context');
+ });
}
Shader engineGradientToShader(GradientSweep gradient, Rect rect) {