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) {