From ad33494e18af674bf86c2d8f38a20fc7dae9601d Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 29 Apr 2021 12:34:42 -0700 Subject: [PATCH 01/12] Move parts to libs --- lib/web_ui/lib/src/engine.dart | 31 ++- lib/web_ui/lib/src/engine/canvas_pool.dart | 2 +- .../engine/canvaskit/canvaskit_canvas.dart | 10 +- .../lib/src/engine/canvaskit/shader.dart | 14 +- .../lib/src/engine/font_change_util.dart | 28 +++ lib/web_ui/lib/src/engine/html/clip.dart | 12 +- .../lib/src/engine/html/path/conic.dart | 63 ++--- .../lib/src/engine/html/path/cubic.dart | 96 ++++---- lib/web_ui/lib/src/engine/html/path/path.dart | 57 ++--- .../src/engine/html/path/path_iterator.dart | 220 +++++++++++++++++ .../src/engine/html/path/path_metrics.dart | 73 +++--- .../lib/src/engine/html/path/path_ref.dart | 22 +- .../lib/src/engine/html/path/path_utils.dart | 80 ++++-- .../src/engine/html/path/path_windings.dart | 231 +----------------- .../lib/src/engine/html/path/tangent.dart | 24 +- .../lib/src/engine/html/path_to_svg_clip.dart | 56 +++++ .../lib/src/engine/html/recording_canvas.dart | 2 +- .../lib/src/engine/html/scene_builder.dart | 2 +- .../lib/src/engine/html/shaders/shader.dart | 16 +- lib/web_ui/lib/src/engine/util.dart | 104 +------- lib/web_ui/lib/src/engine/validators.dart | 13 + 21 files changed, 620 insertions(+), 536 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/font_change_util.dart create mode 100644 lib/web_ui/lib/src/engine/html/path/path_iterator.dart create mode 100644 lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 7966f4b0d7f28..3c06fc0dfc200 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -38,6 +38,27 @@ export 'engine/alarm_clock.dart'; import 'engine/browser_detection.dart'; export 'engine/browser_detection.dart'; +import 'engine/html/path_to_svg_clip.dart'; +export 'engine/html/path_to_svg_clip.dart'; + +import 'engine/html/path/conic.dart'; +export 'engine/html/path/conic.dart'; + +import 'engine/html/path/cubic.dart'; +export 'engine/html/path/cubic.dart'; + +import 'engine/html/path/path_iterator.dart'; +export 'engine/html/path/path_iterator.dart'; + +import 'engine/html/path/path_metrics.dart'; +export 'engine/html/path/path_metrics.dart'; + +import 'engine/html/path/path_ref.dart'; +export 'engine/html/path/path_ref.dart'; + +import 'engine/html/path/path_utils.dart'; +export 'engine/html/path/path_utils.dart'; + import 'engine/mouse_cursor.dart'; export 'engine/mouse_cursor.dart'; @@ -83,6 +104,9 @@ export 'engine/test_embedding.dart'; import 'engine/ulps.dart'; export 'engine/ulps.dart'; +import 'engine/util.dart'; +export 'engine/util.dart'; + import 'engine/validators.dart'; export 'engine/validators.dart'; @@ -141,13 +165,8 @@ part 'engine/html/image_filter.dart'; part 'engine/html/offset.dart'; part 'engine/html/opacity.dart'; part 'engine/html/painting.dart'; -part 'engine/html/path/conic.dart'; -part 'engine/html/path/cubic.dart'; part 'engine/html/path/path.dart'; -part 'engine/html/path/path_metrics.dart'; -part 'engine/html/path/path_ref.dart'; part 'engine/html/path/path_to_svg.dart'; -part 'engine/html/path/path_utils.dart'; part 'engine/html/path/path_windings.dart'; part 'engine/html/path/tangent.dart'; part 'engine/html/picture.dart'; @@ -200,7 +219,7 @@ part 'engine/text_editing/autofill_hint.dart'; part 'engine/text_editing/input_type.dart'; part 'engine/text_editing/text_capitalization.dart'; part 'engine/text_editing/text_editing.dart'; -part 'engine/util.dart'; +part 'engine/font_change_util.dart'; part 'engine/window.dart'; // The mode the app is running in. diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index 4fd2f6de89a0a..5a2d709fea51d 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -193,7 +193,7 @@ class _CanvasPool extends _SaveStackTracking { } catch (e) { // Firefox may explode here: // https://bugzilla.mozilla.org/show_bug.cgi?id=941146 - if (!_isNsErrorFailureException(e)) { + if (!isNsErrorFailureException(e)) { rethrow; } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart index f4dad52128147..c83457ba14c6d 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart @@ -135,8 +135,8 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { - assert(_offsetIsValid(p1)); - assert(_offsetIsValid(p2)); + assert(offsetIsValid(p1)); + assert(offsetIsValid(p2)); assert(paint != null); // ignore: unnecessary_null_comparison _drawLine(p1, p2, paint); } @@ -202,7 +202,7 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawCircle(ui.Offset c, double radius, ui.Paint paint) { - assert(_offsetIsValid(c)); + assert(offsetIsValid(c)); assert(paint != null); // ignore: unnecessary_null_comparison _drawCircle(c, radius, paint); } @@ -236,7 +236,7 @@ class CanvasKitCanvas implements ui.Canvas { void drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { // ignore: unnecessary_null_comparison assert(image != null); // image is checked on the engine side - assert(_offsetIsValid(p)); + assert(offsetIsValid(p)); assert(paint != null); // ignore: unnecessary_null_comparison _canvas.drawImage(image as CkImage, p, paint as CkPaint); } @@ -272,7 +272,7 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { assert(paragraph != null); // ignore: unnecessary_null_comparison - assert(_offsetIsValid(offset)); + assert(offsetIsValid(offset)); _drawParagraph(paragraph, offset); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/shader.dart b/lib/web_ui/lib/src/engine/canvaskit/shader.dart index bd284e91ae0f3..f6908b509e3db 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/shader.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/shader.dart @@ -17,14 +17,14 @@ abstract class CkShader extends ManagedSkiaObject class CkGradientSweep extends CkShader implements ui.Gradient { CkGradientSweep(this.center, this.colors, this.colorStops, this.tileMode, this.startAngle, this.endAngle, this.matrix4) - : assert(_offsetIsValid(center)), + : assert(offsetIsValid(center)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison assert(startAngle != null), // ignore: unnecessary_null_comparison assert(endAngle != null), // ignore: unnecessary_null_comparison assert(startAngle < endAngle), - assert(matrix4 == null || _matrix4IsValid(matrix4)) { - _validateColorStops(colors, colorStops); + assert(matrix4 == null || matrix4IsValid(matrix4)) { + validateColorStops(colors, colorStops); } final ui.Offset center; @@ -65,14 +65,14 @@ class CkGradientLinear extends CkShader implements ui.Gradient { this.colorStops, this.tileMode, Float32List? matrix, - ) : assert(_offsetIsValid(from)), - assert(_offsetIsValid(to)), + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison this.matrix4 = matrix { if (assertionsEnabled) { - assert(matrix4 == null || _matrix4IsValid(matrix4!)); - _validateColorStops(colors, colorStops); + assert(matrix4 == null || matrix4IsValid(matrix4!)); + validateColorStops(colors, colorStops); } } diff --git a/lib/web_ui/lib/src/engine/font_change_util.dart b/lib/web_ui/lib/src/engine/font_change_util.dart new file mode 100644 index 0000000000000..3aebc7532f5dc --- /dev/null +++ b/lib/web_ui/lib/src/engine/font_change_util.dart @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +final ByteData? _fontChangeMessage = + JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); + +// Font load callbacks will typically arrive in sequence, we want to prevent +// sendFontChangeMessage of causing multiple synchronous rebuilds. +// This flag ensures we properly schedule a single call to framework. +bool _fontChangeScheduled = false; + +FutureOr sendFontChangeMessage() async { + if (!_fontChangeScheduled) { + _fontChangeScheduled = true; + // Batch updates into next animationframe. + html.window.requestAnimationFrame((num _) { + _fontChangeScheduled = false; + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/system', + _fontChangeMessage, + (_) {}, + ); + }); + } +} diff --git a/lib/web_ui/lib/src/engine/html/clip.dart b/lib/web_ui/lib/src/engine/html/clip.dart index 447c15dd5346b..470bc1c6a538f 100644 --- a/lib/web_ui/lib/src/engine/html/clip.dart +++ b/lib/web_ui/lib/src/engine/html/clip.dart @@ -326,12 +326,12 @@ class PersistedPhysicalShape extends PersistedContainerSurface /// to clipping rect bounds (which is the case for elevation == 0.0 where /// we shift outer/inner clip area instead to position clip-path). final String svgClipPath = elevation == 0.0 - ? _pathToSvgClipPath(path, + ? pathToSvgClipPath(path, offsetX: -pathBounds.left, offsetY: -pathBounds.top, scaleX: 1.0 / pathBounds.width, scaleY: 1.0 / pathBounds.height) - : _pathToSvgClipPath(path, + : pathToSvgClipPath(path, offsetX: 0.0, offsetY: 0.0, scaleX: 1.0 / pathBounds.right, @@ -345,7 +345,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer()); domRenderer.append(rootElement!, _clipElement!); if (elevation == 0.0) { - DomRenderer.setClipPath(rootElement!, 'url(#svgClip$_clipIdCounter)'); + DomRenderer.setClipPath(rootElement!, createSvgClipUrl()); final html.CssStyleDeclaration rootElementStyle = rootElement!.style; rootElementStyle ..overflow = '' @@ -360,7 +360,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface return; } - DomRenderer.setClipPath(childContainer!, 'url(#svgClip$_clipIdCounter)'); + DomRenderer.setClipPath(childContainer!, createSvgClipUrl()); final html.CssStyleDeclaration rootElementStyle = rootElement!.style; rootElementStyle ..overflow = '' @@ -500,9 +500,9 @@ class PersistedClipPath extends PersistedContainerSurface /// Creates an svg clipPath and applies it to [element]. String createSvgClipDef(html.HtmlElement element, ui.Path clipPath) { final ui.Rect pathBounds = clipPath.getBounds(); - final String svgClipPath = _pathToSvgClipPath(clipPath, + final String svgClipPath = pathToSvgClipPath(clipPath, scaleX: 1.0 / pathBounds.right, scaleY: 1.0 / pathBounds.bottom); - DomRenderer.setClipPath(element, 'url(#svgClip$_clipIdCounter)'); + DomRenderer.setClipPath(element, createSvgClipUrl()); // We need to set width and height for the clipElement to cover the // bounds of the path since browsers such as Safari and Edge // seem to incorrectly intersect the element bounding rect with diff --git a/lib/web_ui/lib/src/engine/html/path/conic.dart b/lib/web_ui/lib/src/engine/html/path/conic.dart index 1c0e48a9c9c04..5febe594db3e6 100644 --- a/lib/web_ui/lib/src/engine/html/path/conic.dart +++ b/lib/web_ui/lib/src/engine/html/path/conic.dart @@ -2,7 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; +import 'path_utils.dart'; /// Converts conic curve to a list of quadratic curves for rendering on /// canvas or conversion to svg. @@ -198,7 +202,7 @@ class Conic { final double coeff0 = fW * p20 - p20; final double coeff1 = p20 - 2 * wP10; final double coeff2 = wP10; - final _QuadRoots quadRoots = _QuadRoots(); + final QuadRoots quadRoots = QuadRoots(); int rootCount = quadRoots.findRoots(coeff0, coeff1, coeff2); assert(rootCount == 0 || rootCount == 1); if (rootCount == 1) { @@ -230,12 +234,12 @@ class Conic { final double dz1 = dz0 + (dz2 - dz0) * t; // Compute new weights. final double root = math.sqrt(dz1); - if (_nearlyEqual(root, 0)) { + if (SPath.nearlyEqual(root, 0)) { return false; } final double w0 = dz0 / root; final double w2 = dz2 / root; - if (_nearlyEqual(dz0, 0) || _nearlyEqual(dz1, 0) || _nearlyEqual(dz2, 0)) { + if (SPath.nearlyEqual(dz0, 0) || SPath.nearlyEqual(dz1, 0) || SPath.nearlyEqual(dz2, 0)) { return false; } // Now we can construct the 2 conics by projecting 3D down to 2D. @@ -314,33 +318,34 @@ class Conic { double ay = fW * p20y - p20y; double bx = p20x - cx - cx; double by = p20y - cy - cy; - _SkQuadCoefficients quadC = _SkQuadCoefficients(ax, ay, bx, by, cx, cy); + SkQuadCoefficients quadC = SkQuadCoefficients(ax, ay, bx, by, cx, cy); return ui.Offset(quadC.evalX(t), quadC.evalY(t)); } -} -double _conicEvalNumerator( - double p0, double p1, double p2, double w, double t) { - assert(t >= 0 && t <= 1); - final double src2w = p1 * w; - final C = p0; - final A = p2 - 2 * src2w + C; - final B = 2 * (src2w - C); - return polyEval(A, B, C, t); -} + static double evalNumerator( + double p0, double p1, double p2, double w, double t) { + assert(t >= 0 && t <= 1); + final double src2w = p1 * w; + final C = p0; + final A = p2 - 2 * src2w + C; + final B = 2 * (src2w - C); + return polyEval(A, B, C, t); + } -double _conicEvalDenominator(double w, double t) { - double B = 2 * (w - 1); - double C = 1; - double A = -B; - return polyEval(A, B, C, t); + static double evalDenominator(double w, double t) { + double B = 2 * (w - 1); + double C = 1; + double A = -B; + return polyEval(A, B, C, t); + } } -class _QuadBounds { +class QuadBounds { double minX = 0; double minY = 0; double maxX = 0; double maxY = 0; + void calculateBounds(Float32List points, int pointIndex) { final double x1 = points[pointIndex++]; final double y1 = points[pointIndex++]; @@ -398,7 +403,7 @@ class _QuadBounds { } } -class _ConicBounds { +class ConicBounds { double minX = 0; double minY = 0; double maxX = 0; @@ -420,7 +425,7 @@ class _ConicBounds { // ------------------------------------------------ // {t^2 (2 - 2 w), t (-2 + 2 w), 1} // Calculate coefficients and solve root. - _QuadRoots roots = _QuadRoots(); + QuadRoots roots = QuadRoots(); final double P20x = x2 - x1; final double P10x = cpX - x1; final double wP10x = w * P10x; @@ -431,10 +436,10 @@ class _ConicBounds { if (n != 0) { final double t1 = roots.root0!; if ((t1 >= 0) && (t1 <= 1.0)) { - final double denom = _conicEvalDenominator(w, t1); - double numerator = _conicEvalNumerator(x1, cpX, x2, w, t1); + final double denom = Conic.evalDenominator(w, t1); + double numerator = Conic.evalNumerator(x1, cpX, x2, w, t1); final double extremaX = numerator / denom; - numerator = _conicEvalNumerator(y1, cpY, y2, w, t1); + numerator = Conic.evalNumerator(y1, cpY, y2, w, t1); final double extremaY = numerator / denom; // Expand bounds. minX = math.min(minX, extremaX); @@ -454,10 +459,10 @@ class _ConicBounds { if (n != 0) { final double t2 = roots.root0!; if ((t2 >= 0) && (t2 <= 1.0)) { - final double denom = _conicEvalDenominator(w, t2); - double numerator = _conicEvalNumerator(x1, cpX, x2, w, t2); + final double denom = Conic.evalDenominator(w, t2); + double numerator = Conic.evalNumerator(x1, cpX, x2, w, t2); final double extrema2X = numerator / denom; - numerator = _conicEvalNumerator(y1, cpY, y2, w, t2); + numerator = Conic.evalNumerator(y1, cpY, y2, w, t2); final double extrema2Y = numerator / denom; // Expand bounds. minX = math.min(minX, extrema2X); diff --git a/lib/web_ui/lib/src/engine/html/path/cubic.dart b/lib/web_ui/lib/src/engine/html/path/cubic.dart index a2358cd6d3eeb..ee2de98b5ff56 100644 --- a/lib/web_ui/lib/src/engine/html/path/cubic.dart +++ b/lib/web_ui/lib/src/engine/html/path/cubic.dart @@ -2,17 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; +import 'dart:math' as math; + +import 'path_utils.dart'; +import '../../util.dart'; /// Chops cubic at Y extrema points and writes result to [dest]. /// /// [points] and [dest] are allowed to share underlying storage as long. -int _chopCubicAtYExtrema(Float32List points, Float32List dest) { +int chopCubicAtYExtrema(Float32List points, Float32List dest) { final double y0 = points[1]; final double y1 = points[3]; final double y2 = points[5]; final double y3 = points[7]; - _QuadRoots _quadRoots = _findCubicExtrema(y0, y1, y2, y3); + QuadRoots _quadRoots = _findCubicExtrema(y0, y1, y2, y3); final List roots = _quadRoots.roots; if (roots.isEmpty) { // No roots, just use input cubic. @@ -30,12 +34,12 @@ int _chopCubicAtYExtrema(Float32List points, Float32List dest) { return rootCount; } -_QuadRoots _findCubicExtrema(double a, double b, double c, double d) { +QuadRoots _findCubicExtrema(double a, double b, double c, double d) { // A,B,C scaled by 1/3 to simplify final double A = d - a + 3 * (b - c); final double B = 2 * (a - b - b + c); final double C = b - a; - return _QuadRoots()..findRoots(A, B, C); + return QuadRoots()..findRoots(A, B, C); } /// Subdivides cubic curve for a list of t values. @@ -72,7 +76,7 @@ void _chopCubicAt( bufferPos += 6; // watch out in case the renormalized t isn't in range - if ((t = _validUnitDivide( + if ((t = validUnitDivide( tValues[i + 1] - tValues[i], 1.0 - tValues[i])) == null) { // Can't renormalize last point, just create a degenerate cubic. @@ -99,18 +103,18 @@ void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, final double p2y = points[bufferPos + 5]; final double p3x = points[bufferPos + 6]; // If startT == 0 chop at end point and return curve. - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double cd1x = _interpolate(p2x, p3x, t); - final double cd1y = _interpolate(p2y, p3y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); - final double bcd1x = _interpolate(bc1x, cd1x, t); - final double bcd1y = _interpolate(bc1y, cd1y, t); - final double abcd1x = _interpolate(abc1x, bcd1x, t); - final double abcd1y = _interpolate(abc1y, bcd1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double cd1x = interpolate(p2x, p3x, t); + final double cd1y = interpolate(p2y, p3y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); + final double bcd1x = interpolate(bc1x, cd1x, t); + final double bcd1y = interpolate(bc1y, cd1y, t); + final double abcd1x = interpolate(abc1x, bcd1x, t); + final double abcd1y = interpolate(abc1y, bcd1y, t); // Return left side of curve. outPts[outIndex++] = p0x; @@ -134,7 +138,7 @@ void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, // // Options are Newton Raphson (quadratic convergence with typically // 3 iterations or bisection with 16 iterations. -double? _chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { +double? chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { // Translate curve points relative to y. final double ycrv0 = _buffer[1 + bufferStartPos] - y; final double ycrv1 = _buffer[3 + bufferStartPos] - y; @@ -180,7 +184,7 @@ double? _chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { return (tNeg + tPos) / 2; } -double _evalCubicPts(double c0, double c1, double c2, double c3, double t) { +double evalCubicPts(double c0, double c1, double c2, double c3, double t) { double A = c3 + 3 * (c1 - c2) - c0; double B = 3 * (c2 - c1 - c1 + c0); double C = 3 * (c1 - c0); @@ -189,7 +193,7 @@ double _evalCubicPts(double c0, double c1, double c2, double c3, double t) { } // Reusable class to compute bounds without object allocation. -class _CubicBounds { +class CubicBounds { double minX = 0.0; double maxX = 0.0; double minY = 0.0; @@ -337,7 +341,7 @@ class _CubicBounds { } /// Chops cubic spline at startT and stopT, writes result to buffer. -void _chopCubicBetweenT( +void chopCubicBetweenT( List points, double startT, double stopT, Float32List buffer) { assert(startT != 0 || stopT != 0); final double p3y = points[7]; @@ -352,18 +356,18 @@ void _chopCubicBetweenT( final bool chopStart = startT != 0; final double t = chopStart ? startT : stopT; - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double cd1x = _interpolate(p2x, p3x, t); - final double cd1y = _interpolate(p2y, p3y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); - final double bcd1x = _interpolate(bc1x, cd1x, t); - final double bcd1y = _interpolate(bc1y, cd1y, t); - final double abcd1x = _interpolate(abc1x, bcd1x, t); - final double abcd1y = _interpolate(abc1y, bcd1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double cd1x = interpolate(p2x, p3x, t); + final double cd1y = interpolate(p2y, p3y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); + final double bcd1x = interpolate(bc1x, cd1x, t); + final double bcd1y = interpolate(bc1y, cd1y, t); + final double abcd1x = interpolate(abc1x, bcd1x, t); + final double abcd1y = interpolate(abc1y, bcd1y, t); if (!chopStart) { // Return left side of curve. buffer[0] = p0x; @@ -391,18 +395,18 @@ void _chopCubicBetweenT( // We chopped at startT, now the right hand side of curve is at // abcd1, bcd1, cd1, p3x, p3y. Chop this part using endT; final double endT = (stopT - startT) / (1 - startT); - final double ab2x = _interpolate(abcd1x, bcd1x, endT); - final double ab2y = _interpolate(abcd1y, bcd1y, endT); - final double bc2x = _interpolate(bcd1x, cd1x, endT); - final double bc2y = _interpolate(bcd1y, cd1y, endT); - final double cd2x = _interpolate(cd1x, p3x, endT); - final double cd2y = _interpolate(cd1y, p3y, endT); - final double abc2x = _interpolate(ab2x, bc2x, endT); - final double abc2y = _interpolate(ab2y, bc2y, endT); - final double bcd2x = _interpolate(bc2x, cd2x, endT); - final double bcd2y = _interpolate(bc2y, cd2y, endT); - final double abcd2x = _interpolate(abc2x, bcd2x, endT); - final double abcd2y = _interpolate(abc2y, bcd2y, endT); + final double ab2x = interpolate(abcd1x, bcd1x, endT); + final double ab2y = interpolate(abcd1y, bcd1y, endT); + final double bc2x = interpolate(bcd1x, cd1x, endT); + final double bc2y = interpolate(bcd1y, cd1y, endT); + final double cd2x = interpolate(cd1x, p3x, endT); + final double cd2y = interpolate(cd1y, p3y, endT); + final double abc2x = interpolate(ab2x, bc2x, endT); + final double abc2y = interpolate(ab2y, bc2y, endT); + final double bcd2x = interpolate(bc2x, cd2x, endT); + final double bcd2y = interpolate(bc2y, cd2y, endT); + final double abcd2x = interpolate(abc2x, bcd2x, endT); + final double abcd2y = interpolate(abc2y, bcd2y, endT); buffer[0] = abcd1x; buffer[1] = abcd1y; buffer[2] = ab2x; diff --git a/lib/web_ui/lib/src/engine/html/path/path.dart b/lib/web_ui/lib/src/engine/html/path/path.dart index 09a61ab442a4f..116aeab41358b 100644 --- a/lib/web_ui/lib/src/engine/html/path/path.dart +++ b/lib/web_ui/lib/src/engine/html/path/path.dart @@ -64,7 +64,7 @@ class SurfacePath implements ui.Path { } SurfacePath._shallowCopy(SurfacePath source) - : pathRef = PathRef._shallowCopy(source.pathRef) { + : pathRef = PathRef.shallowCopy(source.pathRef) { _copyFields(source); } @@ -453,7 +453,7 @@ class SurfacePath implements ui.Path { // e.g. canvas.drawArc(0, 359.99, ...) // -vs- canvas.drawArc(0, 359.9, ...) // Detect this edge case, and tweak the stop vector. - if (_nearlyEqual(cosStart, cosStop) && _nearlyEqual(sinStart, sinStop)) { + if (SPath.nearlyEqual(cosStart, cosStop) && SPath.nearlyEqual(sinStart, sinStop)) { final double sweep = sweepAngle.abs() * 180.0 / math.pi; if (sweep <= 360 && sweep > 359) { // Use tiny angle (in radians) to tweak. @@ -583,8 +583,8 @@ class SurfacePath implements ui.Path { assert(unscaledLength > SPath.scalarNearlyZero); offCurveX /= cosThetaOver2 * unscaledLength; offCurveY /= cosThetaOver2 * unscaledLength; - if (!_nearlyEqual(offCurveX, lastQuadrantPoint.dx) || - !_nearlyEqual(offCurveY, lastQuadrantPoint.dy)) { + if (!SPath.nearlyEqual(offCurveX, lastQuadrantPoint.dx) || + !SPath.nearlyEqual(offCurveY, lastQuadrantPoint.dy)) { conics.add(Conic(lastQuadrantPoint.dx, lastQuadrantPoint.dy, offCurveX, offCurveY, finalPx, finalPy, cosThetaOver2)); ++conicCount; @@ -634,7 +634,8 @@ class SurfacePath implements ui.Path { final ui.Offset lastPoint = pathRef.atPoint(pointCount - 1); final double lastPointX = lastPoint.dx; final double lastPointY = lastPoint.dy; - if (!_nearlyEqual(px, lastPointX) || !_nearlyEqual(py, lastPointY)) { + if (!SPath.nearlyEqual(px, lastPointX) + || SPath.nearlyEqual(py, lastPointY)) { lineTo(px, py); } } @@ -796,16 +797,16 @@ class SurfacePath implements ui.Path { // to start outside their marks. A round rect may lose convexity as a // result. If the input values are on integers, place the conic on // integers as well. - bool expectIntegers = _nearlyEqual((math.pi / 2 - thetaWidth.abs()), 0) && - _isInteger(rx) && - _isInteger(ry) && - _isInteger(x) && - _isInteger(y); + bool expectIntegers = SPath.nearlyEqual((math.pi / 2 - thetaWidth.abs()), 0) && + SPath.isInteger(rx) && + SPath.isInteger(ry) && + SPath.isInteger(x) && + SPath.isInteger(y); for (int i = 0; i < segments; i++) { final double endTheta = startTheta + thetaWidth; - final double sinEndTheta = _snapToZero(math.sin(endTheta)); - final double cosEndTheta = _snapToZero(math.cos(endTheta)); + final double sinEndTheta = SPath.snapToZero(math.sin(endTheta)); + final double cosEndTheta = SPath.snapToZero(math.cos(endTheta)); double unitPts1x = cosEndTheta + centerPointX; double unitPts1y = sinEndTheta + centerPointY; double unitPts0x = unitPts1x + t * sinEndTheta; @@ -943,7 +944,7 @@ class SurfacePath implements ui.Path { final double startOver90 = startAngle / (math.pi / 2.0); final double startOver90I = (startOver90 + 0.5).floorToDouble(); final double error = startOver90 - startOver90I; - if (_nearlyEqual(error, 0)) { + if (SPath.nearlyEqual(error, 0)) { // Index 1 is at startAngle == 0. double startIndex = startOver90I + 1.0 % 4.0; startIndex = startIndex < 0 ? startIndex + 4.0 : startIndex; @@ -1001,7 +1002,7 @@ class SurfacePath implements ui.Path { if (rrect.isRect || rrect.isEmpty) { // degenerate(rect) => radii points are collapsing. addRectWithDirection(bounds, direction, (startIndex + 1) ~/ 2); - } else if (_isRRectOval(rrect)) { + } else if (isRRectOval(rrect)) { // degenerate(oval) => line points are collapsing. _addOval(bounds, direction, startIndex ~/ 2); } else { @@ -1077,7 +1078,7 @@ class SurfacePath implements ui.Path { // we are not extending. if (mode == SPathAddPathMode.kAppend && (matrix4 == null || _isSimple2dTransform(matrix4))) { - pathRef._append(source.pathRef); + pathRef.append(source.pathRef); } else { bool firstVerb = true; final PathRefIterator iter = PathRefIterator(source.pathRef); @@ -1251,13 +1252,13 @@ class SurfacePath implements ui.Path { if (tangents.length > oldCount) { int last = tangents.length - 1; final ui.Offset tangent = tangents[last]; - if (_nearlyEqual(_lengthSquaredOffset(tangent), 0)) { + if (SPath.nearlyEqual(lengthSquaredOffset(tangent), 0)) { tangents.remove(last); } else { for (int index = 0; index < last; ++index) { final ui.Offset test = tangents[index]; double crossProduct = test.dx * tangent.dy - test.dy * tangent.dx; - if (_nearlyEqual(crossProduct, 0) && + if (SPath.nearlyEqual(crossProduct, 0) && SPath.scalarSignedAsInt(tangent.dx * test.dx) <= 0 && SPath.scalarSignedAsInt(tangent.dy * test.dy) <= 0) { ui.Offset offset = tangents.removeAt(last); @@ -1454,9 +1455,9 @@ class SurfacePath implements ui.Path { final PathRefIterator iter = PathRefIterator(pathRef); final Float32List points = pathRef.points; int verb; - _CubicBounds? cubicBounds; - _QuadBounds? quadBounds; - _ConicBounds? conicBounds; + CubicBounds? cubicBounds; + QuadBounds? quadBounds; + ConicBounds? conicBounds; while ((verb = iter.nextIndex()) != SPath.kDoneVerb) { final int pIndex = iter.iterIndex; switch (verb) { @@ -1469,7 +1470,7 @@ class SurfacePath implements ui.Path { minY = maxY = points[pIndex + 3]; break; case SPath.kQuadVerb: - quadBounds ??= _QuadBounds(); + quadBounds ??= QuadBounds(); quadBounds.calculateBounds(points, pIndex); minX = quadBounds.minX; minY = quadBounds.minY; @@ -1477,7 +1478,7 @@ class SurfacePath implements ui.Path { maxY = quadBounds.maxY; break; case SPath.kConicVerb: - conicBounds ??= _ConicBounds(); + conicBounds ??= ConicBounds(); conicBounds.calculateBounds(points, iter.conicWeight, pIndex); minX = conicBounds.minX; minY = conicBounds.minY; @@ -1485,7 +1486,7 @@ class SurfacePath implements ui.Path { maxY = conicBounds.maxY; break; case SPath.kCubicVerb: - cubicBounds ??= _CubicBounds(); + cubicBounds ??= CubicBounds(); cubicBounds.calculateBounds(points, pIndex); minX = cubicBounds.minX; minY = cubicBounds.minY; @@ -1520,7 +1521,7 @@ class SurfacePath implements ui.Path { /// as if they had been closed, even if they were not explicitly closed. @override SurfacePathMetrics computeMetrics({bool forceClosed = false}) { - return SurfacePathMetrics._(this, forceClosed); + return SurfacePathMetrics(this.pathRef, forceClosed); } /// Detects if path is rounded rectangle. @@ -1650,11 +1651,3 @@ bool _isSimple2dTransform(Float32List m) => m[2] == 0.0; // m[1] - 2D rotation is simple // m[0] - scale x is simple - -double _lengthSquaredOffset(ui.Offset offset) { - final double dx = offset.dx; - final double dy = offset.dy; - return dx * dx + dy * dy; -} - -double _lengthSquared(double dx, double dy) => dx * dx + dy * dy; diff --git a/lib/web_ui/lib/src/engine/html/path/path_iterator.dart b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart new file mode 100644 index 0000000000000..0a814703f7014 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart @@ -0,0 +1,220 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'path_ref.dart'; +import 'path_utils.dart'; + +// Iterates through path including generating closing segments. +class PathIterator { + PathIterator(this.pathRef, bool forceClose) + : _forceClose = forceClose, + _verbCount = pathRef.countVerbs() { + _pointIndex = 0; + if (!pathRef.isFinite) { + // Don't allow iteration through non-finite points, prepare to return + // done verb. + _verbIndex = pathRef.countVerbs(); + } + } + + final PathRef pathRef; + final bool _forceClose; + final int _verbCount; + + bool _needClose = false; + int _segmentState = SPathSegmentState.kEmptyContour; + int _conicWeightIndex = -1; + double _lastPointX = 0; + double _lastPointY = 0; + double _moveToX = 0; + double _moveToY = 0; + int _verbIndex = 0; + int _pointIndex = 0; + + int get pathVerbIndex => _verbIndex; + int get conicWeightIndex => _conicWeightIndex; + + /// Maximum buffer size required for points in [next] calls. + static const int kMaxBufferSize = 8; + + /// Returns true if first contour on path is closed. + bool isClosedContour() { + if (_verbCount == 0 || _verbIndex == _verbCount) { + return false; + } + if (_forceClose) { + return true; + } + int verbIndex = 0; + // Skip starting moveTo. + if (pathRef.atVerb(verbIndex) == SPath.kMoveVerb) { + ++verbIndex; + } + while (verbIndex < _verbCount) { + int verb = pathRef.atVerb(verbIndex++); + if (SPath.kMoveVerb == verb) { + break; + } + if (SPath.kCloseVerb == verb) { + return true; + } + } + return false; + } + + int _autoClose(Float32List outPts) { + if (_lastPointX != _moveToX || _lastPointY != _moveToY) { + // Handle special case where comparison above will return true for + // NaN != NaN although it should be false. + if (_lastPointX.isNaN || + _lastPointY.isNaN || + _moveToX.isNaN || + _moveToY.isNaN) { + return SPath.kCloseVerb; + } + outPts[0] = _lastPointX; + outPts[1] = _lastPointY; + outPts[2] = _moveToX; + outPts[3] = _moveToY; + _lastPointX = _moveToX; + _lastPointY = _moveToY; + return SPath.kLineVerb; + } else { + outPts[0] = _moveToX; + outPts[1] = _moveToY; + return SPath.kCloseVerb; + } + } + + // Returns true if caller should use moveTo, false if last point of + // previous primitive. + ui.Offset _constructMoveTo() { + if (_segmentState == SPathSegmentState.kAfterMove) { + // Set the first return point to move point. + _segmentState = SPathSegmentState.kAfterPrimitive; + return ui.Offset(_moveToX, _moveToY); + } + return ui.Offset( + pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); + } + + int peek() { + if (_verbIndex < pathRef.countVerbs()) { + return pathRef.atVerb(_verbIndex); + } + if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { + return (_lastPointX != _moveToX || _lastPointY != _moveToY) + ? SPath.kLineVerb + : SPath.kCloseVerb; + } + return SPath.kDoneVerb; + } + + // Returns next verb and reads associated points into [outPts]. + int next(Float32List outPts) { + if (_verbIndex == pathRef.countVerbs()) { + // Close the curve if requested and if there is some curve to close + if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { + if (SPath.kLineVerb == _autoClose(outPts)) { + return SPath.kLineVerb; + } + _needClose = false; + return SPath.kCloseVerb; + } + return SPath.kDoneVerb; + } + int verb = pathRef.atVerb(_verbIndex++); + switch (verb) { + case SPath.kMoveVerb: + if (_needClose) { + // Move back one verb. + _verbIndex--; + final int autoVerb = _autoClose(outPts); + if (autoVerb == SPath.kCloseVerb) { + _needClose = false; + } + return autoVerb; + } + if (_verbIndex == _verbCount) { + return SPath.kDoneVerb; + } + double offsetX = pathRef.points[_pointIndex++]; + double offsetY = pathRef.points[_pointIndex++]; + _moveToX = offsetX; + _moveToY = offsetY; + outPts[0] = offsetX; + outPts[1] = offsetY; + _segmentState = SPathSegmentState.kAfterMove; + _lastPointX = _moveToX; + _lastPointY = _moveToY; + _needClose = _forceClose; + break; + case SPath.kLineVerb: + final ui.Offset start = _constructMoveTo(); + double offsetX = pathRef.points[_pointIndex++]; + double offsetY = pathRef.points[_pointIndex++]; + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = offsetX; + outPts[3] = offsetY; + _lastPointX = offsetX; + _lastPointY = offsetY; + break; + case SPath.kConicVerb: + _conicWeightIndex++; + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; + break; + case SPath.kQuadVerb: + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; + break; + case SPath.kCubicVerb: + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + outPts[4] = pathRef.points[_pointIndex++]; + outPts[5] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[6] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[7] = pathRef.points[_pointIndex++]; + break; + case SPath.kCloseVerb: + verb = _autoClose(outPts); + if (verb == SPath.kLineVerb) { + // Move back one verb since we constructed line for this close verb. + _verbIndex--; + } else { + _needClose = false; + _segmentState = SPathSegmentState.kEmptyContour; + } + _lastPointX = _moveToX; + _lastPointY = _moveToY; + break; + case SPath.kDoneVerb: + assert(_verbIndex == pathRef.countVerbs()); + break; + default: + throw FormatException('Unsupport Path verb $verb'); + } + return verb; + } + + double get conicWeight => pathRef.atWeight(_conicWeightIndex); +} diff --git a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart index 5b0eb4e2fe2de..4c624f3dbd970 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart @@ -2,7 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:collection' show IterableBase; +import 'dart:typed_data'; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_iterator.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; const double kEpsilon = 0.000000001; @@ -22,7 +32,7 @@ const double kEpsilon = 0.000000001; /// are only valid until the next one is obtained. class SurfacePathMetrics extends IterableBase implements ui.PathMetrics { - SurfacePathMetrics._(SurfacePath path, bool forceClosed) + SurfacePathMetrics(PathRef path, bool forceClosed) : _iterator = SurfacePathMetricIterator._(_SurfacePathMeasure(path, forceClosed)); @@ -41,10 +51,10 @@ class _SurfacePathMeasure { : // nextContour will increment this to the zero based index. _currentContourIndex = -1, - _pathIterator = PathIterator(_path.pathRef, forceClosed); + _pathIterator = PathIterator(_path, forceClosed); final double resScale; - final SurfacePath _path; + final PathRef _path; PathIterator _pathIterator; final List<_PathContourMeasure> _contours = []; @@ -103,11 +113,11 @@ class _SurfacePathMeasure { // calling `_moveNext` - `_moveNext` should be called after the first // iteration is done instead of before. bool _nativeNextContour() { - if (_verbIterIndex == _path.pathRef.countVerbs()) { + if (_verbIterIndex == _path.countVerbs()) { return false; } _PathContourMeasure measure = - _PathContourMeasure(_path.pathRef, _pathIterator, forceClosed); + _PathContourMeasure(_path, _pathIterator, forceClosed); _verbIterIndex = measure.verbEndIndex; _contours.add(measure); return true; @@ -253,7 +263,7 @@ class _PathContourMeasure { path.lineTo(toX, toY); break; case SPath.kCubicVerb: - _chopCubicBetweenT(points, startT, stopT, _buffer); + chopCubicBetweenT(points, startT, stopT, _buffer); path.cubicTo(_buffer[2], _buffer[3], _buffer[4], _buffer[5], _buffer[6], _buffer[7]); break; @@ -351,13 +361,13 @@ class _PathContourMeasure { break; case SPath.kCloseVerb: _contourLength = distance; - return iter._verbIndex; + return iter.pathVerbIndex; default: break; } } while (verb != SPath.kDoneVerb); _contourLength = distance; - return iter._verbIndex; + return iter.pathVerbIndex; } static bool _tspanBigEnough(int tSpan) => (tSpan >> 10) != 0; @@ -631,8 +641,8 @@ class _PathSegment { _SurfaceTangent tangentForQuadAt(double t, double x0, double y0, double x1, double y1, double x2, double y2) { assert(t >= 0 && t <= 1); - final _SkQuadCoefficients _quadEval = - _SkQuadCoefficients(x0, y0, x1, y1, x2, y2); + final SkQuadCoefficients _quadEval = + SkQuadCoefficients(x0, y0, x1, y1, x2, y2); final ui.Offset pos = ui.Offset(_quadEval.evalX(t), _quadEval.evalY(t)); // Derivative of quad curve is 2(b - a + (a - 2b + c)t). // If control point is at start or end point, this yields 0 for t = 0 and @@ -681,23 +691,6 @@ class _PathSegment { } } -/// Evaluates A * t^2 + B * t + C = 0 for quadratic curve. -class _SkQuadCoefficients { - _SkQuadCoefficients( - double x0, double y0, double x1, double y1, double x2, double y2) - : cx = x0, - cy = y0, - bx = 2 * (x1 - x0), - by = 2 * (y1 - y0), - ax = x2 - (2 * x1) + x0, - ay = y2 - (2 * y1) + y0; - final double ax, ay, bx, by, cx, cy; - - double evalX(double t) => (ax * t + bx) * t + cx; - - double evalY(double t) => (ay * t + by) * t + cy; -} - // Evaluates A * t^3 + B * t^2 + Ct + D = 0 for cubic curve. class _SkCubicCoefficients { final double ax, ay, bx, by, cx, cy, dx, dy; @@ -732,12 +725,12 @@ void _chopQuadBetweenT( final bool chopStart = startT != 0; final double t = chopStart ? startT : stopT; - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); if (!chopStart) { // Return left side of curve. buffer[0] = p0x; @@ -761,12 +754,12 @@ void _chopQuadBetweenT( // We chopped at startT, now the right hand side of curve is at // abc1x, abc1y, bc1x, bc1y, p2x, p2y final double endT = (stopT - startT) / (1 - startT); - final double ab2x = _interpolate(abc1x, bc1x, endT); - final double ab2y = _interpolate(abc1y, bc1y, endT); - final double bc2x = _interpolate(bc1x, p2x, endT); - final double bc2y = _interpolate(bc1y, p2y, endT); - final double abc2x = _interpolate(ab2x, bc2x, endT); - final double abc2y = _interpolate(ab2y, bc2y, endT); + final double ab2x = interpolate(abc1x, bc1x, endT); + final double ab2y = interpolate(abc1y, bc1y, endT); + final double bc2x = interpolate(bc1x, p2x, endT); + final double bc2y = interpolate(bc1y, p2y, endT); + final double abc2x = interpolate(ab2x, bc2x, endT); + final double abc2y = interpolate(ab2y, bc2y, endT); buffer[0] = abc1x; buffer[1] = abc1y; diff --git a/lib/web_ui/lib/src/engine/html/path/path_ref.dart b/lib/web_ui/lib/src/engine/html/path/path_ref.dart index 0dc9ca9d8985b..77fe3605a005f 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_ref.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_ref.dart @@ -2,7 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; +import 'dart:js_util' as js_util; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; +import 'path_utils.dart'; +import '../../util.dart'; /// Stores the path verbs, points and conic weights. /// @@ -83,7 +89,7 @@ class PathRef { /// points,verbs and weights arrays. If original path is mutated by adding /// more verbs, this copy only returns path at the time of copy and shares /// typed arrays of original path. - PathRef._shallowCopy(PathRef ref) + PathRef.shallowCopy(PathRef ref) : _fPoints = ref._fPoints, _fVerbs = ref._fVerbs { _fVerbsCapacity = ref._fVerbsCapacity; @@ -126,6 +132,10 @@ class PathRef { return ui.Offset(_fPoints[index * 2], _fPoints[index * 2 + 1]); } + double pointXAt(int index) => _fPoints[index * 2]; + + double pointYAt(int index) => _fPoints[index * 2 + 1]; + double atWeight(int index) { return _conicWeights![index]; } @@ -266,11 +276,11 @@ class PathRef { dy = vector1_0y.abs(); } if (assertionsEnabled) { - final int checkCornerIndex = _nearlyEqual(controlPx, bounds.left) - ? (_nearlyEqual(controlPy, bounds.top) + final int checkCornerIndex = SPath.nearlyEqual(controlPx, bounds.left) + ? (SPath.nearlyEqual(controlPy, bounds.top) ? _Corner.kUpperLeft : _Corner.kLowerLeft) - : (_nearlyEqual(controlPy, bounds.top) + : (SPath.nearlyEqual(controlPy, bounds.top) ? _Corner.kUpperRight : _Corner.kLowerRight); assert(checkCornerIndex == cornerIndex); @@ -465,7 +475,7 @@ class PathRef { _conicWeightsLength = newLength; } - void _append(PathRef source) { + void append(PathRef source) { final int pointCount = source.countPoints(); final int curLength = _fPointsLength; final int newPointCount = curLength + pointCount; diff --git a/lib/web_ui/lib/src/engine/html/path/path_utils.dart b/lib/web_ui/lib/src/engine/html/path/path_utils.dart index b3c12170935b6..52edc02d041b3 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_utils.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_utils.dart @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; +import 'path_ref.dart'; /// Mask used to keep track of types of verbs used in a path segment. class SPathSegmentMask { @@ -55,6 +58,14 @@ class SPath { static int scalarSignedAsInt(double x) { return x < 0 ? -1 : ((x > 0) ? 1 : 0); } + + // Snaps a value to zero if almost zero (within tolerance). + static double snapToZero(double value) => nearlyEqual(value, 0.0) ? 0.0 : value; + + static bool nearlyEqual(double value1, double value2) => + (value1 - value2).abs() < SPath.scalarNearlyZero; + + static bool isInteger(double value) => value.floor() == value; } class SPathAddPathMode { @@ -98,11 +109,11 @@ class SPathSegmentState { /// Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) /// x1 = Q / A /// x2 = C / Q -class _QuadRoots { +class QuadRoots { double? root0; double? root1; - _QuadRoots(); + QuadRoots(); /// Returns roots as list. List get roots => (root0 == null) @@ -112,7 +123,7 @@ class _QuadRoots { int findRoots(double a, double b, double c) { int rootCount = 0; if (a == 0) { - root0 = _validUnitDivide(-c, b); + root0 = validUnitDivide(-c, b); return root0 == null ? 0 : 1; } @@ -126,12 +137,12 @@ class _QuadRoots { } double q = (b < 0) ? -(b - dr) / 2 : -(b + dr) / 2; - double? res = _validUnitDivide(q, a); + double? res = validUnitDivide(q, a); if (res != null) { root0 = res; ++rootCount; } - res = _validUnitDivide(c, q); + res = validUnitDivide(c, q); if (res != null) { if (rootCount == 0) { root0 = res; @@ -154,7 +165,7 @@ class _QuadRoots { } } -double? _validUnitDivide(double numer, double denom) { +double? validUnitDivide(double numer, double denom) { if (numer < 0) { numer = -numer; denom = -denom; @@ -173,15 +184,7 @@ double? _validUnitDivide(double numer, double denom) { return r; } -// Snaps a value to zero if almost zero (within tolerance). -double _snapToZero(double value) => _nearlyEqual(value, 0.0) ? 0.0 : value; - -bool _nearlyEqual(double value1, double value2) => - (value1 - value2).abs() < SPath.scalarNearlyZero; - -bool _isInteger(double value) => value.floor() == value; - -bool _isRRectOval(ui.RRect rrect) { +bool isRRectOval(ui.RRect rrect) { if ((rrect.tlRadiusX + rrect.trRadiusX) != rrect.width) { return false; } @@ -206,10 +209,10 @@ double polyEval4(double A, double B, double C, double D, double t) => // Interpolate between two doubles (Not using lerpDouble here since it null // checks and treats values as 0). -double _interpolate(double startValue, double endValue, double t) => +double interpolate(double startValue, double endValue, double t) => (startValue * (1 - t)) + endValue * t; -double _dotProduct(double x0, double y0, double x1, double y1) { +double dotProduct(double x0, double y0, double x1, double y1) { return x0 * x1 + y0 * y1; } @@ -305,18 +308,18 @@ class Convexicator { final double largest = math.max( math.max(curVecX, math.max(curVecY, math.max(lastX, lastY))), -smallest); - if (_nearlyEqual(largest, largest + cross)) { + if (SPath.nearlyEqual(largest, largest + cross)) { final double nearlyZeroSquared = SPath.scalarNearlyZero * SPath.scalarNearlyZero; - if (_nearlyEqual(_lengthSquared(lastX, lastY), nearlyZeroSquared) || - _nearlyEqual(_lengthSquared(curVecX, curVecY), nearlyZeroSquared)) { + if (SPath.nearlyEqual(lengthSquared(lastX, lastY), nearlyZeroSquared) || + SPath.nearlyEqual(lengthSquared(curVecX, curVecY), nearlyZeroSquared)) { // Length of either vector is smaller than tolerance to be able // to compute direction. return DirChange.kUnknown; } // The vectors are parallel, sign of dot product gives us direction. // cosine is positive for straight -90 < Theta < 90 - return _dotProduct(lastX, lastY, curVecX, curVecY) < 0 + return dotProduct(lastX, lastY, curVecX, curVecY) < 0 ? DirChange.kBackwards : DirChange.kStraight; } @@ -371,10 +374,10 @@ class Convexicator { int lastSy = kValueNeverReturnedBySign; for (int outerLoop = 0; outerLoop < 2; ++outerLoop) { while (pointIndex != lastPointIndex) { - double vecX = pathRef._fPoints[pointIndex * 2] - - pathRef._fPoints[currentPoint * 2]; - double vecY = pathRef._fPoints[pointIndex * 2 + 1] - - pathRef._fPoints[currentPoint * 2 + 1]; + double vecX = pathRef.pointXAt(pointIndex) - + pathRef.pointXAt(currentPoint); + double vecY = pathRef.pointYAt(pointIndex) - + pathRef.pointYAt(currentPoint); if (!(vecX == 0 && vecY == 0)) { // Give up if vector construction failed. // give up if vector construction failed @@ -410,3 +413,28 @@ enum DirChange { kBackwards, // if double back, allow simple lines to be convex kInvalid } + +double lengthSquaredOffset(ui.Offset offset) { + final double dx = offset.dx; + final double dy = offset.dy; + return dx * dx + dy * dy; +} + +double lengthSquared(double dx, double dy) => dx * dx + dy * dy; + +/// Evaluates A * t^2 + B * t + C = 0 for quadratic curve. +class SkQuadCoefficients { + SkQuadCoefficients( + double x0, double y0, double x1, double y1, double x2, double y2) + : cx = x0, + cy = y0, + bx = 2 * (x1 - x0), + by = 2 * (y1 - y0), + ax = x2 - (2 * x1) + x0, + ay = y2 - (2 * y1) + y0; + final double ax, ay, bx, by, cx, cy; + + double evalX(double t) => (ax * t + bx) * t + cx; + + double evalY(double t) => (ay * t + by) * t + cy; +} diff --git a/lib/web_ui/lib/src/engine/html/path/path_windings.dart b/lib/web_ui/lib/src/engine/html/path/path_windings.dart index 0b1a69a3e79dd..9b2b4689ea740 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_windings.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_windings.dart @@ -39,7 +39,7 @@ class PathWinding { _computeQuadWinding(); break; case SPath.kConicVerb: - _computeConicWinding(pathRef._conicWeights![iter._conicWeightIndex]); + _computeConicWinding(pathRef.conicWeights![iter.conicWeightIndex]); break; case SPath.kCubicVerb: _computeCubicWinding(); @@ -143,7 +143,7 @@ class PathWinding { return 0; } - _QuadRoots quadRoots = _QuadRoots(); + QuadRoots quadRoots = QuadRoots(); final int n = quadRoots.findRoots( startY - 2 * y1 + endY, 2 * (y1 - startY), startY - y); assert(n <= 1); @@ -158,7 +158,7 @@ class PathWinding { final double B = 2 * (x1 - C); xt = polyEval(A, B, C, t); } - if (_nearlyEqual(xt, x)) { + if (SPath.nearlyEqual(xt, x)) { if (x != x2 || y != endY) { // don't test end points; they're start points _onCurveCount += 1; @@ -177,7 +177,7 @@ class PathWinding { final double y1 = buffer[3]; final double x2 = buffer[4]; final double y2 = buffer[5]; - double? tValueAtExtrema = _validUnitDivide(y0 - y1, y0 - y1 - y1 + y2); + double? tValueAtExtrema = validUnitDivide(y0 - y1, y0 - y1 - y1 + y2); if (tValueAtExtrema != null) { // Chop quad at t value by interpolating along p0-p1 and p1-p2. double p01x = x0 + (tValueAtExtrema * (x1 - x0)); @@ -259,7 +259,7 @@ class PathWinding { // B = b*w - w * yCept + yCept - a B -= C; C -= y; - final _QuadRoots quadRoots = _QuadRoots(); + final QuadRoots quadRoots = QuadRoots(); int n = quadRoots.findRoots(A, 2 * B, C); assert(n <= 1); double xt; @@ -271,10 +271,10 @@ class PathWinding { } else { final double root = quadRoots.root0!; xt = - _conicEvalNumerator(conic.p0x, conic.p1x, conic.p2x, conic.fW, root) / - _conicEvalDenominator(conic.fW, root); + Conic.evalNumerator(conic.p0x, conic.p1x, conic.p2x, conic.fW, root) / + Conic.evalDenominator(conic.fW, root); } - if (_nearlyEqual(xt, x)) { + if (SPath.nearlyEqual(xt, x)) { if (x != conic.p2x || y != conic.p2y) { // don't test end points; they're start points _onCurveCount += 1; @@ -285,7 +285,7 @@ class PathWinding { } void _computeCubicWinding() { - int n = _chopCubicAtYExtrema(_buffer, _buffer); + int n = chopCubicAtYExtrema(_buffer, _buffer); for (int i = 0; i <= n; ++i) { _windingMonoCubic(i * 3 * 2); } @@ -334,12 +334,12 @@ class PathWinding { return; } // Compute the actual x(t) value. - double? t = _chopMonoAtY(_buffer, bufferStartPos, y); + double? t = chopMonoAtY(_buffer, bufferStartPos, y); if (t == null) { return; } - double xt = _evalCubicPts(px0, px1, px2, px3, t); - if (_nearlyEqual(xt, x)) { + double xt = evalCubicPts(px0, px1, px2, px3, t); + if (SPath.nearlyEqual(xt, x)) { if (x != px3 || y != py3) { // don't test end points; they're start points _onCurveCount += 1; @@ -349,210 +349,3 @@ class PathWinding { _w += xt < x ? dir : 0; } } - -// Iterates through path including generating closing segments. -class PathIterator { - PathIterator(this.pathRef, bool forceClose) - : _forceClose = forceClose, - _verbCount = pathRef.countVerbs() { - _pointIndex = 0; - if (!pathRef.isFinite) { - // Don't allow iteration through non-finite points, prepare to return - // done verb. - _verbIndex = pathRef.countVerbs(); - } - } - - final PathRef pathRef; - final bool _forceClose; - final int _verbCount; - - bool _needClose = false; - int _segmentState = SPathSegmentState.kEmptyContour; - int _conicWeightIndex = -1; - double _lastPointX = 0; - double _lastPointY = 0; - double _moveToX = 0; - double _moveToY = 0; - int _verbIndex = 0; - int _pointIndex = 0; - - /// Maximum buffer size required for points in [next] calls. - static const int kMaxBufferSize = 8; - - /// Returns true if first contour on path is closed. - bool isClosedContour() { - if (_verbCount == 0 || _verbIndex == _verbCount) { - return false; - } - if (_forceClose) { - return true; - } - int verbIndex = 0; - // Skip starting moveTo. - if (pathRef.atVerb(verbIndex) == SPath.kMoveVerb) { - ++verbIndex; - } - while (verbIndex < _verbCount) { - int verb = pathRef.atVerb(verbIndex++); - if (SPath.kMoveVerb == verb) { - break; - } - if (SPath.kCloseVerb == verb) { - return true; - } - } - return false; - } - - int _autoClose(Float32List outPts) { - if (_lastPointX != _moveToX || _lastPointY != _moveToY) { - // Handle special case where comparison above will return true for - // NaN != NaN although it should be false. - if (_lastPointX.isNaN || - _lastPointY.isNaN || - _moveToX.isNaN || - _moveToY.isNaN) { - return SPath.kCloseVerb; - } - outPts[0] = _lastPointX; - outPts[1] = _lastPointY; - outPts[2] = _moveToX; - outPts[3] = _moveToY; - _lastPointX = _moveToX; - _lastPointY = _moveToY; - return SPath.kLineVerb; - } else { - outPts[0] = _moveToX; - outPts[1] = _moveToY; - return SPath.kCloseVerb; - } - } - - // Returns true if caller should use moveTo, false if last point of - // previous primitive. - ui.Offset _constructMoveTo() { - if (_segmentState == SPathSegmentState.kAfterMove) { - // Set the first return point to move point. - _segmentState = SPathSegmentState.kAfterPrimitive; - return ui.Offset(_moveToX, _moveToY); - } - return ui.Offset( - pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); - } - - int peek() { - if (_verbIndex < pathRef.countVerbs()) { - return pathRef._fVerbs[_verbIndex]; - } - if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { - return (_lastPointX != _moveToX || _lastPointY != _moveToY) - ? SPath.kLineVerb - : SPath.kCloseVerb; - } - return SPath.kDoneVerb; - } - - // Returns next verb and reads associated points into [outPts]. - int next(Float32List outPts) { - if (_verbIndex == pathRef.countVerbs()) { - // Close the curve if requested and if there is some curve to close - if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { - if (SPath.kLineVerb == _autoClose(outPts)) { - return SPath.kLineVerb; - } - _needClose = false; - return SPath.kCloseVerb; - } - return SPath.kDoneVerb; - } - int verb = pathRef._fVerbs[_verbIndex++]; - switch (verb) { - case SPath.kMoveVerb: - if (_needClose) { - // Move back one verb. - _verbIndex--; - final int autoVerb = _autoClose(outPts); - if (autoVerb == SPath.kCloseVerb) { - _needClose = false; - } - return autoVerb; - } - if (_verbIndex == _verbCount) { - return SPath.kDoneVerb; - } - double offsetX = pathRef.points[_pointIndex++]; - double offsetY = pathRef.points[_pointIndex++]; - _moveToX = offsetX; - _moveToY = offsetY; - outPts[0] = offsetX; - outPts[1] = offsetY; - _segmentState = SPathSegmentState.kAfterMove; - _lastPointX = _moveToX; - _lastPointY = _moveToY; - _needClose = _forceClose; - break; - case SPath.kLineVerb: - final ui.Offset start = _constructMoveTo(); - double offsetX = pathRef.points[_pointIndex++]; - double offsetY = pathRef.points[_pointIndex++]; - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = offsetX; - outPts[3] = offsetY; - _lastPointX = offsetX; - _lastPointY = offsetY; - break; - case SPath.kConicVerb: - _conicWeightIndex++; - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; - break; - case SPath.kQuadVerb: - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; - break; - case SPath.kCubicVerb: - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - outPts[4] = pathRef.points[_pointIndex++]; - outPts[5] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[6] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[7] = pathRef.points[_pointIndex++]; - break; - case SPath.kCloseVerb: - verb = _autoClose(outPts); - if (verb == SPath.kLineVerb) { - // Move back one verb since we constructed line for this close verb. - _verbIndex--; - } else { - _needClose = false; - _segmentState = SPathSegmentState.kEmptyContour; - } - _lastPointX = _moveToX; - _lastPointY = _moveToY; - break; - case SPath.kDoneVerb: - assert(_verbIndex == pathRef.countVerbs()); - break; - default: - throw FormatException('Unsupport Path verb $verb'); - } - return verb; - } - - double get conicWeight => pathRef.atWeight(_conicWeightIndex); -} diff --git a/lib/web_ui/lib/src/engine/html/path/tangent.dart b/lib/web_ui/lib/src/engine/html/path/tangent.dart index 305a1dc400d62..a9780e8d2c0a8 100644 --- a/lib/web_ui/lib/src/engine/html/path/tangent.dart +++ b/lib/web_ui/lib/src/engine/html/path/tangent.dart @@ -19,7 +19,7 @@ void tangentLine( } final double dx = x1 - x0; final double dy = y1 - y0; - if (!_nearlyEqual((x - x0) * dy, dx * (y - y0))) { + if (!SPath.nearlyEqual((x - x0) * dy, dx * (y - y0))) { return; } tangents.add(ui.Offset(dx, dy)); @@ -40,7 +40,7 @@ void tangentQuad( if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2)) { return; } - final _QuadRoots roots = _QuadRoots(); + final QuadRoots roots = QuadRoots(); int n = roots.findRoots(y0 - 2 * y1 + y2, 2 * (y1 - y0), y0 - y); for (int index = 0; index < n; ++index) { double t = index == 0 ? roots.root0! : roots.root1!; @@ -48,7 +48,7 @@ void tangentQuad( double A = x2 - 2 * x1 + C; double B = 2 * (x1 - C); double xt = polyEval(A, B, C, t); - if (!_nearlyEqual(x, xt)) { + if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalQuadTangentAt(x0, y0, x1, y1, x2, y2, t)); @@ -100,13 +100,13 @@ void tangentConic(Float32List pts, double x, double y, double weight, // B = b*w - w * yCept + yCept - a B -= C; C -= y; - final _QuadRoots quadRoots = _QuadRoots(); + final QuadRoots quadRoots = QuadRoots(); int n = quadRoots.findRoots(A, 2 * B, C); for (int index = 0; index < n; ++index) { double t = index == 0 ? quadRoots.root0! : quadRoots.root1!; - double xt = _conicEvalNumerator(x0, x1, x2, weight, t) / - _conicEvalDenominator(weight, t); - if (!_nearlyEqual(x, xt)) { + double xt = Conic.evalNumerator(x0, x1, x2, weight, t) / + Conic.evalDenominator(weight, t); + if (!SPath.nearlyEqual(x, xt)) { continue; } Conic conic = Conic(x0, y0, x1, y1, x2, y2, weight); @@ -136,16 +136,16 @@ void tangentCubic( return; } final Float32List dst = Float32List(20); - int n = _chopCubicAtYExtrema(pts, dst); + int n = chopCubicAtYExtrema(pts, dst); for (int i = 0; i <= n; ++i) { int bufferPos = i * 6; - double? t = _chopMonoAtY(dst, i * 6, y); + double? t = chopMonoAtY(dst, i * 6, y); if (t == null) { continue; } - double xt = _evalCubicPts(dst[bufferPos], dst[bufferPos + 2], + double xt = evalCubicPts(dst[bufferPos], dst[bufferPos + 2], dst[bufferPos + 4], dst[bufferPos + 6], t); - if (!_nearlyEqual(x, xt)) { + if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalCubicTangentAt(dst, bufferPos, t)); @@ -186,7 +186,7 @@ ui.Offset _evalCubicTangentAt(Float32List points, int bufferPos, double t) { ui.Offset _evalCubicDerivative(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double t) { - final _SkQuadCoefficients coeff = _SkQuadCoefficients( + final SkQuadCoefficients coeff = SkQuadCoefficients( x3 + 3 * (x1 - x2) - x0, y3 + 3 * (y1 - y2) - y0, 2 * (x2 - (2 * x1) + x0), diff --git a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart new file mode 100644 index 0000000000000..eee4299aaa40e --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; +import '../browser_detection.dart'; + +import 'package:ui/src/engine.dart'; + +/// Counter used for generating clip path id inside an svg tag. +int _clipIdCounter = 0; + +/// Used for clipping and filter svg resources. +/// +/// Position needs to be absolute since these svgs are sandwiched between +/// canvas elements and can cause layout shifts otherwise. +const String kSvgResourceHeader = ''; + +/// Converts Path to svg element that contains a clip-path definition. +/// +/// Calling this method updates [_clipIdCounter]. The HTML id of the generated +/// clip is set to "svgClip${_clipIdCounter}", e.g. "svgClip123". +String pathToSvgClipPath(ui.Path path, + {double offsetX = 0, + double offsetY = 0, + double scaleX = 1.0, + double scaleY = 1.0}) { + _clipIdCounter += 1; + final StringBuffer sb = StringBuffer(); + sb.write(kSvgResourceHeader); + sb.write(''); + + final String clipId = 'svgClip$_clipIdCounter'; + + if (browserEngine == BrowserEngine.firefox) { + // Firefox objectBoundingBox fails to scale to 1x1 units, instead use + // no clipPathUnits but write the path in target units. + sb.write(''); + sb.write(''); + sb.write(' 'url(#svgClip$_clipIdCounter)'; + +/// Resets clip ids. Used for testing by [debugForgetFrameScene] API. +void resetSvgClipIds() { + _clipIdCounter = 0; +} diff --git a/lib/web_ui/lib/src/engine/html/recording_canvas.dart b/lib/web_ui/lib/src/engine/html/recording_canvas.dart index d47d94fca6bc0..170345266b750 100644 --- a/lib/web_ui/lib/src/engine/html/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/recording_canvas.dart @@ -189,7 +189,7 @@ class RecordingCanvas { } catch (e) { // commands should never fail, but... // https://bugzilla.mozilla.org/show_bug.cgi?id=941146 - if (!_isNsErrorFailureException(e)) { + if (!isNsErrorFailureException(e)) { rethrow; } } diff --git a/lib/web_ui/lib/src/engine/html/scene_builder.dart b/lib/web_ui/lib/src/engine/html/scene_builder.dart index 99a4118563ed3..bb6801fa628b8 100644 --- a/lib/web_ui/lib/src/engine/html/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/html/scene_builder.dart @@ -525,7 +525,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { static void debugForgetFrameScene() { _lastFrameScene?.rootElement?.remove(); _lastFrameScene = null; - _clipIdCounter = 0; + resetSvgClipIds(); _recycledCanvases.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 46d854412111b..1a75e1cdfc855 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -20,14 +20,14 @@ abstract class EngineGradient implements ui.Gradient { class GradientSweep extends EngineGradient { GradientSweep(this.center, this.colors, this.colorStops, this.tileMode, this.startAngle, this.endAngle, this.matrix4) - : assert(_offsetIsValid(center)), + : assert(offsetIsValid(center)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison assert(startAngle != null), // ignore: unnecessary_null_comparison assert(endAngle != null), // ignore: unnecessary_null_comparison assert(startAngle < endAngle), super._() { - _validateColorStops(colors, colorStops); + validateColorStops(colors, colorStops); } @override @@ -155,14 +155,14 @@ class GradientLinear extends EngineGradient { this.colorStops, this.tileMode, Float32List? matrix, - ) : assert(_offsetIsValid(from)), - assert(_offsetIsValid(to)), + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison - this.matrix4 = matrix == null ? null : _FastMatrix32(matrix), + this.matrix4 = matrix == null ? null : FastMatrix32(matrix), super._() { if (assertionsEnabled) { - _validateColorStops(colors, colorStops); + validateColorStops(colors, colorStops); } } @@ -171,7 +171,7 @@ class GradientLinear extends EngineGradient { final List colors; final List? colorStops; final ui.TileMode tileMode; - final _FastMatrix32? matrix4; + final FastMatrix32? matrix4; @override Object createPaintStyle(html.CanvasRenderingContext2D? ctx, @@ -185,7 +185,7 @@ class GradientLinear extends EngineGradient { html.CanvasGradient _createCanvasGradient(html.CanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density) { - _FastMatrix32? matrix4 = this.matrix4; + FastMatrix32? matrix4 = this.matrix4; html.CanvasGradient gradient; final double offsetX = shaderBounds!.left; final double offsetY = shaderBounds.top; diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 9305ecc897e03..323532b536e22 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -2,7 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'vector_math.dart'; /// Generic callback signature, used by [_futurize]. typedef Callback = void Function(T result); @@ -313,47 +322,6 @@ bool rectContainsOther(ui.Rect rect, ui.Rect other) { rect.bottom >= other.bottom; } -/// Counter used for generating clip path id inside an svg tag. -int _clipIdCounter = 0; - -/// Used for clipping and filter svg resources. -/// -/// Position needs to be absolute since these svgs are sandwiched between -/// canvas elements and can cause layout shifts otherwise. -const String kSvgResourceHeader = ''; - -/// Converts Path to svg element that contains a clip-path definition. -/// -/// Calling this method updates [_clipIdCounter]. The HTML id of the generated -/// clip is set to "svgClip${_clipIdCounter}", e.g. "svgClip123". -String _pathToSvgClipPath(ui.Path path, - {double offsetX = 0, - double offsetY = 0, - double scaleX = 1.0, - double scaleY = 1.0}) { - _clipIdCounter += 1; - final StringBuffer sb = StringBuffer(); - sb.write(kSvgResourceHeader); - sb.write(''); - - final String clipId = 'svgClip$_clipIdCounter'; - - if (browserEngine == BrowserEngine.firefox) { - // Firefox objectBoundingBox fails to scale to 1x1 units, instead use - // no clipPathUnits but write the path in target units. - sb.write(''); - sb.write(''); - sb.write('{'type': 'fontsChange'}); - -// Font load callbacks will typically arrive in sequence, we want to prevent -// sendFontChangeMessage of causing multiple synchronous rebuilds. -// This flag ensures we properly schedule a single call to framework. -bool _fontChangeScheduled = false; - -FutureOr sendFontChangeMessage() async { - if (!_fontChangeScheduled) { - _fontChangeScheduled = true; - // Batch updates into next animationframe. - html.window.requestAnimationFrame((num _) { - _fontChangeScheduled = false; - EnginePlatformDispatcher.instance.invokeOnPlatformMessage( - 'flutter/system', - _fontChangeMessage, - (_) {}, - ); - }); - } -} - // Stores matrix in a form that allows zero allocation transforms. -class _FastMatrix32 { +class FastMatrix32 { final Float32List matrix; double transformedX = 0, transformedY = 0; - _FastMatrix32(this.matrix); + FastMatrix32(this.matrix); void transform(double x, double y) { transformedX = matrix[12] + (matrix[0] * x) + (matrix[4] * y); @@ -567,29 +512,6 @@ bool isUnsoundNull(dynamic object) { return object == null; } -bool _offsetIsValid(ui.Offset offset) { - assert(!offset.dx.isNaN && !offset.dy.isNaN, - 'Offset argument contained a NaN value.'); - return true; -} - -bool _matrix4IsValid(Float32List matrix4) { - assert(matrix4.length == 16, 'Matrix4 must have 16 entries.'); - return true; -} - -void _validateColorStops(List colors, List? colorStops) { - if (colorStops == null) { - if (colors.length != 2) - throw ArgumentError( - '"colors" must have length 2 if "colorStops" is omitted.'); - } else { - if (colors.length != colorStops.length) - throw ArgumentError( - '"colors" and "colorStops" arguments must have equal length.'); - } -} - int clampInt(int value, int min, int max) { assert(min <= max); if (value < min) { diff --git a/lib/web_ui/lib/src/engine/validators.dart b/lib/web_ui/lib/src/engine/validators.dart index 885b4c0afd89c..252920e48093d 100644 --- a/lib/web_ui/lib/src/engine/validators.dart +++ b/lib/web_ui/lib/src/engine/validators.dart @@ -47,3 +47,16 @@ bool radiusIsValid(ui.Radius radius) { 'Radius argument contained a NaN value.'); return true; } + +/// Validates color and color stops used for a gradient. +void validateColorStops(List colors, List? colorStops) { + if (colorStops == null) { + if (colors.length != 2) + throw ArgumentError( + '"colors" must have length 2 if "colorStops" is omitted.'); + } else { + if (colors.length != colorStops.length) + throw ArgumentError( + '"colors" and "colorStops" arguments must have equal length.'); + } +} From b59999ec1a13eb65c3d7dcb37353d0c6577e36ed Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 29 Apr 2021 12:38:55 -0700 Subject: [PATCH 02/12] update imports --- lib/web_ui/lib/src/engine/html/path/conic.dart | 1 + lib/web_ui/lib/src/engine/html/path/cubic.dart | 2 +- lib/web_ui/lib/src/engine/html/path/path_ref.dart | 3 ++- lib/web_ui/lib/src/engine/html/path/path_utils.dart | 1 + lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart | 4 ++-- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/html/path/conic.dart b/lib/web_ui/lib/src/engine/html/path/conic.dart index 5febe594db3e6..90502ddc27d66 100644 --- a/lib/web_ui/lib/src/engine/html/path/conic.dart +++ b/lib/web_ui/lib/src/engine/html/path/conic.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'dart:math' as math; import 'package:ui/ui.dart' as ui; + import 'path_utils.dart'; /// Converts conic curve to a list of quadratic curves for rendering on diff --git a/lib/web_ui/lib/src/engine/html/path/cubic.dart b/lib/web_ui/lib/src/engine/html/path/cubic.dart index ee2de98b5ff56..0692cc2be7584 100644 --- a/lib/web_ui/lib/src/engine/html/path/cubic.dart +++ b/lib/web_ui/lib/src/engine/html/path/cubic.dart @@ -5,8 +5,8 @@ import 'dart:typed_data'; import 'dart:math' as math; -import 'path_utils.dart'; import '../../util.dart'; +import 'path_utils.dart'; /// Chops cubic at Y extrema points and writes result to [dest]. /// diff --git a/lib/web_ui/lib/src/engine/html/path/path_ref.dart b/lib/web_ui/lib/src/engine/html/path/path_ref.dart index 77fe3605a005f..cf03a2340dbb8 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_ref.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_ref.dart @@ -7,8 +7,9 @@ import 'dart:js_util' as js_util; import 'dart:math' as math; import 'package:ui/ui.dart' as ui; -import 'path_utils.dart'; + import '../../util.dart'; +import 'path_utils.dart'; /// Stores the path verbs, points and conic weights. /// diff --git a/lib/web_ui/lib/src/engine/html/path/path_utils.dart b/lib/web_ui/lib/src/engine/html/path/path_utils.dart index 52edc02d041b3..d66452215bed1 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_utils.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_utils.dart @@ -5,6 +5,7 @@ import 'dart:math' as math; import 'package:ui/ui.dart' as ui; + import 'path_ref.dart'; /// Mask used to keep track of types of verbs used in a path segment. diff --git a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart index eee4299aaa40e..b8e7523a61892 100644 --- a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart +++ b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../browser_detection.dart'; -import 'package:ui/src/engine.dart'; +import '../browser_detection.dart'; /// Counter used for generating clip path id inside an svg tag. int _clipIdCounter = 0; From 164b3b64fdfc1735c2c474b4d4e96c6f4d6aec64 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 29 Apr 2021 13:10:57 -0700 Subject: [PATCH 03/12] move normalized_gradient and shader_builder --- lib/web_ui/lib/src/engine.dart | 5 +++-- .../src/engine/html/shaders/normalized_gradient.dart | 11 +++++++---- lib/web_ui/lib/src/engine/html/shaders/shader.dart | 2 +- .../lib/src/engine/html/shaders/shader_builder.dart | 5 ++++- .../surface/shaders/normalized_gradient_test.dart | 2 +- .../engine/surface/shaders/shader_builder_test.dart | 1 + 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 3c06fc0dfc200..d358029ce6edb 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -59,6 +59,9 @@ export 'engine/html/path/path_ref.dart'; import 'engine/html/path/path_utils.dart'; export 'engine/html/path/path_utils.dart'; +import 'engine/html/shaders/normalized_gradient.dart'; +import 'engine/html/shaders/shader_builder.dart'; + import 'engine/mouse_cursor.dart'; export 'engine/mouse_cursor.dart'; @@ -176,9 +179,7 @@ part 'engine/html/render_vertices.dart'; part 'engine/html/scene.dart'; part 'engine/html/scene_builder.dart'; part 'engine/html/shader_mask.dart'; -part 'engine/html/shaders/normalized_gradient.dart'; part 'engine/html/shaders/shader.dart'; -part 'engine/html/shaders/shader_builder.dart'; part 'engine/html/surface.dart'; part 'engine/html/surface_stats.dart'; part 'engine/html/transform.dart'; diff --git a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart index f11c1f5652bcd..5f38787bfad0d 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; +import 'shader_builder.dart'; /// Converts colors and stops to typed array of bias, scale and threshold to use /// in shaders. @@ -125,7 +128,7 @@ class NormalizedGradient { /// uniforms. /// /// Bias and scale data are vec4 uniforms that hold color data. -void _writeUnrolledBinarySearch(ShaderMethod method, int start, int end, +void writeUnrolledBinarySearch(ShaderMethod method, int start, int end, {required String probe, required String sourcePrefix, required String biasName, required String scaleName}) { @@ -141,13 +144,13 @@ void _writeUnrolledBinarySearch(ShaderMethod method, int start, int end, thresholdAtMid += '.${_vectorComponentIndexToName((mid + 1) % 4)}'; method.addStatement('if ($probe < $thresholdAtMid) {'); method.indent(); - _writeUnrolledBinarySearch(method, start, mid, + writeUnrolledBinarySearch(method, start, mid, probe: probe, sourcePrefix: sourcePrefix, biasName: biasName, scaleName: scaleName); method.unindent(); method.addStatement('} else {'); method.indent(); - _writeUnrolledBinarySearch(method, mid + 1, end, + writeUnrolledBinarySearch(method, mid + 1, end, probe: probe, sourcePrefix: sourcePrefix, biasName: biasName, scaleName: scaleName); method.unindent(); 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 1a75e1cdfc855..8d0b3e119d841 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -438,7 +438,7 @@ String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method, probeName = 'tiled_st'; break; } - _writeUnrolledBinarySearch(method, 0, gradient.thresholdCount - 1, + writeUnrolledBinarySearch(method, 0, gradient.thresholdCount - 1, probe: probeName, sourcePrefix: 'threshold', biasName: 'bias', diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart index 41b1695c04f4a..ce584dab49d34 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import '../../util.dart'; /// Creates shader program for target webgl version. /// diff --git a/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart b/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart index b413bb65a220f..dfe0c620de90c 100644 --- a/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart +++ b/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart @@ -5,7 +5,7 @@ // @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/html/shaders/normalized_gradient.dart'; import 'package:ui/ui.dart' as ui hide window; void main() { diff --git a/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart index 336cf4f8d8890..df67ce9e7962f 100644 --- a/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart +++ b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/html/shaders/shader_builder.dart'; import 'package:ui/ui.dart' hide window; void main() { From 9e303a13bccf81a6a0c7760a2e6dd9e171115f64 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 29 Apr 2021 13:17:31 -0700 Subject: [PATCH 04/12] Add vertex shader --- lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/html/render_vertices.dart | 90 ++++++------------- .../html/shaders/normalized_gradient.dart | 2 +- .../lib/src/engine/html/shaders/shader.dart | 30 +++---- .../engine/html/shaders/vertex_shaders.dart | 42 +++++++++ 5 files changed, 88 insertions(+), 77 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index d358029ce6edb..336ffdc6bc31e 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -61,6 +61,7 @@ export 'engine/html/path/path_utils.dart'; import 'engine/html/shaders/normalized_gradient.dart'; import 'engine/html/shaders/shader_builder.dart'; +import 'engine/html/shaders/vertex_shaders.dart'; import 'engine/mouse_cursor.dart'; export 'engine/mouse_cursor.dart'; diff --git a/lib/web_ui/lib/src/engine/html/render_vertices.dart b/lib/web_ui/lib/src/engine/html/render_vertices.dart index 0965b43cd67bf..7fd1fbef96ea8 100644 --- a/lib/web_ui/lib/src/engine/html/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/html/render_vertices.dart @@ -72,10 +72,10 @@ abstract class _GlRenderer { ui.BlendMode blendMode, SurfacePaintData paint); - Object? drawRect(ui.Rect targetRect, _GlContext gl, _GlProgram glProgram, + Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, NormalizedGradient gradient, int widthInPixels, int heightInPixels); - String drawRectToImageUrl(ui.Rect targetRect, _GlContext gl, _GlProgram glProgram, + String drawRectToImageUrl(ui.Rect targetRect, GlContext gl, GlProgram glProgram, NormalizedGradient gradient, int widthInPixels, int heightInPixels); void drawHairline(html.CanvasRenderingContext2D? _ctx, Float32List positions); @@ -88,10 +88,9 @@ abstract class _GlRenderer { class _WebGlRenderer implements _GlRenderer { /// Cached vertex shader reused by [drawVertices] and gradients. - static String? _baseVertexShader; static String? _textureVertexShader; - static void _setupVertexTransforms(_GlContext gl, _GlProgram glProgram, + static void _setupVertexTransforms(GlContext gl, GlProgram glProgram, double offsetX, double offsetY, double widthInPixels, double heightInPixels, Matrix4 transform) { Object transformUniform = gl.getUniformLocation(glProgram.program, @@ -109,13 +108,13 @@ class _WebGlRenderer implements _GlRenderer { gl.setUniform4f(shift, -1, 1, 0, 0); } - static void _setupTextureScalar(_GlContext gl, _GlProgram glProgram, + static void _setupTextureScalar(GlContext gl, GlProgram glProgram, double sx, double sy) { Object scalar = gl.getUniformLocation(glProgram.program, 'u_texscale'); gl.setUniform2f(scalar, sx, sy); } - static dynamic _tileModeToGlWrapping(_GlContext gl, ui.TileMode tileMode) { + static dynamic _tileModeToGlWrapping(GlContext gl, ui.TileMode tileMode) { switch(tileMode) { case ui.TileMode.clamp: return gl.kClampToEdge; @@ -175,15 +174,15 @@ class _WebGlRenderer implements _GlRenderer { : paint.shader as ImageShader; final String vertexShader = imageShader == null - ? writeBaseVertexShader() : writeTextureVertexShader(); + ? VertexShaders.writeBaseVertexShader() : writeTextureVertexShader(); final String fragmentShader = imageShader == null ? _writeVerticesFragmentShader() : _writeVerticesTextureFragmentShader(isWebGl2, imageShader.tileModeX, imageShader.tileModeY); - _GlContext gl = _GlContextCache.createGlContext(widthInPixels, heightInPixels)!; + GlContext gl = _GlContextCache.createGlContext(widthInPixels, heightInPixels)!; - _GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader); + GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader); gl.useProgram(glProgram); Object? positionAttributeLocation = @@ -332,7 +331,7 @@ class _WebGlRenderer implements _GlRenderer { /// /// Browsers that support OffscreenCanvas and the transferToImageBitmap api /// will return ImageBitmap, otherwise will return CanvasElement. - Object? drawRect(ui.Rect targetRect, _GlContext gl, _GlProgram glProgram, + Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, NormalizedGradient gradient, int widthInPixels, int heightInPixels) { drawRectToGl(targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); Object? image = gl.readPatternData(); @@ -343,7 +342,7 @@ class _WebGlRenderer implements _GlRenderer { /// Renders a rectangle using given program into an image resource and returns /// url. - String drawRectToImageUrl(ui.Rect targetRect, _GlContext gl, _GlProgram glProgram, + String drawRectToImageUrl(ui.Rect targetRect, GlContext gl, GlProgram glProgram, NormalizedGradient gradient, int widthInPixels, int heightInPixels) { drawRectToGl(targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); final String imageUrl = gl.toImageUrl(); @@ -353,10 +352,10 @@ class _WebGlRenderer implements _GlRenderer { return imageUrl; } - /// Renders a rectangle using given program into [_GlContext]. + /// Renders a rectangle using given program into [GlContext]. /// /// Caller has to cleanup gl array and element array buffers. - void drawRectToGl(ui.Rect targetRect, _GlContext gl, _GlProgram glProgram, + void drawRectToGl(ui.Rect targetRect, GlContext gl, GlProgram glProgram, NormalizedGradient gradient, int widthInPixels, int heightInPixels) { // Setup rectangle coordinates. final double left = targetRect.left; @@ -425,38 +424,7 @@ class _WebGlRenderer implements _GlRenderer { gl.drawElements(gl.kTriangles, _vertexIndicesForRect.length, gl.kUnsignedShort); } - /// Creates a vertex shader transforms pixel space [Vertices.positions] to - /// final clipSpace -1..1 coordinates with inverted Y Axis. - /// #version 300 es - /// layout (location=0) in vec4 position; - /// layout (location=1) in vec4 color; - /// uniform mat4 u_ctransform; - /// uniform vec4 u_scale; - /// uniform vec4 u_shift; - /// out vec4 vColor; - /// void main() { - /// gl_Position = ((u_ctransform * position) * u_scale) + u_shift; - /// v_color = color.zyxw; - /// } - static String writeBaseVertexShader() { - if (_baseVertexShader == null) { - ShaderBuilder builder = ShaderBuilder(webGLVersion); - builder.addIn(ShaderType.kVec4, name: 'position'); - builder.addIn(ShaderType.kVec4, name: 'color'); - builder.addUniform(ShaderType.kMat4, name: 'u_ctransform'); - builder.addUniform(ShaderType.kVec4, name: 'u_scale'); - builder.addUniform(ShaderType.kVec4, name: 'u_shift'); - builder.addOut(ShaderType.kVec4, name: 'v_color'); - ShaderMethod method = builder.addMethod('main'); - method.addStatement( - 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); - method.addStatement('v_color = color.zyxw;'); - _baseVertexShader = builder.build(); - } - return _baseVertexShader!; - } - - static String writeTextureVertexShader() { + static String writeTextureVertexShader() { if (_textureVertexShader == null) { ShaderBuilder builder = ShaderBuilder(webGLVersion); builder.addIn(ShaderType.kVec4, name: 'position'); @@ -647,13 +615,13 @@ Float32List _convertVertexPositions(ui.VertexMode mode, Float32List positions) { } /// Compiled and cached gl program. -class _GlProgram { +class GlProgram { final Object program; - _GlProgram(this.program); + GlProgram(this.program); } /// JS Interop helper for webgl apis. -class _GlContext { +class GlContext { final Object glContext; final bool isOffscreen; dynamic _kCompileStatus; @@ -680,20 +648,20 @@ class _GlContext { Object? _canvas; int? _widthInPixels; int? _heightInPixels; - static late Map _programCache; + static late Map _programCache; - _GlContext.fromOffscreenCanvas(html.OffscreenCanvas canvas) + GlContext.fromOffscreenCanvas(html.OffscreenCanvas canvas) : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false})!, isOffscreen = true { - _programCache = {}; + _programCache = {}; _canvas = canvas; } - _GlContext.fromCanvas(html.CanvasElement canvas, bool useWebGl1) + GlContext.fromCanvas(html.CanvasElement canvas, bool useWebGl1) : glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', {'premultipliedAlpha': false})!, isOffscreen = false { - _programCache = {}; + _programCache = {}; _canvas = canvas; } @@ -712,10 +680,10 @@ class _GlContext { left, top, _widthInPixels, _heightInPixels]); } - _GlProgram cacheProgram( + GlProgram cacheProgram( String vertexShaderSource, String fragmentShaderSource) { String cacheKey = '$vertexShaderSource||$fragmentShaderSource'; - _GlProgram? cachedProgram = _programCache[cacheKey]; + GlProgram? cachedProgram = _programCache[cacheKey]; if (cachedProgram == null) { // Create and compile shaders. Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource); @@ -726,7 +694,7 @@ class _GlContext { attachShader(program, vertexShader); attachShader(program, fragmentShader); linkProgram(program); - cachedProgram = _GlProgram(program); + cachedProgram = GlProgram(program); _programCache[cacheKey] = cachedProgram; } return cachedProgram; @@ -762,7 +730,7 @@ class _GlContext { } } - void useProgram(_GlProgram program) { + void useProgram(GlProgram program) { js_util.callMethod(glContext, 'useProgram', [program.program]); } @@ -1124,7 +1092,7 @@ class _OffScreenCanvas { class _GlContextCache { static int _maxPixelWidth = 0; static int _maxPixelHeight = 0; - static _GlContext? _cachedContext; + static GlContext? _cachedContext; static _OffScreenCanvas? _offScreenCanvas; static void dispose() { @@ -1134,7 +1102,7 @@ class _GlContextCache { _offScreenCanvas?.dispose(); } - static _GlContext? createGlContext(int widthInPixels, int heightInPixels) { + static GlContext? createGlContext(int widthInPixels, int heightInPixels) { if (widthInPixels > _maxPixelWidth || heightInPixels > _maxPixelHeight) { _cachedContext?.dispose(); _cachedContext = null; @@ -1145,9 +1113,9 @@ class _GlContextCache { _offScreenCanvas ??= _OffScreenCanvas(widthInPixels, heightInPixels); if (_OffScreenCanvas.supported) { _cachedContext ??= - _GlContext.fromOffscreenCanvas(_offScreenCanvas!._canvas!); + GlContext.fromOffscreenCanvas(_offScreenCanvas!._canvas!); } else { - _cachedContext ??= _GlContext.fromCanvas(_offScreenCanvas!._glCanvas!, + _cachedContext ??= GlContext.fromCanvas(_offScreenCanvas!._glCanvas!, webGLVersion == WebGLVersion.webgl1); } _cachedContext!.setViewportSize(widthInPixels, heightInPixels); diff --git a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart index 5f38787bfad0d..60acb25bec302 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart @@ -98,7 +98,7 @@ class NormalizedGradient { } /// Sets uniforms for threshold, bias and scale for program. - void setupUniforms(_GlContext gl, _GlProgram glProgram) { + void setupUniforms(GlContext gl, GlProgram glProgram) { for (int i = 0; i < thresholdCount; i++) { Object biasId = gl.getUniformLocation(glProgram.program, 'bias_$i'); gl.setUniform4f(biasId, _bias[i * 4], _bias[i * 4 + 1], _bias[i * 4 + 2], _bias[i * 4 + 3]); 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 8d0b3e119d841..723a680edf86b 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -42,17 +42,17 @@ class GradientSweep extends EngineGradient { // Render gradient into a bitmap and create a canvas pattern. _OffScreenCanvas offScreenCanvas = _OffScreenCanvas(widthInPixels, heightInPixels); - _GlContext gl = _OffScreenCanvas.supported - ? _GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) - : _GlContext.fromCanvas( + GlContext gl = _OffScreenCanvas.supported + ? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) + : GlContext.fromCanvas( offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1); gl.setViewportSize(widthInPixels, heightInPixels); NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); - _GlProgram glProgram = gl.cacheProgram( - _WebGlRenderer.writeBaseVertexShader(), + GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), _createSweepFragmentShader(normalizedGradient, tileMode)); gl.useProgram(glProgram); @@ -225,17 +225,17 @@ class GradientLinear extends EngineGradient { // Render gradient into a bitmap and create a canvas pattern. _OffScreenCanvas offScreenCanvas = _OffScreenCanvas(widthInPixels, heightInPixels); - _GlContext gl = _OffScreenCanvas.supported - ? _GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) - : _GlContext.fromCanvas( + GlContext gl = _OffScreenCanvas.supported + ? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) + : GlContext.fromCanvas( offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1); gl.setViewportSize(widthInPixels, heightInPixels); NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); - _GlProgram glProgram = gl.cacheProgram( - _WebGlRenderer.writeBaseVertexShader(), + GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), _createLinearFragmentShader(normalizedGradient, tileMode)); gl.useProgram(glProgram); @@ -496,17 +496,17 @@ class GradientRadial extends EngineGradient { // Render gradient into a bitmap and create a canvas pattern. _OffScreenCanvas offScreenCanvas = _OffScreenCanvas(widthInPixels, heightInPixels); - _GlContext gl = _OffScreenCanvas.supported - ? _GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) - : _GlContext.fromCanvas( + GlContext gl = _OffScreenCanvas.supported + ? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!) + : GlContext.fromCanvas( offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1); gl.setViewportSize(widthInPixels, heightInPixels); NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); - _GlProgram glProgram = gl.cacheProgram( - _WebGlRenderer.writeBaseVertexShader(), + GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), _createRadialFragmentShader( normalizedGradient, shaderBounds, tileMode)); gl.useProgram(glProgram); diff --git a/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart new file mode 100644 index 0000000000000..84bd37f786d99 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'shader_builder.dart'; +import '../../browser_detection.dart'; + +/// Provides common shaders used for gradients and drawVertices APIs. +class VertexShaders { + static String? _baseVertexShader; + + /// Creates a vertex shader transforms pixel space [Vertices.positions] to + /// final clipSpace -1..1 coordinates with inverted Y Axis. + /// #version 300 es + /// layout (location=0) in vec4 position; + /// layout (location=1) in vec4 color; + /// uniform mat4 u_ctransform; + /// uniform vec4 u_scale; + /// uniform vec4 u_shift; + /// out vec4 vColor; + /// void main() { + /// gl_Position = ((u_ctransform * position) * u_scale) + u_shift; + /// v_color = color.zyxw; + /// } + static String writeBaseVertexShader() { + if (_baseVertexShader == null) { + ShaderBuilder builder = ShaderBuilder(webGLVersion); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addIn(ShaderType.kVec4, name: 'color'); + builder.addUniform(ShaderType.kMat4, name: 'u_ctransform'); + builder.addUniform(ShaderType.kVec4, name: 'u_scale'); + builder.addUniform(ShaderType.kVec4, name: 'u_shift'); + builder.addOut(ShaderType.kVec4, name: 'v_color'); + ShaderMethod method = builder.addMethod('main'); + method.addStatement( + 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); + method.addStatement('v_color = color.zyxw;'); + _baseVertexShader = builder.build(); + } + return _baseVertexShader!; + } +} From 15c11bfe3f0b6fcc138bdbec95256405b07d3067 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 29 Apr 2021 13:30:12 -0700 Subject: [PATCH 05/12] add exports --- lib/web_ui/lib/src/engine.dart | 5 +++++ .../lib/src/engine/html/shaders/normalized_gradient.dart | 2 ++ 2 files changed, 7 insertions(+) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 336ffdc6bc31e..fca7a82571b0e 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -60,8 +60,13 @@ import 'engine/html/path/path_utils.dart'; export 'engine/html/path/path_utils.dart'; import 'engine/html/shaders/normalized_gradient.dart'; +export 'engine/html/shaders/normalized_gradient.dart'; + import 'engine/html/shaders/shader_builder.dart'; +export 'engine/html/shaders/shader_builder.dart'; + import 'engine/html/shaders/vertex_shaders.dart'; +export 'engine/html/shaders/vertex_shaders.dart'; import 'engine/mouse_cursor.dart'; export 'engine/mouse_cursor.dart'; diff --git a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart index 60acb25bec302..5afe9fe0dade9 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart @@ -4,6 +4,8 @@ import 'dart:typed_data'; +import 'package:ui/src/engine.dart'; + import 'package:ui/ui.dart' as ui; import 'shader_builder.dart'; From 2265d618598d02ee528291172d2c1e5c601f07f5 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Fri, 30 Apr 2021 14:50:58 -0700 Subject: [PATCH 06/12] update codec --- lib/web_ui/lib/src/engine.dart | 4 +++- lib/web_ui/lib/src/engine/html_image_codec.dart | 10 +++++++++- .../test/engine/image/html_image_codec_test.dart | 2 +- lib/web_ui/test/engine/surface/scene_builder_test.dart | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index fca7a82571b0e..cbbccb97a4c67 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -38,6 +38,9 @@ export 'engine/alarm_clock.dart'; import 'engine/browser_detection.dart'; export 'engine/browser_detection.dart'; +import 'engine/html_image_codec.dart'; +export 'engine/html_image_codec.dart'; + import 'engine/html/path_to_svg_clip.dart'; export 'engine/html/path_to_svg_clip.dart'; @@ -189,7 +192,6 @@ part 'engine/html/shaders/shader.dart'; part 'engine/html/surface.dart'; part 'engine/html/surface_stats.dart'; part 'engine/html/transform.dart'; -part 'engine/html_image_codec.dart'; part 'engine/keyboard_binding.dart'; part 'engine/keyboard.dart'; part 'engine/key_map.dart'; diff --git a/lib/web_ui/lib/src/engine/html_image_codec.dart b/lib/web_ui/lib/src/engine/html_image_codec.dart index a691f52c6e4d5..8dfe8fb75f956 100644 --- a/lib/web_ui/lib/src/engine/html_image_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_codec.dart @@ -2,7 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; +import 'dart:js_util' as js_util; + +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'util.dart'; final bool _supportsDecode = js_util.getProperty( js_util.getProperty( diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index 8723bb00d7552..6c50233c9f72e 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart' as ui; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/html_image_codec.dart'; void main() { internalBootstrapBrowserTest(() => testMain); diff --git a/lib/web_ui/test/engine/surface/scene_builder_test.dart b/lib/web_ui/test/engine/surface/scene_builder_test.dart index c964d4b22d627..04d692c4765ec 100644 --- a/lib/web_ui/test/engine/surface/scene_builder_test.dart +++ b/lib/web_ui/test/engine/surface/scene_builder_test.dart @@ -12,6 +12,7 @@ import 'dart:js_util' as js_util; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/html_image_codec.dart'; import 'package:ui/ui.dart' hide window; From c084f07bc48a085c1f0872930de9f8d5beb022ec Mon Sep 17 00:00:00 2001 From: ferhatb Date: Fri, 30 Apr 2021 15:38:24 -0700 Subject: [PATCH 07/12] fix import breaking rewriter --- lib/web_ui/lib/src/engine.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index cbbccb97a4c67..2b3d4662d2d3b 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -38,8 +38,8 @@ export 'engine/alarm_clock.dart'; import 'engine/browser_detection.dart'; export 'engine/browser_detection.dart'; -import 'engine/html_image_codec.dart'; -export 'engine/html_image_codec.dart'; +import 'engine/html_image_codec.dart'; +export 'engine/html_image_codec.dart'; import 'engine/html/path_to_svg_clip.dart'; export 'engine/html/path_to_svg_clip.dart'; From 7d512fdfbf0e0fdda87ccce06d555514bd0aa7c3 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Fri, 30 Apr 2021 16:18:51 -0700 Subject: [PATCH 08/12] rename ImageShader to EngineImageShader --- lib/web_ui/lib/src/engine.dart | 2 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 2 +- .../lib/src/engine/html/recording_canvas.dart | 20 +++++++++---------- .../lib/src/engine/html/render_vertices.dart | 4 ++-- .../lib/src/engine/html/shaders/shader.dart | 4 ++-- lib/web_ui/lib/src/ui/painting.dart | 2 +- .../engine/draw_vertices_golden_test.dart | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 2b3d4662d2d3b..d50f367e5813f 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -167,6 +167,7 @@ part 'engine/color_filter.dart'; part 'engine/dom_canvas.dart'; part 'engine/dom_renderer.dart'; part 'engine/engine_canvas.dart'; +part 'engine/font_change_util.dart'; part 'engine/frame_reference.dart'; part 'engine/html/backdrop_filter.dart'; part 'engine/html/canvas.dart'; @@ -228,7 +229,6 @@ part 'engine/text_editing/autofill_hint.dart'; part 'engine/text_editing/input_type.dart'; part 'engine/text_editing/text_capitalization.dart'; part 'engine/text_editing/text_editing.dart'; -part 'engine/font_change_util.dart'; part 'engine/window.dart'; // The mode the app is running in. diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 860cc1fb7bc07..b5af3350d356e 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -963,7 +963,7 @@ class BitmapCanvas extends EngineCanvas { // blendMode. https://github.com/flutter/flutter/issues/40096 // Move rendering to OffscreenCanvas so that transform is preserved // as well. - assert(paint.shader == null || paint.shader is ImageShader, + assert(paint.shader == null || paint.shader is EngineImageShader, 'Linear/Radial/SweepGradient not supported yet'); final Int32List? colors = vertices._colors; final ui.VertexMode mode = vertices._mode; diff --git a/lib/web_ui/lib/src/engine/html/recording_canvas.dart b/lib/web_ui/lib/src/engine/html/recording_canvas.dart index 170345266b750..861fd0c966630 100644 --- a/lib/web_ui/lib/src/engine/html/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/recording_canvas.dart @@ -321,7 +321,7 @@ class RecordingCanvas { void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); final double paintSpread = math.max(_getPaintSpread(paint), 1.0); final PaintDrawLine command = PaintDrawLine(p1, p2, paint.paintData); // TODO(yjbanov): This can be optimized. Currently we create a box around @@ -345,7 +345,7 @@ class RecordingCanvas { void drawPaint(SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); renderStrategy.hasArbitraryPaint = true; _didDraw = true; final PaintDrawPaint command = PaintDrawPaint(paint.paintData); @@ -355,7 +355,7 @@ class RecordingCanvas { void drawRect(ui.Rect rect, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); if (paint.shader != null) { renderStrategy.hasArbitraryPaint = true; } @@ -372,7 +372,7 @@ class RecordingCanvas { void drawRRect(ui.RRect rrect, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); if (paint.shader != null || !rrect.webOnlyUniformRadii) { renderStrategy.hasArbitraryPaint = true; } @@ -389,7 +389,7 @@ class RecordingCanvas { void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); // Check the inner bounds are contained within the outer bounds // see: https://cs.chromium.org/chromium/src/third_party/skia/src/core/SkCanvas.cpp?l=1787-1789 ui.Rect innerRect = inner.outerRect; @@ -448,7 +448,7 @@ class RecordingCanvas { void drawOval(ui.Rect rect, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); @@ -463,7 +463,7 @@ class RecordingCanvas { void drawCircle(ui.Offset c, double radius, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); @@ -481,7 +481,7 @@ class RecordingCanvas { void drawPath(ui.Path path, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); if (paint.shader == null) { // For Rect/RoundedRect paths use drawRect/drawRRect code paths for // DomCanvas optimization. @@ -518,7 +518,7 @@ class RecordingCanvas { void drawImage(ui.Image image, ui.Offset offset, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); renderStrategy.hasArbitraryPaint = true; renderStrategy.hasImageElements = true; _didDraw = true; @@ -556,7 +556,7 @@ class RecordingCanvas { void drawImageRect( ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! ImageShader, 'ImageShader not supported yet'); + assert(paint.shader == null || paint.shader is! EngineImageShader, 'ImageShader not supported yet'); renderStrategy.hasArbitraryPaint = true; renderStrategy.hasImageElements = true; _didDraw = true; diff --git a/lib/web_ui/lib/src/engine/html/render_vertices.dart b/lib/web_ui/lib/src/engine/html/render_vertices.dart index 7fd1fbef96ea8..7e4acb35643d1 100644 --- a/lib/web_ui/lib/src/engine/html/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/html/render_vertices.dart @@ -170,8 +170,8 @@ class _WebGlRenderer implements _GlRenderer { final bool isWebGl2 = webGLVersion == WebGLVersion.webgl2; - final ImageShader? imageShader = paint.shader == null ? null - : paint.shader as ImageShader; + final EngineImageShader? imageShader = paint.shader == null ? null + : paint.shader as EngineImageShader; final String vertexShader = imageShader == null ? VertexShaders.writeBaseVertexShader() : writeTextureVertexShader(); 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 723a680edf86b..a8e929776f76c 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -135,8 +135,8 @@ class GradientSweep extends EngineGradient { final Float32List? matrix4; } -class ImageShader implements ui.ImageShader { - ImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, this.filterQuality) +class EngineImageShader implements ui.ImageShader { + EngineImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, this.filterQuality) : _image = image as HtmlImage; final ui.TileMode tileModeX; diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index f8e5d20ff28a0..cd58b0f05a430 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -695,7 +695,7 @@ class ImageShader extends Shader { FilterQuality? filterQuality, }) => engine.useCanvasKit ? engine.CkImageShader(image, tmx, tmy, matrix4, filterQuality) - : engine.ImageShader(image, tmx, tmy, matrix4, filterQuality); + : engine.EngineImageShader(image, tmx, tmy, matrix4, filterQuality); } class ImmutableBuffer { diff --git a/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart b/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart index acd6584a86004..273d068ed1489 100644 --- a/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart @@ -366,7 +366,7 @@ void testMain() async { final HtmlImage img = await createTestImage(); final SurfacePaint paint = SurfacePaint(); - final ImageShader imgShader = ImageShader(img, tileMode, tileMode, + final EngineImageShader imgShader = EngineImageShader(img, tileMode, tileMode, Float64List.fromList(matrix4), FilterQuality.high); paint.shader = imgShader; From dfb89b4a1e061ad95309fe24b4dd28b40af28bff Mon Sep 17 00:00:00 2001 From: ferhatb Date: Fri, 30 Apr 2021 16:54:29 -0700 Subject: [PATCH 09/12] update tangent imports --- lib/web_ui/lib/src/engine.dart | 4 +++- lib/web_ui/lib/src/engine/html/painting.dart | 4 +++- lib/web_ui/lib/src/engine/html/path/tangent.dart | 8 +++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index d50f367e5813f..935f4cb4c9f89 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -41,6 +41,9 @@ export 'engine/browser_detection.dart'; import 'engine/html_image_codec.dart'; export 'engine/html_image_codec.dart'; +import 'engine/html/painting.dart'; +export 'engine/html/painting.dart'; + import 'engine/html/path_to_svg_clip.dart'; export 'engine/html/path_to_svg_clip.dart'; @@ -177,7 +180,6 @@ part 'engine/html/debug_canvas_reuse_overlay.dart'; part 'engine/html/image_filter.dart'; part 'engine/html/offset.dart'; part 'engine/html/opacity.dart'; -part 'engine/html/painting.dart'; part 'engine/html/path/path.dart'; part 'engine/html/path/path_to_svg.dart'; part 'engine/html/path/path_windings.dart'; diff --git a/lib/web_ui/lib/src/engine/html/painting.dart b/lib/web_ui/lib/src/engine/html/painting.dart index 683579a49132e..09acfbe0c9713 100644 --- a/lib/web_ui/lib/src/engine/html/painting.dart +++ b/lib/web_ui/lib/src/engine/html/painting.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; /// Implementation of [ui.Paint] used by the HTML rendering backend. class SurfacePaint implements ui.Paint { diff --git a/lib/web_ui/lib/src/engine/html/path/tangent.dart b/lib/web_ui/lib/src/engine/html/path/tangent.dart index a9780e8d2c0a8..44c009b68ca50 100644 --- a/lib/web_ui/lib/src/engine/html/path/tangent.dart +++ b/lib/web_ui/lib/src/engine/html/path/tangent.dart @@ -2,7 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_utils.dart'; /// Computes tangent at point x,y on a line. void tangentLine( From fd745e477f363f53ac4ad9b26b9593496a7c9a1d Mon Sep 17 00:00:00 2001 From: ferhatb Date: Mon, 3 May 2021 13:51:51 -0700 Subject: [PATCH 10/12] update path_to_svg --- lib/web_ui/lib/src/engine.dart | 8 ++++++-- lib/web_ui/lib/src/engine/dom_canvas.dart | 2 +- lib/web_ui/lib/src/engine/html/path/path_to_svg.dart | 12 +++++++++--- lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart | 2 +- .../golden_tests/engine/path_to_svg_golden_test.dart | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 935f4cb4c9f89..004ce2e979ac9 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -62,9 +62,15 @@ export 'engine/html/path/path_metrics.dart'; import 'engine/html/path/path_ref.dart'; export 'engine/html/path/path_ref.dart'; +import 'engine/html/path/path_to_svg.dart'; +export 'engine/html/path/path_to_svg.dart'; + import 'engine/html/path/path_utils.dart'; export 'engine/html/path/path_utils.dart'; +import 'engine/html/path/tangent.dart'; +export 'engine/html/path/tangent.dart'; + import 'engine/html/shaders/normalized_gradient.dart'; export 'engine/html/shaders/normalized_gradient.dart'; @@ -181,9 +187,7 @@ part 'engine/html/image_filter.dart'; part 'engine/html/offset.dart'; part 'engine/html/opacity.dart'; part 'engine/html/path/path.dart'; -part 'engine/html/path/path_to_svg.dart'; part 'engine/html/path/path_windings.dart'; -part 'engine/html/path/tangent.dart'; part 'engine/html/picture.dart'; part 'engine/html/platform_view.dart'; part 'engine/html/recording_canvas.dart'; diff --git a/lib/web_ui/lib/src/engine/dom_canvas.dart b/lib/web_ui/lib/src/engine/dom_canvas.dart index 2b6fa31028ffd..d72a5d9fafe55 100644 --- a/lib/web_ui/lib/src/engine/dom_canvas.dart +++ b/lib/web_ui/lib/src/engine/dom_canvas.dart @@ -267,7 +267,7 @@ html.Element _pathToSvgElement(SurfacePath path, SurfacePaintData paint, sb.write('fill-rule="evenodd" '); } sb.write('d="'); - pathToSvg(path, sb); + pathToSvg(path.pathRef, sb); sb.write('">'); sb.write(''); return html.Element.html(sb.toString(), treeSanitizer: _NullTreeSanitizer()); diff --git a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart index 71968c9334a7e..6bcbff9a5978d 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart @@ -2,13 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; /// Converts [path] to SVG path syntax to be used as "d" attribute in path /// element. -void pathToSvg(SurfacePath path, StringBuffer sb, +void pathToSvg(PathRef pathRef, StringBuffer sb, {double offsetX = 0, double offsetY = 0}) { - final PathRefIterator iter = PathRefIterator(path.pathRef); + final PathRefIterator iter = PathRefIterator(pathRef); int verb = 0; final Float32List outPts = Float32List(PathRefIterator.kMaxBufferSize); while ((verb = iter.next(outPts)) != SPath.kDoneVerb) { diff --git a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart index b8e7523a61892..878ba758957da 100644 --- a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart +++ b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart @@ -43,7 +43,7 @@ String pathToSvgClipPath(ui.Path path, sb.write(''); sb.write(''); final html.Element svgElement = From 405b37e696c2b0f15fd17490eb7c3dfafff4dbb4 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Thu, 6 May 2021 10:02:08 -0700 Subject: [PATCH 11/12] switch path_windings to lib --- lib/web_ui/lib/src/engine.dart | 4 +++- lib/web_ui/lib/src/engine/html/path/path_windings.dart | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 004ce2e979ac9..c2f69676f6789 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -68,6 +68,9 @@ export 'engine/html/path/path_to_svg.dart'; import 'engine/html/path/path_utils.dart'; export 'engine/html/path/path_utils.dart'; +import 'engine/html/path/path_windings.dart'; +export 'engine/html/path/path_windings.dart'; + import 'engine/html/path/tangent.dart'; export 'engine/html/path/tangent.dart'; @@ -187,7 +190,6 @@ part 'engine/html/image_filter.dart'; part 'engine/html/offset.dart'; part 'engine/html/opacity.dart'; part 'engine/html/path/path.dart'; -part 'engine/html/path/path_windings.dart'; part 'engine/html/picture.dart'; part 'engine/html/platform_view.dart'; part 'engine/html/recording_canvas.dart'; diff --git a/lib/web_ui/lib/src/engine/html/path/path_windings.dart b/lib/web_ui/lib/src/engine/html/path/path_windings.dart index 9b2b4689ea740..380110d8ac0fa 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_windings.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_windings.dart @@ -2,7 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_iterator.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; /// Computes winding number and onCurveCount for a path and point. class PathWinding { From c60e072df5852ca57ab2704b0b02e0fd0e30c52e Mon Sep 17 00:00:00 2001 From: ferhatb Date: Fri, 7 May 2021 11:31:42 -0700 Subject: [PATCH 12/12] add vertex_shaders to licenses --- ci/licenses_golden/licenses_flutter | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2d4afaff7f585..0ab31ecc860a7 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -514,6 +514,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shader_mask.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart