From 041d5246f7ea9ea43cc869770c7544e2566dd3d3 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Mon, 3 Feb 2025 15:01:15 -0500 Subject: [PATCH] [web] Remove HTML from the engine's test suites (#162404) - Remove html bundles/suites from configs and CI. - Delete html test files. - Replace the `isHtml` helper with `false` and refactor accordingly. --- .../ci/builders/linux_web_engine_test.json | 128 -- .../flutter/lib/web_ui/dev/felt_config.dart | 2 +- .../lib/web_ui/test/common/matchers.dart | 14 + .../flutter_view_manager_test.dart | 1 - .../flutter/lib/web_ui/test/felt_config.yaml | 48 - .../test/html/bitmap_canvas_golden_test.dart | 291 ----- .../html/canvas_clip_path_golden_test.dart | 211 ---- .../test/html/canvas_context_golden_test.dart | 79 -- .../test/html/canvas_reuse_golden_test.dart | 112 -- .../lib/web_ui/test/html/canvas_test.dart | 87 -- .../html/canvas_winding_rule_golden_test.dart | 79 -- .../web_ui/test/html/clip_op_golden_test.dart | 90 -- .../backdrop_filter_golden_test.dart | 191 --- .../compositing/canvas_blend_golden_test.dart | 121 -- .../canvas_image_blend_mode_golden_test.dart | 174 --- .../canvas_image_filter_golden_test.dart | 50 - .../canvas_mask_filter_golden_test.dart | 160 --- .../compositing/color_filter_golden_test.dart | 128 -- .../compositing/compositing_golden_test.dart | 683 ----------- .../dom_mask_filter_golden_test.dart | 30 - .../web_ui/test/html/deprecation_test.dart | 37 - .../lib/web_ui/test/html/dom_canvas_test.dart | 103 -- .../html/drawing/canvas_arc_golden_test.dart | 132 -- .../canvas_draw_color_golden_test.dart | 83 -- .../canvas_draw_image_golden_test.dart | 836 ------------- .../canvas_draw_picture_golden_test.dart | 131 -- .../drawing/canvas_lines_golden_test.dart | 175 --- .../html/drawing/canvas_rect_golden_test.dart | 74 -- .../drawing/canvas_rrect_golden_test.dart | 119 -- .../canvas_stroke_joins_golden_test.dart | 84 -- .../canvas_stroke_rects_golden_test.dart | 77 -- .../test/html/drawing/conic_golden_test.dart | 99 -- .../drawing/dom_clip_stroke_golden_test.dart | 139 --- .../drawing/draw_vertices_golden_test.dart | 461 ------- .../lib/web_ui/test/html/image_test.dart | 181 --- .../test/html/path_metrics_golden_test.dart | 176 --- .../lib/web_ui/test/html/path_ref_test.dart | 102 -- .../lib/web_ui/test/html/path_test.dart | 596 --------- .../test/html/path_to_svg_golden_test.dart | 229 ---- .../test/html/path_transform_golden_test.dart | 202 ---- .../web_ui/test/html/picture_golden_test.dart | 44 - .../html/recording_canvas_golden_test.dart | 777 ------------ .../test/html/recording_canvas_test.dart | 324 ----- .../test/html/resource_manager_test.dart | 86 -- .../lib/web_ui/test/html/screenshot.dart | 70 -- .../html/shaders/gradient_golden_test.dart | 921 -------------- .../shaders/image_shader_golden_test.dart | 137 --- .../shaders/linear_gradient_golden_test.dart | 154 --- .../shaders/radial_gradient_golden_test.dart | 128 -- .../html/shaders/shader_mask_golden_test.dart | 248 ---- .../web_ui/test/html/shadow_golden_test.dart | 136 --- .../html/surface/path/path_iterator_test.dart | 90 -- .../html/surface/path/path_winding_test.dart | 473 -------- .../test/html/surface/platform_view_test.dart | 93 -- .../test/html/surface/scene_builder_test.dart | 961 --------------- .../shaders/normalized_gradient_test.dart | 133 -- .../surface/shaders/shader_builder_test.dart | 217 ---- .../test/html/surface/surface_test.dart | 502 -------- .../lib/web_ui/test/html/testimage.dart | 122 -- .../lib/web_ui/test/ui/canvas_test.dart | 35 +- .../test/ui/draw_atlas_golden_test.dart | 6 +- .../test/ui/fallback_fonts_golden_test.dart | 1069 ++++++++--------- .../lib/web_ui/test/ui/filters_test.dart | 89 +- .../web_ui/test/ui/font_collection_test.dart | 22 +- .../web_ui/test/ui/fragment_shader_test.dart | 2 +- .../web_ui/test/ui/gradient_golden_test.dart | 3 +- .../image/html_image_element_codec_test.dart | 22 +- .../lib/web_ui/test/ui/image_golden_test.dart | 53 +- .../lib/web_ui/test/ui/line_metrics_test.dart | 5 +- .../test/ui/paragraph_builder_test.dart | 37 +- .../flutter/lib/web_ui/test/ui/path_test.dart | 16 +- .../lib/web_ui/test/ui/renderer_test.dart | 3 +- .../web_ui/test/ui/scene_builder_test.dart | 110 +- .../src/flutter/lib/web_ui/test/ui/utils.dart | 3 - .../lib/web_ui/test/ui/vertices_test.dart | 2 +- 75 files changed, 686 insertions(+), 13122 deletions(-) delete mode 100644 engine/src/flutter/lib/web_ui/test/html/bitmap_canvas_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/canvas_clip_path_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/canvas_context_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/canvas_reuse_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/canvas_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/clip_op_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/color_filter_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/compositing_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/deprecation_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/dom_canvas_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/conic_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/image_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/path_metrics_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/path_ref_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/path_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/path_to_svg_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/path_transform_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/picture_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/recording_canvas_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/recording_canvas_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/resource_manager_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/screenshot.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shaders/gradient_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shaders/image_shader_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/shadow_golden_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/path/path_iterator_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/path/path_winding_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/platform_view_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/scene_builder_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/surface/surface_test.dart delete mode 100644 engine/src/flutter/lib/web_ui/test/html/testimage.dart diff --git a/engine/src/flutter/ci/builders/linux_web_engine_test.json b/engine/src/flutter/ci/builders/linux_web_engine_test.json index 1fe86356eb..839bbae2f3 100644 --- a/engine/src/flutter/ci/builders/linux_web_engine_test.json +++ b/engine/src/flutter/ci/builders/linux_web_engine_test.json @@ -2,50 +2,6 @@ "_comment": "THIS IS A GENERATED FILE. Do not edit this file directly.", "_comment2": "See `generate_builder_json.dart` for the generator code", "builds": [ - { - "name": "web_tests/test_bundles/dart2js-html-html", - "drone_dimensions": [ - "device_type=none", - "os=Linux" - ], - "generators": { - "tasks": [ - { - "name": "compile bundle dart2js-html-html", - "parameters": [ - "test", - "--compile", - "--bundle=dart2js-html-html" - ], - "scripts": [ - "flutter/lib/web_ui/dev/felt" - ] - } - ] - } - }, - { - "name": "web_tests/test_bundles/dart2js-html-ui", - "drone_dimensions": [ - "device_type=none", - "os=Linux" - ], - "generators": { - "tasks": [ - { - "name": "compile bundle dart2js-html-ui", - "parameters": [ - "test", - "--compile", - "--bundle=dart2js-html-ui" - ], - "scripts": [ - "flutter/lib/web_ui/dev/felt" - ] - } - ] - } - }, { "name": "web_tests/test_bundles/dart2js-canvaskit-engine", "drone_dimensions": [ @@ -209,8 +165,6 @@ "download_jdk": false }, "dependencies": [ - "web_tests/test_bundles/dart2js-html-html", - "web_tests/test_bundles/dart2js-html-ui", "web_tests/test_bundles/dart2js-canvaskit-engine", "web_tests/test_bundles/dart2js-canvaskit-canvaskit", "web_tests/test_bundles/dart2js-canvaskit-ui", @@ -234,27 +188,19 @@ "parameters": [ "test", "--copy-artifacts", - "--suite=chrome-dart2js-html-html", - "--suite=chrome-dart2js-html-ui", "--suite=chrome-dart2js-canvaskit-engine", "--suite=chrome-dart2js-canvaskit-canvaskit", "--suite=chrome-dart2js-canvaskit-ui", "--suite=chrome-full-dart2js-canvaskit-canvaskit", "--suite=chrome-full-dart2js-canvaskit-ui", - "--suite=edge-dart2js-html-html", - "--suite=edge-dart2js-html-ui", "--suite=edge-dart2js-canvaskit-engine", "--suite=edge-dart2js-canvaskit-canvaskit", "--suite=edge-dart2js-canvaskit-ui", "--suite=edge-full-dart2js-canvaskit-canvaskit", "--suite=edge-full-dart2js-canvaskit-ui", - "--suite=firefox-dart2js-html-html", - "--suite=firefox-dart2js-html-ui", "--suite=firefox-dart2js-canvaskit-engine", "--suite=firefox-dart2js-canvaskit-canvaskit", "--suite=firefox-dart2js-canvaskit-ui", - "--suite=safari-dart2js-html-html", - "--suite=safari-dart2js-html-ui", "--suite=safari-dart2js-canvaskit-engine", "--suite=safari-dart2js-canvaskit-canvaskit", "--suite=safari-dart2js-canvaskit-ui", @@ -269,24 +215,6 @@ ], "script": "flutter/lib/web_ui/dev/felt" }, - { - "name": "run suite chrome-dart2js-html-html", - "parameters": [ - "test", - "--run", - "--suite=chrome-dart2js-html-html" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, - { - "name": "run suite chrome-dart2js-html-ui", - "parameters": [ - "test", - "--run", - "--suite=chrome-dart2js-html-ui" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, { "name": "run suite chrome-dart2js-canvaskit-engine", "parameters": [ @@ -400,8 +328,6 @@ "download_jdk": false }, "dependencies": [ - "web_tests/test_bundles/dart2js-html-html", - "web_tests/test_bundles/dart2js-html-ui", "web_tests/test_bundles/dart2js-canvaskit-engine", "web_tests/test_bundles/dart2js-canvaskit-canvaskit", "web_tests/test_bundles/dart2js-canvaskit-ui", @@ -423,27 +349,19 @@ "parameters": [ "test", "--copy-artifacts", - "--suite=chrome-dart2js-html-html", - "--suite=chrome-dart2js-html-ui", "--suite=chrome-dart2js-canvaskit-engine", "--suite=chrome-dart2js-canvaskit-canvaskit", "--suite=chrome-dart2js-canvaskit-ui", "--suite=chrome-full-dart2js-canvaskit-canvaskit", "--suite=chrome-full-dart2js-canvaskit-ui", - "--suite=edge-dart2js-html-html", - "--suite=edge-dart2js-html-ui", "--suite=edge-dart2js-canvaskit-engine", "--suite=edge-dart2js-canvaskit-canvaskit", "--suite=edge-dart2js-canvaskit-ui", "--suite=edge-full-dart2js-canvaskit-canvaskit", "--suite=edge-full-dart2js-canvaskit-ui", - "--suite=firefox-dart2js-html-html", - "--suite=firefox-dart2js-html-ui", "--suite=firefox-dart2js-canvaskit-engine", "--suite=firefox-dart2js-canvaskit-canvaskit", "--suite=firefox-dart2js-canvaskit-ui", - "--suite=safari-dart2js-html-html", - "--suite=safari-dart2js-html-ui", "--suite=safari-dart2js-canvaskit-engine", "--suite=safari-dart2js-canvaskit-canvaskit", "--suite=safari-dart2js-canvaskit-ui", @@ -458,24 +376,6 @@ ], "script": "flutter/lib/web_ui/dev/felt" }, - { - "name": "run suite firefox-dart2js-html-html", - "parameters": [ - "test", - "--run", - "--suite=firefox-dart2js-html-html" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, - { - "name": "run suite firefox-dart2js-html-ui", - "parameters": [ - "test", - "--run", - "--suite=firefox-dart2js-html-ui" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, { "name": "run suite firefox-dart2js-canvaskit-engine", "parameters": [ @@ -527,8 +427,6 @@ "download_jdk": false }, "dependencies": [ - "web_tests/test_bundles/dart2js-html-html", - "web_tests/test_bundles/dart2js-html-ui", "web_tests/test_bundles/dart2js-canvaskit-engine", "web_tests/test_bundles/dart2js-canvaskit-canvaskit", "web_tests/test_bundles/dart2js-canvaskit-ui", @@ -546,27 +444,19 @@ "parameters": [ "test", "--copy-artifacts", - "--suite=chrome-dart2js-html-html", - "--suite=chrome-dart2js-html-ui", "--suite=chrome-dart2js-canvaskit-engine", "--suite=chrome-dart2js-canvaskit-canvaskit", "--suite=chrome-dart2js-canvaskit-ui", "--suite=chrome-full-dart2js-canvaskit-canvaskit", "--suite=chrome-full-dart2js-canvaskit-ui", - "--suite=edge-dart2js-html-html", - "--suite=edge-dart2js-html-ui", "--suite=edge-dart2js-canvaskit-engine", "--suite=edge-dart2js-canvaskit-canvaskit", "--suite=edge-dart2js-canvaskit-ui", "--suite=edge-full-dart2js-canvaskit-canvaskit", "--suite=edge-full-dart2js-canvaskit-ui", - "--suite=firefox-dart2js-html-html", - "--suite=firefox-dart2js-html-ui", "--suite=firefox-dart2js-canvaskit-engine", "--suite=firefox-dart2js-canvaskit-canvaskit", "--suite=firefox-dart2js-canvaskit-ui", - "--suite=safari-dart2js-html-html", - "--suite=safari-dart2js-html-ui", "--suite=safari-dart2js-canvaskit-engine", "--suite=safari-dart2js-canvaskit-canvaskit", "--suite=safari-dart2js-canvaskit-ui", @@ -581,24 +471,6 @@ ], "script": "flutter/lib/web_ui/dev/felt" }, - { - "name": "run suite safari-dart2js-html-html", - "parameters": [ - "test", - "--run", - "--suite=safari-dart2js-html-html" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, - { - "name": "run suite safari-dart2js-html-ui", - "parameters": [ - "test", - "--run", - "--suite=safari-dart2js-html-ui" - ], - "script": "flutter/lib/web_ui/dev/felt" - }, { "name": "run suite safari-dart2js-canvaskit-engine", "parameters": [ diff --git a/engine/src/flutter/lib/web_ui/dev/felt_config.dart b/engine/src/flutter/lib/web_ui/dev/felt_config.dart index 8cb3cb2862..7c96041f83 100644 --- a/engine/src/flutter/lib/web_ui/dev/felt_config.dart +++ b/engine/src/flutter/lib/web_ui/dev/felt_config.dart @@ -7,7 +7,7 @@ import 'package:yaml/yaml.dart'; enum Compiler { dart2js, dart2wasm } -enum Renderer { html, canvaskit, skwasm } +enum Renderer { canvaskit, skwasm } class CompileConfiguration { CompileConfiguration(this.name, this.compiler, this.renderer); diff --git a/engine/src/flutter/lib/web_ui/test/common/matchers.dart b/engine/src/flutter/lib/web_ui/test/common/matchers.dart index 1f8d540825..cfae19b378 100644 --- a/engine/src/flutter/lib/web_ui/test/common/matchers.dart +++ b/engine/src/flutter/lib/web_ui/test/common/matchers.dart @@ -512,3 +512,17 @@ class HtmlPatternMatcher extends Matcher { return mismatchDescription; } } + +Matcher listEqual(List source, {int tolerance = 0}) { + return predicate((List target) { + if (source.length != target.length) { + return false; + } + for (int i = 0; i < source.length; i += 1) { + if ((source[i] - target[i]).abs() > tolerance) { + return false; + } + } + return true; + }, source.toString()); +} diff --git a/engine/src/flutter/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart b/engine/src/flutter/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart index 66bf71909b..2618d7f5ac 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart @@ -10,7 +10,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import '../../common/matchers.dart'; -import '../../html/image_test.dart'; void main() { internalBootstrapBrowserTest(() => doTests); diff --git a/engine/src/flutter/lib/web_ui/test/felt_config.yaml b/engine/src/flutter/lib/web_ui/test/felt_config.yaml index 5836919a36..7681b1c8b0 100644 --- a/engine/src/flutter/lib/web_ui/test/felt_config.yaml +++ b/engine/src/flutter/lib/web_ui/test/felt_config.yaml @@ -1,10 +1,6 @@ # See the `README.md` in this directory for documentation on the structure of # this file. compile-configs: - - name: dart2js-html - compiler: dart2js - renderer: html - - name: dart2js-canvaskit compiler: dart2js renderer: canvaskit @@ -30,10 +26,6 @@ test-sets: - name: canvaskit directory: canvaskit - # Tests for html-renderer-specific functionality - - name: html - directory: html - # Tests for renderer functionality that can be run on any renderer - name: ui directory: ui @@ -43,14 +35,6 @@ test-sets: directory: fallbacks test-bundles: - - name: dart2js-html-html - test-set: html - compile-configs: dart2js-html - - - name: dart2js-html-ui - test-set: ui - compile-configs: dart2js-html - - name: dart2js-canvaskit-engine test-set: engine compile-configs: dart2js-canvaskit @@ -114,14 +98,6 @@ run-configs: canvaskit-variant: full test-suites: - - name: chrome-dart2js-html-html - test-bundle: dart2js-html-html - run-config: chrome - - - name: chrome-dart2js-html-ui - test-bundle: dart2js-html-ui - run-config: chrome - - name: chrome-dart2js-canvaskit-engine test-bundle: dart2js-canvaskit-engine run-config: chrome @@ -147,14 +123,6 @@ test-suites: run-config: chrome-full artifact-deps: [ canvaskit ] - - name: edge-dart2js-html-html - test-bundle: dart2js-html-html - run-config: edge - - - name: edge-dart2js-html-ui - test-bundle: dart2js-html-ui - run-config: edge - - name: edge-dart2js-canvaskit-engine test-bundle: dart2js-canvaskit-engine run-config: edge @@ -180,14 +148,6 @@ test-suites: run-config: edge-full artifact-deps: [ canvaskit ] - - name: firefox-dart2js-html-html - test-bundle: dart2js-html-html - run-config: firefox - - - name: firefox-dart2js-html-ui - test-bundle: dart2js-html-ui - run-config: firefox - - name: firefox-dart2js-canvaskit-engine test-bundle: dart2js-canvaskit-engine run-config: firefox @@ -203,14 +163,6 @@ test-suites: run-config: firefox artifact-deps: [ canvaskit ] - - name: safari-dart2js-html-html - test-bundle: dart2js-html-html - run-config: safari - - - name: safari-dart2js-html-ui - test-bundle: dart2js-html-ui - run-config: safari - - name: safari-dart2js-canvaskit-engine test-bundle: dart2js-canvaskit-engine run-config: safari diff --git a/engine/src/flutter/lib/web_ui/test/html/bitmap_canvas_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/bitmap_canvas_golden_test.dart deleted file mode 100644 index 4d17b2aac9..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/bitmap_canvas_golden_test.dart +++ /dev/null @@ -1,291 +0,0 @@ -// 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:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; -import 'paragraph/helper.dart'; - -DomElement get sceneHost => - EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost.querySelector( - DomManager.sceneHostTagName, - )!; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 500, 100); - - late BitmapCanvas canvas; - - void appendToScene() { - // Create a element to make sure our CSS reset applies correctly. - final DomElement testScene = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - testScene.style.position = 'absolute'; - testScene.style.transformOrigin = '0 0 0'; - testScene.style.transform = 'scale(0.3)'; - } - testScene.append(canvas.rootElement); - sceneHost.append(testScene); - } - - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - tearDown(() { - sceneHost.querySelector('flt-scene')?.remove(); - }); - - /// Draws several lines, some aligned precisely with the pixel grid, and some - /// that are offset by 0.5 vertically or horizontally. - /// - /// The produced picture stresses the antialiasing generated by the browser - /// when positioning and rasterizing `` tags. Aliasing artifacts can - /// be seen depending on pixel alignment and whether antialiasing happens - /// before or after rasterization. - void drawMisalignedLines(BitmapCanvas canvas) { - final SurfacePaintData linePaint = - (SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1) - .paintData; - - final SurfacePaintData fillPaint = (SurfacePaint()..style = PaintingStyle.fill).paintData; - - canvas.translate(10, 10); - - canvas.drawRect(const Rect.fromLTWH(0, 0, 40, 40), linePaint); - - canvas.drawLine(const Offset(10, 0), const Offset(10, 40), linePaint); - - canvas.drawLine(const Offset(20.5, 0), const Offset(20, 40), linePaint); - - canvas.drawCircle(const Offset(30, 10), 3, fillPaint); - canvas.drawCircle(const Offset(30.5, 30), 3, fillPaint); - } - - test('renders pixels that are not aligned inside the canvas', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 60, 60), RenderStrategy()); - - drawMisalignedLines(canvas); - - appendToScene(); - - await matchGoldenFile('misaligned_pixels_in_canvas_test.png', region: region); - }); - - test('compensates for misalignment of the canvas', () async { - // Notice the 0.5 offset in the bounds rectangle. It's what causes the - // misalignment of canvas relative to the pixel grid. BitmapCanvas will - // shift its position back to 0.0 and at the same time it will it will - // compensate by shifting the contents of the canvas in the opposite - // direction. - canvas = BitmapCanvas(const Rect.fromLTWH(0.5, 0.5, 60, 60), RenderStrategy()); - canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); - drawMisalignedLines(canvas); - - appendToScene(); - - await matchGoldenFile('misaligned_canvas_test.png', region: region); - }); - - test('fill the whole canvas with color even when transformed', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), RenderStrategy()); - canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); - canvas.translate(25, 25); - canvas.drawColor(const Color.fromRGBO(0, 255, 0, 1.0), BlendMode.src); - - appendToScene(); - - await matchGoldenFile('bitmap_canvas_fills_color_when_transformed.png', region: region); - }); - - test('fill the whole canvas with paint even when transformed', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), RenderStrategy()); - canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); - canvas.translate(25, 25); - canvas.drawPaint( - SurfacePaintData() - ..color = const Color.fromRGBO(0, 255, 0, 1.0).value - ..style = PaintingStyle.fill, - ); - - appendToScene(); - - await matchGoldenFile('bitmap_canvas_fills_paint_when_transformed.png', region: region); - }); - - // This test reproduces text blurriness when two pieces of text appear inside - // two nested clips: - // - // ┌───────────────────────┐ - // │ text in outer clip │ - // │ ┌────────────────────┐│ - // │ │ text in inner clip ││ - // │ └────────────────────┘│ - // └───────────────────────┘ - // - // This test clips using canvas. See a similar test in `compositing_golden_test.dart`, - // which clips using layers. - // - // More details: https://github.com/flutter/flutter/issues/32274 - test('renders clipped DOM text with high quality', () async { - final CanvasParagraph paragraph = - (ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto'))..addText('Am I blurry?')).build() - as CanvasParagraph; - paragraph.layout(const ParagraphConstraints(width: 1000)); - - final Rect canvasSize = Rect.fromLTRB( - 0, - 0, - paragraph.maxIntrinsicWidth + 16, - 2 * paragraph.height + 32, - ); - final Rect outerClip = Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); - final Rect innerClip = Rect.fromLTRB( - 0.5, - canvasSize.bottom / 2 + 0.5, - canvasSize.right, - canvasSize.bottom, - ); - - canvas = BitmapCanvas(canvasSize, RenderStrategy()); - canvas.debugChildOverdraw = true; - canvas.clipRect(outerClip, ClipOp.intersect); - canvas.drawParagraph(paragraph, const Offset(8.5, 8.5)); - canvas.clipRect(innerClip, ClipOp.intersect); - canvas.drawParagraph(paragraph, Offset(8.5, 8.5 + innerClip.top)); - - expect( - canvas.rootElement - .querySelectorAll('flt-paragraph') - .map((DomElement e) => e.innerText) - .toList(), - ['Am I blurry?', 'Am I blurry?'], - reason: 'Expected to render text using HTML', - ); - - appendToScene(); - - await matchGoldenFile('bitmap_canvas_draws_high_quality_text.png', region: canvasSize); - }, testOn: 'chrome'); - - // NOTE: Chrome in --headless mode does not reproduce the bug that this test - // attempts to reproduce. However, it's still good to have this test - // for potential future regressions related to paint order. - test('draws text on top of canvas when transformed and clipped', () async { - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle(fontFamily: 'Ahem', fontSize: 18), - ); - - const String text = - 'This text is intentionally very long to make sure that it ' - 'breaks into multiple lines.'; - builder.addText(text); - - final CanvasParagraph paragraph = builder.build() as CanvasParagraph; - paragraph.layout(const ParagraphConstraints(width: 100)); - - final Rect canvasSize = Offset.zero & const Size(500, 500); - - canvas = BitmapCanvas(canvasSize, RenderStrategy()); - canvas.debugChildOverdraw = true; - - final SurfacePaintData pathPaint = - SurfacePaintData() - ..color = 0xFF7F7F7F - ..style = PaintingStyle.fill; - - const double r = 200.0; - const double l = 50.0; - - final Path path = (Path() - ..moveTo(-l, -l) - ..lineTo(0, -r) - ..lineTo(l, -l) - ..lineTo(r, 0) - ..lineTo(l, l) - ..lineTo(0, r) - ..lineTo(-l, l) - ..lineTo(-r, 0) - ..close()) - .shift(const Offset(250, 250)); - - final SurfacePaintData borderPaint = - SurfacePaintData() - ..color = black.value - ..style = PaintingStyle.stroke; - - canvas.drawPath(path, pathPaint); - canvas.drawParagraph(paragraph, const Offset(180, 50)); - canvas.drawRect(Rect.fromLTWH(180, 50, paragraph.width, paragraph.height), borderPaint); - - expect( - canvas.rootElement - .querySelectorAll('flt-paragraph') - .map((DomElement e) => e.text) - .toList(), - [text], - reason: 'Expected to render text using HTML', - ); - - final SceneBuilder sb = SceneBuilder(); - sb.pushTransform( - Matrix4.diagonal3Values( - EngineFlutterDisplay.instance.browserDevicePixelRatio, - EngineFlutterDisplay.instance.browserDevicePixelRatio, - 1.0, - ).toFloat64(), - ); - sb.pushTransform(Matrix4.rotationZ(math.pi / 2).toFloat64()); - sb.pushOffset(0, -500); - sb.pushClipRect(canvasSize); - sb.pop(); - sb.pop(); - sb.pop(); - sb.pop(); - final SurfaceScene scene = sb.build() as SurfaceScene; - final DomElement sceneElement = scene.webOnlyRootElement!; - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - - sceneElement.querySelector('flt-clip')!.append(canvas.rootElement); - sceneHost.append(sceneElement); - - await matchGoldenFile('bitmap_canvas_draws_text_on_top_of_canvas.png', region: canvasSize); - }); - - // Regression test for https://github.com/flutter/flutter/issues/96498. When - // a picture is made of just text that can be rendered using plain HTML, - // BitmapCanvas should not create any elements as they are expensive. - test('does not allocate bitmap canvas just for text', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), RenderStrategy()); - - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto')); - builder.addText('Hello'); - final CanvasParagraph paragraph = builder.build() as CanvasParagraph; - paragraph.layout(const ParagraphConstraints(width: 1000)); - - canvas.drawParagraph(paragraph, const Offset(8.5, 8.5)); - expect(canvas.rootElement.querySelectorAll('canvas'), isEmpty); - expect(canvas.rootElement.querySelectorAll('flt-paragraph').single.innerText, 'Hello'); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/canvas_clip_path_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/canvas_clip_path_golden_test.dart deleted file mode 100644 index 0c51dc5cee..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/canvas_clip_path_golden_test.dart +++ /dev/null @@ -1,211 +0,0 @@ -// 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:js_util' as js_util; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' as engine; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import '../common/test_initialization.dart'; -import 'screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - // Regression test for https://github.com/flutter/flutter/issues/48683 - // Should clip image with oval. - test('Clips image with oval clip path', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - final Path path = Path(); - path.addOval(Rect.fromLTWH(100, 30, testWidth, testHeight)); - rc.clipPath(path); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTWH(100, 30, testWidth, testHeight), - engine.SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'image_clipped_by_oval'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/48683 - test('Clips triangle with oval clip path', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.save(); - const double testWidth = 200; - const double testHeight = 150; - final Path path = Path(); - path.addOval(const Rect.fromLTWH(100, 30, testWidth, testHeight)); - rc.clipPath(path); - final Path paintPath = Path(); - paintPath.moveTo(testWidth / 2, 0); - paintPath.lineTo(testWidth, testHeight); - paintPath.lineTo(0, testHeight); - paintPath.close(); - rc.drawPath( - paintPath, - engine.SurfacePaint() - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill, - ); - rc.restore(); - await canvasScreenshot(rc, 'triangle_clipped_by_oval'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/78782 - test('Clips on Safari when clip bounds off screen', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.save(); - const double testWidth = 200; - const double testHeight = 150; - - final Path paintPath = Path(); - paintPath.addRect(const Rect.fromLTWH(-50, 0, testWidth, testHeight)); - paintPath.close(); - rc.drawPath( - paintPath, - engine.SurfacePaint() - ..color = const Color(0xFF000000) - ..style = PaintingStyle.stroke, - ); - - final Path path = Path(); - path.moveTo(-200, 0); - path.lineTo(100, 75); - path.lineTo(-200, 150); - path.close(); - rc.clipPath(path); - rc.drawImageRect( - createTestImage(), - const Rect.fromLTRB(0, 0, testWidth, testHeight), - const Rect.fromLTWH(-50, 0, testWidth, testHeight), - engine.SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot( - rc, - 'image_clipped_by_triangle_off_screen', - region: const Rect.fromLTWH(0, 0, 600, 800), - ); - }); - - // Tests oval clipping using border radius 50%. - test('Clips against oval', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.save(); - const double testWidth = 200; - const double testHeight = 150; - - final Path paintPath = Path(); - paintPath.addRect(const Rect.fromLTWH(-50, 0, testWidth, testHeight)); - paintPath.close(); - rc.drawPath( - paintPath, - engine.SurfacePaint() - ..color = const Color(0xFF000000) - ..style = PaintingStyle.stroke, - ); - - final Path path = Path(); - path.addOval(const Rect.fromLTRB(-200, 0, 100, 150)); - rc.clipPath(path); - rc.drawImageRect( - createTestImage(), - const Rect.fromLTRB(0, 0, testWidth, testHeight), - const Rect.fromLTWH(-50, 0, testWidth, testHeight), - engine.SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot( - rc, - 'image_clipped_by_oval_path', - region: const Rect.fromLTWH(0, 0, 600, 800), - ); - }); - - test('Clips with fillType evenOdd', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.save(); - const double testWidth = 400; - const double testHeight = 350; - - // draw RGB test image - rc.drawImageRect( - createTestImage(), - const Rect.fromLTRB(0, 0, testWidth, testHeight), - const Rect.fromLTWH(0, 0, testWidth, testHeight), - engine.SurfacePaint(), - ); - - // draw a clipping path with: - // 1) an outside larger rectangle - // 2) a smaller inner rectangle specified by a path - final Path path = Path(); - path.addRect(const Rect.fromLTWH(0, 0, testWidth, testHeight)); - const double left = 25; - const double top = 30; - const double right = 300; - const double bottom = 250; - path - ..moveTo(left, top) - ..lineTo(right, top) - ..lineTo(right, bottom) - ..lineTo(left, bottom) - ..close(); - path.fillType = PathFillType.evenOdd; - rc.clipPath(path); - - // draw an orange paint path of size testWidth and testHeight - final Path paintPath = Path(); - paintPath.addRect(const Rect.fromLTWH(0, 0, testWidth, testHeight)); - paintPath.close(); - rc.drawPath( - paintPath, - engine.SurfacePaint() - ..color = const Color(0xFFFF9800) - ..style = PaintingStyle.fill, - ); - rc.restore(); - - // when fillType is set to evenOdd from the clipping path, expect the inner - // rectangle should clip some of the orange painted portion, revealing the RGB testImage - await canvasScreenshot( - rc, - 'clipPath_uses_fillType_evenOdd', - region: const Rect.fromLTWH(0, 0, 600, 800), - ); - }); -} - -engine.HtmlImage createTestImage({int width = 200, int height = 150}) { - final engine.DomCanvasElement canvas = engine.createDomCanvasElement( - width: width, - height: height, - ); - final engine.DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(width / 3, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(2 * width / 3, 0, width / 3, height); - ctx.fill(); - final engine.DomHTMLImageElement imageElement = engine.createDomHTMLImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return engine.HtmlImage(imageElement, width, height); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/canvas_context_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/canvas_context_golden_test.dart deleted file mode 100644 index fbbd2dd733..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/canvas_context_golden_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' as engine; -import 'package:ui/ui.dart' hide TextStyle; - -import '../common/test_initialization.dart'; -import 'screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -/// Tests context save/restore. -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - setUpUnitTests(setUpTestViewDimensions: false); - - // Regression test for https://github.com/flutter/flutter/issues/49429 - // Should clip with correct transform. - test('Clips image with oval clip path', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - final engine.SurfacePaint paint = - Paint() as engine.SurfacePaint - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill; - rc.save(); - final Path ovalPath = Path(); - ovalPath.addOval(const Rect.fromLTWH(100, 30, 200, 100)); - rc.clipPath(ovalPath); - rc.translate(-500, -500); - rc.save(); - rc.translate(500, 500); - rc.drawPath(ovalPath, paint); - // The line below was causing SaveClipStack to incorrectly set - // transform before path painting. - rc.translate(-1000, -1000); - rc.save(); - rc.restore(); - rc.restore(); - rc.restore(); - // The rectangle should paint without clipping since we restored - // context. - rc.drawRect(const Rect.fromLTWH(0, 0, 4, 200), paint); - await canvasScreenshot(rc, 'context_save_restore_transform', canvasRect: screenRect); - }); - - test('Should restore clip path', () async { - final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - final Paint goodPaint = - Paint() - ..color = const Color(0x8000FF00) - ..style = PaintingStyle.fill; - final Paint badPaint = - Paint() - ..color = const Color(0xFFFF0000) - ..style = PaintingStyle.fill; - rc.save(); - final Path ovalPath = Path(); - ovalPath.addOval(const Rect.fromLTWH(100, 30, 200, 100)); - rc.clipPath(ovalPath); - rc.translate(-500, -500); - rc.save(); - rc.restore(); - // The rectangle should be clipped against oval. - rc.drawRect(const Rect.fromLTWH(0, 0, 300, 300), badPaint as engine.SurfacePaint); - rc.restore(); - // The rectangle should paint without clipping since we restored - // context. - rc.drawRect(const Rect.fromLTWH(0, 0, 200, 200), goodPaint as engine.SurfacePaint); - await canvasScreenshot(rc, 'context_save_restore_clip', canvasRect: screenRect); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/canvas_reuse_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/canvas_reuse_golden_test.dart deleted file mode 100644 index 97def468e8..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/canvas_reuse_golden_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - final SurfacePaint testPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFFFF00FF); - - setUpUnitTests(); - - // Regression test for https://github.com/flutter/flutter/issues/51514 - test("Canvas is reused and z-index doesn't leak across paints", () async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect, RenderStrategy()); - const Rect region = Rect.fromLTWH(0, 0, 500, 500); - - // Draw first frame into engine canvas. - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - final Path path = - Path() - ..moveTo(3, 0) - ..lineTo(100, 97); - rc.drawPath(path, testPaint); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - engineCanvas.endOfPaint(); - - DomElement sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - sceneElement.append(engineCanvas.rootElement); - domDocument.body!.append(sceneElement); - - final DomCanvasElement canvas = domDocument.querySelector('canvas')! as DomCanvasElement; - // ! Since canvas is first element, it should have zIndex = -1 for correct - // paint order. - expect(canvas.style.zIndex, '-1'); - - // Add id to canvas element to test for reuse. - const String kTestId = 'test-id-5698'; - canvas.id = kTestId; - - sceneElement.remove(); - // Clear so resources are marked for reuse. - - engineCanvas.clear(); - - // Now paint a second scene to same [BitmapCanvas] but paint an image - // before the path to move canvas element into second position. - final RecordingCanvas rc2 = RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - final Path path2 = - Path() - ..moveTo(3, 0) - ..quadraticBezierTo(100, 0, 100, 100); - rc2.drawImage(_createRealTestImage(), Offset.zero, SurfacePaint()); - rc2.drawPath(path2, testPaint); - rc2.endRecording(); - rc2.apply(engineCanvas, screenRect); - - sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - sceneElement.append(engineCanvas.rootElement); - domDocument.body!.append(sceneElement); - - final DomCanvasElement canvas2 = domDocument.querySelector('canvas')! as DomCanvasElement; - // ZIndex should have been cleared since we have image element preceding - // canvas. - expect(canvas.style.zIndex != '-1', isTrue); - expect(canvas2.id, kTestId); - await matchGoldenFile('bitmap_canvas_reuse_zindex.png', region: region); - }); -} - -const String _base64Encoded20x20TestImage = - 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' - 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' - 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; - -HtmlImage _createRealTestImage() { - return HtmlImage( - createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', - 20, - 20, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/canvas_test.dart b/engine/src/flutter/lib/web_ui/test/html/canvas_test.dart deleted file mode 100644 index c1efa489e3..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/canvas_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; - -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import '../common/mock_engine_canvas.dart'; -import '../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - await bootstrapAndRunApp(withImplicitView: true); - - group('EngineCanvas', () { - late MockEngineCanvas mockCanvas; - late ui.Paragraph paragraph; - - void testCanvas( - String description, - void Function(EngineCanvas canvas) testFn, { - ui.Rect canvasSize = const ui.Rect.fromLTWH(0, 0, 100, 100), - ui.VoidCallback? whenDone, - }) { - test(description, () { - testFn(BitmapCanvas(canvasSize, RenderStrategy())); - testFn(DomCanvas(domDocument.createElement('flt-picture'))); - testFn(mockCanvas = MockEngineCanvas()); - whenDone?.call(); - }); - } - - testCanvas( - 'draws laid out paragraph', - (EngineCanvas canvas) { - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 100, 100); - final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); - builder.addText('sample'); - paragraph = builder.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 100)); - recordingCanvas.drawParagraph(paragraph, const ui.Offset(10, 10)); - recordingCanvas.endRecording(); - canvas.clear(); - recordingCanvas.apply(canvas, screenRect); - }, - whenDone: () { - expect(mockCanvas.methodCallLog, hasLength(3)); - - MockCanvasCall call = mockCanvas.methodCallLog[0]; - expect(call.methodName, 'clear'); - - call = mockCanvas.methodCallLog[1]; - expect(call.methodName, 'drawParagraph'); - final Map arguments = call.arguments as Map; - expect(arguments['paragraph'], paragraph); - expect(arguments['offset'], const ui.Offset(10, 10)); - }, - ); - - testCanvas( - 'ignores paragraphs that were not laid out', - (EngineCanvas canvas) { - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 100, 100); - final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); - builder.addText('sample'); - final ui.Paragraph paragraph = builder.build(); - recordingCanvas.drawParagraph(paragraph, const ui.Offset(10, 10)); - recordingCanvas.endRecording(); - canvas.clear(); - recordingCanvas.apply(canvas, screenRect); - }, - whenDone: () { - expect(mockCanvas.methodCallLog, hasLength(2)); - expect(mockCanvas.methodCallLog[0].methodName, 'clear'); - expect(mockCanvas.methodCallLog[1].methodName, 'endOfPaint'); - }, - ); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart deleted file mode 100644 index 815d54bbe4..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 500, 500); - - late BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws paths using nonzero and evenodd winding rules', () async { - paintPaths(canvas); - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_path_winding.png', region: region); - }); -} - -void paintPaths(BitmapCanvas canvas) { - canvas.drawRect( - const Rect.fromLTRB(0, 0, 500, 500), - SurfacePaintData() - ..color = 0xFFFFFFFF - ..style = PaintingStyle.fill, - ); // white - - final SurfacePaint paintFill = - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF00B0FF); - final SurfacePaint paintStroke = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2 - ..color = const Color(0xFFE00000); - final Path path1 = - Path() - ..fillType = PathFillType.evenOdd - ..moveTo(50, 0) - ..lineTo(21, 90) - ..lineTo(98, 35) - ..lineTo(2, 35) - ..lineTo(79, 90) - ..close() - ..addRect(const Rect.fromLTWH(20, 100, 200, 50)) - ..addRect(const Rect.fromLTWH(40, 120, 160, 10)); - final Path path2 = - Path() - ..fillType = PathFillType.nonZero - ..moveTo(50, 200) - ..lineTo(21, 290) - ..lineTo(98, 235) - ..lineTo(2, 235) - ..lineTo(79, 290) - ..close() - ..addRect(const Rect.fromLTWH(20, 300, 200, 50)) - ..addRect(const Rect.fromLTWH(40, 320, 160, 10)); - canvas.drawPath(path1, paintFill.paintData); - canvas.drawPath(path2, paintFill.paintData); - canvas.drawPath(path1, paintStroke.paintData); - canvas.drawPath(path2, paintStroke.paintData); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/clip_op_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/clip_op_golden_test.dart deleted file mode 100644 index 6c30bf7903..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/clip_op_golden_test.dart +++ /dev/null @@ -1,90 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../common/test_initialization.dart'; -import 'paragraph/helper.dart'; -import 'screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - /// Regression test for https://github.com/flutter/flutter/issues/64734. - test('Clips using difference', () async { - const Offset shift = Offset(8, 8); - const Rect region = Rect.fromLTRB(0, 0, 400, 300); - final RecordingCanvas canvas = RecordingCanvas(region); - final Rect titleRect = const Rect.fromLTWH(20, 0, 50, 20).shift(shift); - final SurfacePaint paint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..color = const Color(0xff000000) - ..strokeWidth = 1; - canvas.save(); - try { - final Rect borderRect = Rect.fromLTRB(0, 10, region.width, region.height).shift(shift); - canvas.clipRect(titleRect, ClipOp.difference); - canvas.drawRect(borderRect, paint); - } finally { - canvas.restore(); - } - canvas.drawRect(titleRect, paint); - await canvasScreenshot( - canvas, - 'clip_op_difference', - region: const Rect.fromLTRB(0, 0, 420, 360), - ); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/86345 - test('Clips with zero width or height', () async { - const Rect region = Rect.fromLTRB(0, 0, 400, 300); - final RecordingCanvas canvas = RecordingCanvas(region); - - final SurfacePaint paint = - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xff00ff00); - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..color = const Color(0xffff0000) - ..strokeWidth = 1; - - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - final double x = 10 + i * 70; - final double y = 10 + j * 70; - canvas.save(); - // Clip. - canvas.clipRect(Rect.fromLTWH(x, y, i * 25, j * 25), ClipOp.intersect); - // Draw the blue (clipped) rect. - canvas.drawRect(Rect.fromLTWH(x, y, 50, 50), paint); - final Paragraph p = plain( - EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 34), - '23', - textStyle: EngineTextStyle.only(color: const Color(0xff0000ff)), - ); - p.layout(const ParagraphConstraints(width: double.infinity)); - canvas.drawParagraph(p, Offset(x, y)); - canvas.restore(); - // Draw the red border. - canvas.drawRect(Rect.fromLTWH(x, y, 50, 50), borderPaint); - } - } - await canvasScreenshot(canvas, 'clip_zero_width_height', region: region); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart deleted file mode 100644 index 10d13e8b61..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart +++ /dev/null @@ -1,191 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' hide BackdropFilterEngineLayer, ClipRectEngineLayer; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (final DomNode scene in domDocument.querySelectorAll('flt-scene')) { - scene.remove(); - } - }); - - // The black circle on the left should not be blurred since it is outside - // the clip boundary around backdrop filter. However there should be only - // one red dot since the other one should be blurred by filter. - test('Background should only blur at ancestor clip boundary', () async { - const Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - - builder.pushClipRect(const Rect.fromLTRB(10, 10, 180, 120)); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - builder.addPicture(Offset.zero, circles1); - - builder.pushClipRect(const Rect.fromLTRB(60, 10, 180, 120)); - builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0)); - final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); - builder.addPicture(Offset.zero, circles2); - builder.pop(); - builder.pop(); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('backdrop_filter_clip.png', region: region); - }); - - test('Background should only blur at ancestor clip boundary after move', () async { - const Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - final ClipRectEngineLayer clipEngineLayer = builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - builder.addPicture(Offset.zero, circles1); - final ClipRectEngineLayer clipEngineLayer2 = builder.pushClipRect( - const Rect.fromLTRB(60, 10, 180, 120), - ); - final BackdropFilterEngineLayer oldBackdropFilterLayer = builder.pushBackdropFilter( - ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - ); - final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); - builder.addPicture(Offset.zero, circles2); - builder.pop(); - builder.pop(); - builder.pop(); - builder.build(); - - // Now reparent filter layer in next scene. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.addPicture(Offset.zero, backgroundPicture); - builder2.pushClipRect(const Rect.fromLTRB(10, 10, 180, 120), oldLayer: clipEngineLayer); - builder2.addPicture(Offset.zero, circles1); - builder2.pushClipRect(const Rect.fromLTRB(10, 75, 180, 120), oldLayer: clipEngineLayer2); - builder2.pushBackdropFilter( - ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - oldLayer: oldBackdropFilterLayer, - ); - builder2.addPicture(Offset.zero, circles2); - builder2.pop(); - builder2.pop(); - builder2.pop(); - - domDocument.body!.append(builder2.build().webOnlyRootElement!); - - await matchGoldenFile('backdrop_filter_clip_moved.png', region: region); - }); - - // The blur filter should be applied to the background inside the clip even - // though there are no children of the backdrop filter. - test('Background should blur even if child does not paint', () async { - const Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - - builder.pushClipRect(const Rect.fromLTRB(10, 10, 180, 120)); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - builder.addPicture(Offset.zero, circles1); - - builder.pushClipRect(const Rect.fromLTRB(60, 10, 180, 120)); - builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0)); - builder.pop(); - builder.pop(); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('backdrop_filter_no_child_rendering.png', region: region); - }); - test('colorFilter as imageFilter', () async { - const Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - - builder.pushClipRect(const Rect.fromLTRB(10, 10, 180, 120)); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - - // current background color is light green, apply a light yellow colorFilter - const ColorFilter colorFilter = ColorFilter.mode(Color(0xFFFFFFB1), BlendMode.modulate); - builder.pushBackdropFilter(colorFilter); - builder.addPicture(Offset.zero, circles1); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('backdrop_filter_colorFilter_as_imageFilter.png', region: region); - }); -} - -Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(region); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), - 10, - SurfacePaint()..style = PaintingStyle.fill, - ); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), - 10, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1), - ); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), - 10, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1), - ); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), - 10, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1), - ); - return recorder.endRecording(); -} - -Picture _drawBackground(Rect region) { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(region); - canvas.drawRect( - region.deflate(8.0), - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFFE0FFE0), - ); - return recorder.endRecording(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart deleted file mode 100644 index 04b49b5f60..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart +++ /dev/null @@ -1,121 +0,0 @@ -// 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: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/ui.dart' hide TextStyle; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - test('Blend circles with difference and color', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawRect( - const Rect.fromLTRB(0, 0, 400, 400), - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(255, 255, 255, 255), - ); - rc.drawCircle( - const Offset(100, 100), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0) - ..blendMode = BlendMode.difference, - ); - - rc.drawCircle( - const Offset(170, 100), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..blendMode = BlendMode.color - ..color = const Color.fromARGB(128, 0, 255, 0), - ); - - rc.drawCircle( - const Offset(135, 170), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0), - ); - rc.restore(); - - await canvasScreenshot(rc, 'canvas_blend_circle_diff_color'); - }); - - test('Blend circle and text with multiply', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawRect( - const Rect.fromLTRB(0, 0, 400, 400), - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(255, 255, 255, 255), - ); - rc.drawCircle( - const Offset(100, 100), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0) - ..blendMode = BlendMode.difference, - ); - rc.drawCircle( - const Offset(170, 100), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..blendMode = BlendMode.color - ..color = const Color.fromARGB(128, 0, 255, 0), - ); - - rc.drawCircle( - const Offset(135, 170), - 80.0, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0), - ); - rc.drawImage( - createTestImage(), - const Offset(135.0, 130.0), - SurfacePaint()..blendMode = BlendMode.multiply, - ); - rc.restore(); - await canvasScreenshot(rc, 'canvas_blend_image_multiply'); - }); -} - -HtmlImage createTestImage() { - const int width = 100; - const int height = 50; - final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(33, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(66, 0, 33, 50); - ctx.fill(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart deleted file mode 100644 index a91bc152bb..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart +++ /dev/null @@ -1,174 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; -import '../testimage.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -SurfacePaint makePaint() => Paint() as SurfacePaint; - -Future testMain() async { - setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); - - const Color red = Color(0xFFFF0000); - const Color green = Color(0xFF00FF00); - const Color blue = Color(0xFF2196F3); - const Color white = Color(0xFFFFFFFF); - const Color grey = Color(0xFF808080); - const Color black = Color(0xFF000000); - - final List> modes = >[ - [ - BlendMode.clear, - BlendMode.src, - BlendMode.dst, - BlendMode.srcOver, - BlendMode.dstOver, - BlendMode.srcIn, - BlendMode.dstIn, - BlendMode.srcOut, - ], - [ - BlendMode.dstOut, - BlendMode.srcATop, - BlendMode.dstATop, - BlendMode.xor, - BlendMode.plus, - BlendMode.modulate, - BlendMode.screen, - BlendMode.overlay, - ], - [ - BlendMode.darken, - BlendMode.lighten, - BlendMode.colorDodge, - BlendMode.hardLight, - BlendMode.softLight, - BlendMode.difference, - BlendMode.exclusion, - BlendMode.multiply, - ], - [BlendMode.hue, BlendMode.saturation, BlendMode.color, BlendMode.luminosity], - ]; - - for (int blendGroup = 0; blendGroup < 4; ++blendGroup) { - test('Draw image with Group$blendGroup blend modes', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 400)); - rc.save(); - final List blendModes = modes[blendGroup]; - for (int row = 0; row < blendModes.length; row++) { - // draw white background for first 4, black for next 4 blends. - final double top = row * 50.0; - rc.drawRect(Rect.fromLTWH(0, top, 200, 50), makePaint()..color = white); - rc.drawRect(Rect.fromLTWH(200, top, 200, 50), makePaint()..color = grey); - final BlendMode blendMode = blendModes[row]; - rc.drawImage( - createFlutterLogoTestImage(), - Offset(0, top), - makePaint()..colorFilter = EngineColorFilter.mode(red, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(50, top), - makePaint()..colorFilter = EngineColorFilter.mode(green, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(100, top), - makePaint()..colorFilter = EngineColorFilter.mode(blue, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(150, top), - makePaint()..colorFilter = EngineColorFilter.mode(black, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(200, top), - makePaint()..colorFilter = EngineColorFilter.mode(red, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(250, top), - makePaint()..colorFilter = EngineColorFilter.mode(green, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(300, top), - makePaint()..colorFilter = EngineColorFilter.mode(blue, blendMode), - ); - rc.drawImage( - createFlutterLogoTestImage(), - Offset(350, top), - makePaint()..colorFilter = EngineColorFilter.mode(black, blendMode), - ); - } - rc.restore(); - await canvasScreenshot(rc, 'canvas_image_blend_group$blendGroup'); - }, skip: isSafari); - } - - // Regression test for https://github.com/flutter/flutter/issues/56971 - test('Draws image and paragraph at same vertical position', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 400)); - rc.save(); - rc.drawRect(const Rect.fromLTWH(0, 50, 200, 50), makePaint()..color = white); - rc.drawImage( - createFlutterLogoTestImage(), - const Offset(0, 50), - makePaint()..colorFilter = const EngineColorFilter.mode(red, BlendMode.srcIn), - ); - - final Paragraph paragraph = createTestParagraph(); - const double textLeft = 80.0; - const double textTop = 50.0; - const double widthConstraint = 300.0; - paragraph.layout(const ParagraphConstraints(width: widthConstraint)); - rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); - - rc.restore(); - await canvasScreenshot(rc, 'canvas_image_blend_and_text'); - }); - - test('Does not re-use styles with same image src', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 400)); - final HtmlImage flutterImage = createFlutterLogoTestImage(); - rc.save(); - rc.drawRect(const Rect.fromLTWH(0, 50, 200, 50), makePaint()..color = white); - rc.drawImage( - flutterImage, - const Offset(0, 50), - makePaint()..colorFilter = const EngineColorFilter.mode(red, BlendMode.modulate), - ); - - // Expect that the colorFilter is only applied to the first image, since the - // colorFilter is applied to a clone of the flutterImage and not the original - rc.drawImage(flutterImage, const Offset(0, 100), makePaint()); - - rc.restore(); - await canvasScreenshot(rc, 'canvas_image_same_src'); - }); -} - -Paragraph createTestParagraph() { - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - ), - ); - builder.addText('FOO'); - return builder.build(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart deleted file mode 100644 index c4b6f51a7a..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import '../screenshot.dart'; -import '../testimage.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -SurfacePaint makePaint() => Paint() as SurfacePaint; - -Future testMain() async { - const double screenWidth = 100.0; - const double screenHeight = 100.0; - const Rect region = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Regression test for https://github.com/flutter/flutter/issues/76966 - test('Draws image with dstATop color filter', () async { - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawImage( - createFlutterLogoTestImage(), - const Offset(10, 10), - makePaint()..colorFilter = const EngineColorFilter.mode(Color(0x40000000), BlendMode.dstATop), - ); - await canvasScreenshot(canvas, 'image_color_fiter_dstatop', region: region); - }); - - test('Draws image with matrix color filter', () async { - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawImage( - createFlutterLogoTestImage(), - const Offset(10, 10), - makePaint() - ..colorFilter = const EngineColorFilter.matrix([ - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0, 0, 0, 1, 0, // - ]), - ); - await canvasScreenshot(canvas, 'image_matrix_color_fiter', region: region); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart deleted file mode 100644 index d3571a096b..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart +++ /dev/null @@ -1,160 +0,0 @@ -// 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:math' as math; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - tearDown(() { - ContextStateHandle.debugEmulateWebKitMaskFilter = false; - }); - - // Regression test for https://github.com/flutter/flutter/issues/55930 - void testMaskFilterBlur({bool isWebkit = false}) { - final String browser = isWebkit ? 'Safari' : 'Chrome'; - - test('renders MaskFilter.blur in $browser', () async { - const double screenWidth = 800.0; - const double screenHeight = 150.0; - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - ContextStateHandle.debugEmulateWebKitMaskFilter = isWebkit; - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(0, 75); - - final SurfacePaint paint = - SurfacePaint()..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - rc.translate(50, 0); - rc.drawRect(ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), paint); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF00FF00); - rc.drawRRect( - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), - const ui.Radius.circular(20), - ), - paint, - ); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF0000FF); - rc.drawCircle(ui.Offset.zero, 30, paint); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF00FFFF); - rc.drawPath( - SurfacePath() - ..moveTo(-20, 0) - ..lineTo(0, -50) - ..lineTo(20, 0) - ..lineTo(0, 50) - ..close(), - paint, - ); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFFFF00FF); - rc.drawOval(ui.Rect.fromCenter(center: ui.Offset.zero, width: 40, height: 100), paint); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF888800); - paint.strokeWidth = 5; - rc.drawLine(const ui.Offset(-20, -50), const ui.Offset(20, 50), paint); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF888888); - rc.drawDRRect( - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 35), - const ui.Radius.circular(20), - ), - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 15), - const ui.Radius.circular(7), - ), - paint, - ); - - rc.translate(100, 0); - paint.color = const ui.Color(0xFF6500C9); - rc.drawRawPoints( - ui.PointMode.points, - Float32List.fromList([-10, -10, -10, 10, 10, -10, 10, 10]), - paint, - ); - - await canvasScreenshot(rc, 'mask_filter_$browser', region: screenRect); - }); - - test('renders transformed MaskFilter.blur in $browser', () async { - const double screenWidth = 300.0; - const double screenHeight = 300.0; - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - ContextStateHandle.debugEmulateWebKitMaskFilter = isWebkit; - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(150, 150); - - final SurfacePaint paint = - SurfacePaint()..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - const List colors = [ - ui.Color(0xFF000000), - ui.Color(0xFF00FF00), - ui.Color(0xFF0000FF), - ui.Color(0xFF00FFFF), - ui.Color(0xFFFF00FF), - ui.Color(0xFF888800), - ui.Color(0xFF888888), - ui.Color(0xFF6500C9), - ]; - - for (final ui.Color color in colors) { - paint.color = color; - rc.rotate(math.pi / 4); - rc.drawRect(ui.Rect.fromCircle(center: const ui.Offset(90, 0), radius: 20), paint); - } - - await canvasScreenshot(rc, 'mask_filter_transformed_$browser', region: screenRect); - }); - } - - testMaskFilterBlur(); - testMaskFilterBlur(isWebkit: true); - - for (final int testDpr in [1, 2, 4]) { - test('MaskFilter.blur blurs correctly for device-pixel ratio $testDpr', () async { - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(testDpr.toDouble()); - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 150, 150); - - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(0, 75); - - final SurfacePaint paint = - SurfacePaint()..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - rc.translate(75, 0); - rc.drawRect(ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), paint); - - await canvasScreenshot(rc, 'mask_filter_blur_dpr_$testDpr', region: screenRect); - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(1.0); - }); - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/color_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/color_filter_golden_test.dart deleted file mode 100644 index c7b033b9ad..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/color_filter_golden_test.dart +++ /dev/null @@ -1,128 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../../common/test_initialization.dart'; - -const Rect region = Rect.fromLTWH(0, 0, 500, 500); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (final DomNode scene in domDocument.querySelectorAll('flt-scene')) { - scene.remove(); - } - }); - - test('Should apply color filter to image', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(); - builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter(const EngineColorFilter.mode(Color(0xF0000080), BlendMode.color)); - final Picture circles1 = _drawTestPictureWithCircles(30, 30); - builder.addPicture(Offset.zero, circles1); - builder.pop(); - domDocument.body!.append(builder.build().webOnlyRootElement!); - - // TODO(ferhat): update golden for this test after canvas sandwich detection is - // added to RecordingCanvas. - await matchGoldenFile('color_filter_blendMode_color.png', region: region); - }); - - test('Should apply matrix color filter to image', () async { - final List colorMatrix = [ - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0, 0, 0, 1, 0, // - ]; - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(); - builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter(EngineColorFilter.matrix(colorMatrix)); - final Picture circles1 = _drawTestPictureWithCircles(30, 30); - builder.addPicture(Offset.zero, circles1); - builder.pop(); - domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('color_filter_matrix.png', region: region); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/85733 - test('Should apply mode color filter to circles', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(); - builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter(const ColorFilter.mode(Color(0xFFFF0000), BlendMode.srcIn)); - final Picture circles1 = _drawTestPictureWithCircles(30, 30); - builder.addPicture(Offset.zero, circles1); - builder.pop(); - domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('color_filter_mode.png', region: region); - }); -} - -Picture _drawTestPictureWithCircles(double offsetX, double offsetY) { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), - 10, - (Paint()..style = PaintingStyle.fill) as SurfacePaint, - ); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), - 10, - (Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)) - as SurfacePaint, - ); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), - 10, - (Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)) - as SurfacePaint, - ); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), - 10, - (Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)) - as SurfacePaint, - ); - return recorder.endRecording(); -} - -Picture _drawBackground() { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); - canvas.drawRect( - const Rect.fromLTWH(8, 8, 400.0 - 16, 400.0 - 16), - (Paint() - ..style = PaintingStyle.fill - ..color = const Color(0xFFE0FFE0)) - as SurfacePaint, - ); - return recorder.endRecording(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/compositing_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/compositing_golden_test.dart deleted file mode 100644 index ff0b022c08..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/compositing_golden_test.dart +++ /dev/null @@ -1,683 +0,0 @@ -// 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:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; -import 'package:web_engine_tester/golden_tester.dart'; - -import '../../common/matchers.dart'; -import '../../common/test_initialization.dart'; - -const ui.Rect region = ui.Rect.fromLTWH(0, 0, 500, 100); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - setUp(() async { - // To debug test failures uncomment the following to visualize clipping - // layers: - // debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (final DomNode scene in domDocument.querySelectorAll('flt-scene')) { - scene.remove(); - } - }); - - test('pushClipRect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_shifted_clip_rect.png', region: region); - }); - - test('pushClipRect with offset and transform', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushOffset(0, 60); - builder.pushTransform(Matrix4.diagonal3Values(1, -1, 1).toFloat64()); - builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); - _drawTestPicture(builder); - builder.pop(); - builder.pop(); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_clip_rect_with_offset_and_transform.png', region: region); - }); - - test('pushClipRect with offset and transform ClipOp none should not clip', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushOffset(0, 80); - builder.pushTransform(Matrix4.diagonal3Values(1, -1, 1).toFloat64()); - builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60), clipBehavior: ui.Clip.none); - _drawTestPicture(builder); - builder.pop(); - builder.pop(); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_clip_rect_clipop_none.png', region: region); - }); - - test('pushClipRRect with offset and transform ClipOp none should not clip', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushOffset(0, 80); - builder.pushTransform(Matrix4.diagonal3Values(1, -1, 1).toFloat64()); - builder.pushClipRRect( - ui.RRect.fromRectAndRadius( - const ui.Rect.fromLTRB(10, 10, 60, 60), - const ui.Radius.circular(1), - ), - clipBehavior: ui.Clip.none, - ); - _drawTestPicture(builder); - builder.pop(); - builder.pop(); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_clip_rrect_clipop_none.png', region: region); - }); - - test('pushClipRRect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRRect(ui.RRect.fromLTRBR(10, 10, 60, 60, const ui.Radius.circular(5))); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_shifted_clip_rrect.png', region: region); - }); - - test('pushImageFilter blur', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushImageFilter(ui.ImageFilter.blur(sigmaX: 1, sigmaY: 3)); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_image_filter.png', region: region); - }); - - test('pushImageFilter matrix', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushImageFilter( - ui.ImageFilter.matrix( - (Matrix4.identity() - ..translate(40, 10) - ..rotateZ(math.pi / 6) - ..scale(0.75, 0.75)) - .toFloat64(), - ), - ); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_image_filter_matrix.png', region: region); - }); - - test('pushImageFilter using mode ColorFilter', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - // Applying the colorFilter should turn all the circles red. - builder.pushImageFilter(const ui.ColorFilter.mode(ui.Color(0xFFFF0000), ui.BlendMode.srcIn)); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_image_filter_using_mode_color_filter.png', region: region); - }); - - test('pushImageFilter using matrix ColorFilter', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - // Apply a "greyscale" color filter. - final List colorMatrix = [ - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0.2126, 0.7152, 0.0722, 0, 0, // - 0, 0, 0, 1, 0, // - ]; - - builder.pushImageFilter(ui.ColorFilter.matrix(colorMatrix)); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_image_filter_using_matrix_color_filter.png', region: region); - }); - - group('Cull rect computation', () { - _testCullRectComputation(); - }); -} - -void _testCullRectComputation() { - // Draw a picture larger that screen. Verify that cull rect is equal to screen - // bounds. - test('fills screen bounds', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 10000, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, 0, 500, 100)); - // Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500 - // https://github.com/flutter/flutter/issues/40395 - }, skip: true); - - // Draw a picture that overflows the screen. Verify that cull rect is the - // intersection of screen bounds and paint bounds. - test('intersects with screen bounds', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, 0, 20, 20)); - }); - - // Draw a picture that's fully outside the screen bounds. Verify the cull rect - // is zero. - test('fully outside screen bounds', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const ui.Offset(-100, -100), - 20, - SurfacePaint()..style = ui.PaintingStyle.fill, - ); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, ui.Rect.zero); - expect(picture.debugExactGlobalCullRect, ui.Rect.zero); - }); - - // Draw a picture that's fully inside the screen. Verify that cull rect is - // equal to the paint bounds. - test('limits to paint bounds if no clip layers', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 60, 60)); - }); - - // Draw a picture smaller than the screen. Offset it such that it remains - // fully inside the screen bounds. Verify that cull rect is still just the - // paint bounds. - test('offset does not affect paint bounds', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - - builder.pushOffset(10, 10); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - builder.pop(); - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 60, 60)); - }); - - // Draw a picture smaller than the screen. Offset it such that the picture - // overflows screen bounds. Verify that the cull rect is the intersection - // between screen bounds and paint bounds. - test('offset overflows paint bounds', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - - builder.pushOffset(0, 90); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - builder.pop(); - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.debugExactGlobalCullRect, const ui.Rect.fromLTRB(0, 70, 20, 100)); - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, -20, 20, 10)); - // Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500 - // https://github.com/flutter/flutter/issues/40395 - }, skip: true); - - // Draw a picture inside a layer clip but fill all available space inside it. - // Verify that the cull rect is equal to the layer clip. - test('fills layer clip rect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const ui.Rect.fromLTWH(10, 10, 60, 60)); - - builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 10000, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_cull_rect_fills_layer_clip.png', region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 70, 70)); - }); - - // Draw a picture inside a layer clip but position the picture such that its - // paint bounds overflow the layer clip. Verify that the cull rect is the - // intersection between the layer clip and paint bounds. - test('intersects layer clip rect and paint bounds', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const ui.Rect.fromLTWH(10, 10, 60, 60)); - - builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(const ui.Offset(80, 55), 30, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile( - 'compositing_cull_rect_intersects_clip_and_paint_bounds.png', - region: region, - ); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(50, 40, 70, 70)); - }); - - // Draw a picture inside a layer clip that's positioned inside the clip using - // an offset layer. Verify that the cull rect is the intersection between the - // layer clip and the offset paint bounds. - test('offsets picture inside layer clip rect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const ui.Rect.fromLTWH(10, 10, 60, 60)); - - builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); - - builder.pushOffset(55, 70); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - - builder.pop(); // pushOffset - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_cull_rect_offset_inside_layer_clip.png', region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(-15.0, -20.0, 15.0, 0.0)); - }); - - // Draw a picture inside a layer clip that's positioned an offset layer such - // that the picture is push completely outside the clip area. Verify that the - // cull rect is zero. - test('zero intersection with clip', () async { - final ui.SceneBuilder builder = ui.SceneBuilder(); - builder.pushClipRect(const ui.Rect.fromLTWH(10, 10, 60, 60)); - - builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); - - builder.pushOffset(100, 50); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); - }); - - builder.pop(); // pushOffset - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, ui.Rect.zero); - expect(picture.debugExactGlobalCullRect, ui.Rect.zero); - }); - - // Draw a picture inside a rotated clip. Verify that the cull rect is big - // enough to fit the rotated clip. - test('rotates clip and the picture', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(80, 50); - - builder.pushTransform(Matrix4.rotationZ(-math.pi / 4).toFloat64()); - - builder.pushClipRect(const ui.Rect.fromLTRB(-10, -10, 10, 10)); - - builder.pushTransform(Matrix4.rotationZ(math.pi / 4).toFloat64()); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawPaint( - SurfacePaint() - ..color = const ui.Color.fromRGBO(0, 0, 255, 0.6) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(-5, -5, 5, 5), - SurfacePaint() - ..color = const ui.Color.fromRGBO(0, 255, 0, 1.0) - ..style = ui.PaintingStyle.fill, - ); - }); - - builder.pop(); // pushTransform - builder.pop(); // pushClipRect - builder.pop(); // pushTransform - builder.pop(); // pushOffset - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_cull_rect_rotated.png', region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect( - picture.optimalLocalCullRect, - within(distance: 0.05, from: const ui.Rect.fromLTRB(-14.1, -14.1, 14.1, 14.1)), - ); - }); - - test('pushClipPath', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Path path = ui.Path(); - path.addRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); - builder.pushClipPath(path); - _drawTestPicture(builder); - builder.pop(); - - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_clip_path.png', region: region); - }); - - // Draw a picture inside a rotated clip. Verify that the cull rect is big - // enough to fit the rotated clip. - test('clips correctly when using 3d transforms', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushTransform( - Matrix4.diagonal3Values( - EngineFlutterDisplay.instance.browserDevicePixelRatio, - EngineFlutterDisplay.instance.browserDevicePixelRatio, - 1.0, - ).toFloat64(), - ); - - // TODO(yjbanov): see the TODO below. - // final double screenWidth = domWindow.innerWidth.toDouble(); - // final double screenHeight = domWindow.innerHeight.toDouble(); - - final Matrix4 scaleTransform = Matrix4.identity().scaled(0.5, 0.2); - builder.pushTransform(scaleTransform.toFloat64()); - - builder.pushOffset(400, 200); - - builder.pushClipRect(const ui.Rect.fromLTRB(-200, -200, 200, 200)); - - builder.pushTransform(Matrix4.rotationY(45.0 * math.pi / 180.0).toFloat64()); - - builder.pushClipRect(const ui.Rect.fromLTRB(-140, -140, 140, 140)); - - builder.pushTransform(Matrix4.translationValues(0, 0, -50).toFloat64()); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawPaint( - SurfacePaint() - ..color = const ui.Color.fromRGBO(0, 0, 255, 0.6) - ..style = ui.PaintingStyle.fill, - ); - // ui.Rect will be clipped. - canvas.drawRect( - const ui.Rect.fromLTRB(-150, -150, 150, 150), - SurfacePaint() - ..color = const ui.Color.fromRGBO(0, 255, 0, 1.0) - ..style = ui.PaintingStyle.fill, - ); - // Should be outside the clip range. - canvas.drawRect( - const ui.Rect.fromLTRB(-150, -150, -140, -140), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(140, -150, 150, -140), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(-150, 140, -140, 150), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(140, 140, 150, 150), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) - ..style = ui.PaintingStyle.fill, - ); - // Should be inside clip range - canvas.drawRect( - const ui.Rect.fromLTRB(-100, -100, -90, -90), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(90, -100, 100, -90), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(-100, 90, -90, 100), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = ui.PaintingStyle.fill, - ); - canvas.drawRect( - const ui.Rect.fromLTRB(90, 90, 100, 100), - SurfacePaint() - ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = ui.PaintingStyle.fill, - ); - }); - - builder.pop(); // pushTransform Z-50 - builder.pop(); // pushClipRect - builder.pop(); // pushTransform 3D rotate - builder.pop(); // pushClipRect - builder.pop(); // pushOffset - builder.pop(); // pushTransform scale - builder.pop(); // pushTransform scale devicepixelratio - domDocument.body!.append(builder.build().webOnlyRootElement!); - - await matchGoldenFile('compositing_3d_rotate1.png', region: region); - - // ignore: unused_local_variable - final PersistedPicture picture = enumeratePictures().single; - // TODO(yjbanov): https://github.com/flutter/flutter/issues/40395) - // Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500. - // expect( - // picture.optimalLocalCullRect, - // within( - // distance: 0.05, - // from: ui.Rect.fromLTRB( - // -140, -140, screenWidth - 360.0, screenHeight + 40.0)), - // ); - }); - - // This test reproduces text blurriness when two pieces of text appear inside - // two nested clips: - // - // ┌───────────────────────┐ - // │ text in outer clip │ - // │ ┌────────────────────┐│ - // │ │ text in inner clip ││ - // │ └────────────────────┘│ - // └───────────────────────┘ - // - // This test clips using layers. See a similar test in `bitmap_canvas_golden_test.dart`, - // which clips using canvas. - // - // More details: https://github.com/flutter/flutter/issues/32274 - test('renders clipped text with high quality', () async { - // To reproduce blurriness we need real clipping. - final CanvasParagraph paragraph = - (ui.ParagraphBuilder(ui.ParagraphStyle(fontFamily: 'Roboto')) - // Use a decoration to force rendering in DOM mode. - ..pushStyle( - ui.TextStyle( - decoration: ui.TextDecoration.lineThrough, - decorationColor: const ui.Color(0x00000000), - ), - ) - ..addText('Am I blurry?')) - .build() - as CanvasParagraph; - paragraph.layout(const ui.ParagraphConstraints(width: 1000)); - - final ui.Rect canvasSize = ui.Rect.fromLTRB( - 0, - 0, - paragraph.maxIntrinsicWidth + 16, - 2 * paragraph.height + 32, - ); - final ui.Rect outerClip = ui.Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); - final ui.Rect innerClip = ui.Rect.fromLTRB( - 0.5, - canvasSize.bottom / 2 + 0.5, - canvasSize.right, - canvasSize.bottom, - ); - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushClipRect(outerClip); - - { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(outerClip); - canvas.drawParagraph(paragraph, const ui.Offset(8.5, 8.5)); - final ui.Picture picture = recorder.endRecording(); - expect(paragraph.canDrawOnCanvas, isFalse); - - builder.addPicture(ui.Offset.zero, picture); - } - - builder.pushClipRect(innerClip); - { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(innerClip); - canvas.drawParagraph(paragraph, ui.Offset(8.5, 8.5 + innerClip.top)); - final ui.Picture picture = recorder.endRecording(); - expect(paragraph.canDrawOnCanvas, isFalse); - - builder.addPicture(ui.Offset.zero, picture); - } - builder.pop(); // inner clip - builder.pop(); // outer clip - - final DomElement sceneElement = builder.build().webOnlyRootElement!; - expect( - sceneElement - .querySelectorAll('flt-paragraph') - .map((DomElement e) => e.innerText) - .toList(), - ['Am I blurry?', 'Am I blurry?'], - reason: 'Expected to render text using HTML', - ); - domDocument.body!.append(sceneElement); - - await matchGoldenFile('compositing_draw_high_quality_text.png', region: canvasSize); - }, testOn: 'chrome'); -} - -void _drawTestPicture(ui.SceneBuilder builder) { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 100, 100)); - canvas.drawCircle(const ui.Offset(10, 10), 10, SurfacePaint()..style = ui.PaintingStyle.fill); - canvas.drawCircle( - const ui.Offset(60, 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(255, 0, 0, 1), - ); - canvas.drawCircle( - const ui.Offset(10, 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 255, 0, 1), - ); - canvas.drawCircle( - const ui.Offset(60, 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 0, 255, 1), - ); - final ui.Picture picture = recorder.endRecording(); - - builder.addPicture(ui.Offset.zero, picture); -} - -typedef PaintCallback = void Function(RecordingCanvas canvas); - -void drawWithBitmapCanvas( - ui.SceneBuilder builder, - PaintCallback callback, { - ui.Rect bounds = ui.Rect.largest, -}) { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(bounds); - - canvas.debugEnforceArbitraryPaint(); - callback(canvas); - final ui.Picture picture = recorder.endRecording(); - - builder.addPicture(ui.Offset.zero, picture); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart deleted file mode 100644 index 9a258f3eeb..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - test('Should blur rectangles based on sigma.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - for (int blurSigma = 1; blurSigma < 10; blurSigma += 2) { - final SurfacePaint paint = - SurfacePaint() - ..color = const Color(0xFF2fdfd2) - ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma.toDouble()); - rc.drawRect(Rect.fromLTWH(15.0, 15.0 + blurSigma * 40, 200, 20), paint); - } - await canvasScreenshot(rc, 'dom_mask_filter_blur'); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/deprecation_test.dart b/engine/src/flutter/lib/web_ui/test/html/deprecation_test.dart deleted file mode 100644 index 1e46b7cfb8..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/deprecation_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - final List warnings = []; - late void Function(String) oldPrintWarning; - - setUpAll(() async { - oldPrintWarning = printWarning; - printWarning = (String warning) { - warnings.add(warning); - }; - }); - - tearDownAll(() { - printWarning = oldPrintWarning; - }); - - test('Emit a warning when the HTML Renderer was picked.', () { - final Renderer chosenRenderer = renderer; - - expect(chosenRenderer, isA()); - expect( - warnings, - contains(contains('See: https://docs.flutter.dev/to/web-html-renderer-deprecation')), - ); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/dom_canvas_test.dart b/engine/src/flutter/lib/web_ui/test/html/dom_canvas_test.dart deleted file mode 100644 index d4cdf75313..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/dom_canvas_test.dart +++ /dev/null @@ -1,103 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - group('$adjustRectForDom', () { - test('does not change rect when not necessary', () async { - const Rect rect = Rect.fromLTWH(10, 20, 140, 160); - expect(adjustRectForDom(rect, SurfacePaintData()..style = PaintingStyle.fill), rect); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 0, - ), - rect, - ); - }); - - test('takes stroke width into consideration', () async { - const Rect rect = Rect.fromLTWH(10, 20, 140, 160); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 1, - ), - const Rect.fromLTWH(9.5, 19.5, 139, 159), - ); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 10, - ), - const Rect.fromLTWH(5, 15, 130, 150), - ); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 15, - ), - const Rect.fromLTWH(2.5, 12.5, 125, 145), - ); - }); - - test('flips rect when necessary', () { - Rect rect = const Rect.fromLTWH(100, 200, -40, -60); - expect( - adjustRectForDom(rect, SurfacePaintData()..style = PaintingStyle.fill), - const Rect.fromLTWH(60, 140, 40, 60), - ); - - rect = const Rect.fromLTWH(100, 200, 40, -60); - expect( - adjustRectForDom(rect, SurfacePaintData()..style = PaintingStyle.fill), - const Rect.fromLTWH(100, 140, 40, 60), - ); - - rect = const Rect.fromLTWH(100, 200, -40, 60); - expect( - adjustRectForDom(rect, SurfacePaintData()..style = PaintingStyle.fill), - const Rect.fromLTWH(60, 200, 40, 60), - ); - }); - - test('handles stroke width greater than width or height', () { - const Rect rect = Rect.fromLTWH(100, 200, 20, 70); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 50, - ), - const Rect.fromLTWH(75, 175, 0, 20), - ); - expect( - adjustRectForDom( - rect, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 80, - ), - const Rect.fromLTWH(60, 160, 0, 0), - ); - }); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart deleted file mode 100644 index ccfc779104..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart +++ /dev/null @@ -1,132 +0,0 @@ -// 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:math' as math; -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 400, 600); - - late BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws arcs with largeArc , anticlockwise variations', () async { - paintArc(canvas, Offset.zero, distance: 20); - paintArc(canvas, const Offset(200, 0), largeArc: true, distance: 20); - paintArc(canvas, const Offset(0, 150), clockwise: true, distance: 20); - paintArc(canvas, const Offset(200, 150), largeArc: true, clockwise: true, distance: 20); - paintArc(canvas, const Offset(0, 300), distance: -20); - paintArc(canvas, const Offset(200, 300), largeArc: true, distance: -20); - paintArc(canvas, const Offset(0, 400), clockwise: true, distance: -20); - paintArc(canvas, const Offset(200, 400), largeArc: true, clockwise: true, distance: -20); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_arc_to_point.png', region: region); - }); - - test('Path.addArc that starts new path has correct start point', () async { - const Rect rect = Rect.fromLTWH(20, 20, 200, 200); - final Path p = - Path() - ..fillType = PathFillType.evenOdd - ..addRect(rect) - ..addArc( - Rect.fromCircle(center: rect.center, radius: rect.size.shortestSide / 2), - 0.25 * math.pi, - 1.5 * math.pi, - ); - canvas.drawPath( - p, - SurfacePaintData() - ..color = - 0xFFFF9800 // orange - ..style = PaintingStyle.fill, - ); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_addarc.png', region: region); - }); - - test('Should render counter clockwise arcs', () async { - final Path path = Path(); - path.moveTo(149.999999999999997, 50); - path.lineTo(149.999999999999997, 20); - path.arcTo( - const Rect.fromLTRB(20, 20, 280, 280), - 4.71238898038469, - 5.759586531581287 - 4.71238898038469, - true, - ); - path.lineTo(236.60254037844385, 99.99999999999999); - path.arcTo( - const Rect.fromLTRB(50, 50, 250, 250), - 5.759586531581287, - 4.71238898038469 - 5.759586531581287, - true, - ); - path.lineTo(149.999999999999997, 20); - canvas.drawPath( - path, - SurfacePaintData() - ..color = - 0xFFFF9800 // orange - ..style = PaintingStyle.fill, - ); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_addarc_ccw.png', region: region); - }); -} - -void paintArc( - BitmapCanvas canvas, - Offset offset, { - bool largeArc = false, - bool clockwise = false, - double distance = 0, -}) { - final Offset startP = Offset(75 - distance + offset.dx, 75 - distance + offset.dy); - final Offset endP = Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); - canvas.drawRect( - Rect.fromLTRB(startP.dx, startP.dy, endP.dx, endP.dy), - SurfacePaintData() - ..strokeWidth = 1 - ..color = - 0xFFFF9800 // orange - ..style = PaintingStyle.stroke, - ); - final Path path = Path(); - path.moveTo(startP.dx, startP.dy); - path.arcToPoint( - endP, - rotation: 45, - radius: const Radius.elliptical(40, 60), - largeArc: largeArc, - clockwise: clockwise, - ); - canvas.drawPath( - path, - SurfacePaintData() - ..strokeWidth = 2 - ..color = - 0x61000000 // black38 - ..style = PaintingStyle.stroke, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart deleted file mode 100644 index b6795d8c15..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart +++ /dev/null @@ -1,83 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); - - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - }); - - tearDown(() { - for (final DomNode scene in domDocument.querySelectorAll('flt-scene')) { - scene.remove(); - } - }); - - test('drawColor should cover entire viewport', () async { - const Rect region = Rect.fromLTWH(0, 0, 400, 400); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture testPicture = _drawTestPicture(region, useColor: true); - builder.addPicture(Offset.zero, testPicture); - await sceneScreenshot(builder, 'canvas_draw_color', region: region); - }, skip: true); // TODO(ferhat): matchGolden fails when a div covers viewport. - - test('drawPaint should cover entire viewport', () async { - const Rect region = Rect.fromLTWH(0, 0, 400, 400); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture testPicture = _drawTestPicture(region); - builder.addPicture(Offset.zero, testPicture); - await sceneScreenshot(builder, 'canvas_draw_paint', region: region); - }, skip: true); // TODO(ferhat): matchGolden fails when a div covers viewport.); -} - -Picture _drawTestPicture(Rect region, {bool useColor = false}) { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - const Rect r = Rect.fromLTWH(0, 0, 200, 200); - final RecordingCanvas canvas = recorder.beginRecording(r); - - canvas.drawRect( - region.deflate(8.0), - Paint() as SurfacePaint - ..style = PaintingStyle.fill - ..color = const Color(0xFFE0E0E0), - ); - - canvas.transform(Matrix4.translationValues(50, 50, 0).storage); - - if (useColor) { - canvas.drawColor(const Color.fromRGBO(0, 255, 0, 1), BlendMode.srcOver); - } else { - canvas.drawPaint( - Paint() as SurfacePaint - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1), - ); - } - - canvas.drawCircle( - Offset(r.width / 2, r.height / 2), - r.width / 2, - Paint() as SurfacePaint - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1), - ); - - return recorder.endRecording(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart deleted file mode 100644 index 6b778e6640..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart +++ /dev/null @@ -1,836 +0,0 @@ -// 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:convert'; -import 'dart:js_util' as js_util; -import 'dart:math' as math; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); - - test('Paints image', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawImage(createTestImage(), Offset.zero, SurfacePaint()); - rc.restore(); - await canvasScreenshot(rc, 'draw_image'); - }); - - test( - 'Images from raw data are composited when picture is roundtripped through toImage', - () async { - final Uint8List imageData = base64Decode(base64PngData); - final Codec codec = await instantiateImageCodec(imageData); - final FrameInfo frameInfo = await codec.getNextFrame(); - codec.dispose(); - - const Rect bounds = Rect.fromLTRB(0, 0, 400, 300); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas scratchCanvas = recorder.beginRecording(bounds); - scratchCanvas.save(); - scratchCanvas.drawImage(frameInfo.image, Offset.zero, SurfacePaint()); - scratchCanvas.restore(); - final Picture picture = recorder.endRecording(); - final Image image = await picture.toImage(400, 300); - - final RecordingCanvas rc = RecordingCanvas(bounds); - rc.save(); - rc.drawImage(image, Offset.zero, SurfacePaint()); - rc.restore(); - await canvasScreenshot(rc, 'draw_raw_image'); - }, - ); - - test('Paints image with transform', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - rc.drawImage(createTestImage(), Offset.zero, SurfacePaint()); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_with_transform'); - }); - - test('Paints image with transform and offset', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - rc.drawImage(createTestImage(), const Offset(30, 20), SurfacePaint()); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_with_transform_and_offset'); - }); - - test('Paints image with transform using destination', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_rect_with_transform'); - }); - - test('Paints image with source and destination', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_rect_with_source'); - }); - - test('Paints image with source and destination and round clip', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.save(); - rc.clipRRect( - RRect.fromLTRBR(100, 30, 2 * testWidth, 2 * testHeight, const Radius.circular(16)), - ); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_rect_with_source_and_clip'); - }); - - test('Paints image with transform using source and destination', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 6.0); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_image_rect_with_transform_source'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image not below. - test('Paints on top of image', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should below image not on top. - test('Paints below image', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_below_image'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rect. - test('Paints on top of image with clip rect', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rect and transform. - test('Paints on top of image with clip rect with transform', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - // Rotate around center of circle. - rc.translate(100, 100); - rc.rotate(math.pi / 4.0); - rc.translate(-100, -100); - rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect_with_transform'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with stack of clip rect and transforms. - test('Paints on top of image with clip rect with stack', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - // Rotate around center of circle. - rc.translate(100, 100); - rc.rotate(-math.pi / 4.0); - rc.save(); - rc.translate(-100, -100); - rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect_with_stack'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rrect. - test('Paints on top of image with clip rrect', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - rc.clipRRect(RRect.fromLTRBR(75, 75, 160, 160, const Radius.circular(5))); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image_clip_rrect'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rrect. - test('Paints on top of image with clip path', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - final Path path = Path(); - // Triangle. - path.moveTo(118, 57); - path.lineTo(75, 160); - path.lineTo(160, 160); - rc.clipPath(path); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - SurfacePaint(), - ); - rc.drawCircle( - const Offset(100, 100), - 50.0, - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color.fromARGB(128, 0, 0, 0), - ); - rc.restore(); - await canvasScreenshot(rc, 'draw_circle_on_image_clip_path'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/53078 - // Verified that Text+Image+Text+Rect+Text composites correctly. - // Yellow text should be behind image and rectangle. - // Cyan text should be above everything. - test('Paints text above and below image', () async { - // Use a non-Ahem font so that text is visible. - ui_web.debugEmulateFlutterTesterEnvironment = false; - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - final Image testImage = createTestImage(); - final double testWidth = testImage.width.toDouble(); - final double testHeight = testImage.height.toDouble(); - const Color orange = Color(0xFFFF9800); - final Paragraph paragraph1 = createTestParagraph( - 'Should be below below below below below', - color: orange, - ); - paragraph1.layout(const ParagraphConstraints(width: 400.0)); - rc.drawParagraph(paragraph1, const Offset(20, 100)); - rc.drawImageRect( - testImage, - Rect.fromLTRB(0, 0, testWidth, testHeight), - const Rect.fromLTRB(100, 100, 200, 200), - SurfacePaint(), - ); - rc.drawRect( - const Rect.fromLTWH(50, 50, 100, 200), - SurfacePaint() - ..strokeWidth = 3 - ..color = const Color(0xA0000000), - ); - const Color cyan = Color(0xFF0097A7); - final Paragraph paragraph2 = createTestParagraph( - 'Should be above above above above above', - color: cyan, - ); - paragraph2.layout(const ParagraphConstraints(width: 400.0)); - rc.drawParagraph(paragraph2, const Offset(20, 150)); - rc.restore(); - await canvasScreenshot( - rc, - 'draw_text_composite_order_below', - region: const Rect.fromLTWH(0, 0, 350, 300), - ); - }); - - // Creates a picture - test('Paints nine slice image', () async { - const Rect region = Rect.fromLTWH(0, 0, 500, 500); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final Canvas canvas = Canvas(recorder, region); - final Image testImage = createNineSliceImage(); - canvas.clipRect(const Rect.fromLTWH(0, 0, 420, 200)); - canvas.drawImageNine( - testImage, - const Rect.fromLTWH(20, 20, 20, 20), - const Rect.fromLTWH(20, 20, 400, 400), - SurfacePaint(), - ); - final Picture picture = recorder.endRecording(); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.addPicture(Offset.zero, picture); - - // Wrap in so that our CSS selectors kick in. - final DomElement sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - try { - sceneElement.append(builder.build().webOnlyRootElement!); - domDocument.body!.append(sceneElement); - await matchGoldenFile('draw_nine_slice.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // screenshot. - sceneElement.remove(); - } - }); - - // Regression test for https://github.com/flutter/flutter/issues/78068 - // Tests for correct behavior when using drawImageNine with a destination - // size that is too small to render the center portion of the original image. - test('Paints nine slice image', () async { - const Rect region = Rect.fromLTWH(0, 0, 100, 100); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final Canvas canvas = Canvas(recorder, region); - final Image testImage = createNineSliceImage(); - canvas.clipRect(const Rect.fromLTWH(0, 0, 100, 100)); - // The testImage is 60x60 and the center slice is 20x20 so the edges - // of the image are 40x40. Drawing into a destination that is smaller - // than that will not provide enough room to draw the center portion. - canvas.drawImageNine( - testImage, - const Rect.fromLTWH(20, 20, 20, 20), - const Rect.fromLTWH(20, 20, 36, 36), - SurfacePaint(), - ); - final Picture picture = recorder.endRecording(); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.addPicture(Offset.zero, picture); - - // Wrap in so that our CSS selectors kick in. - final DomElement sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - try { - sceneElement.append(builder.build().webOnlyRootElement!); - domDocument.body!.append(sceneElement); - await matchGoldenFile('draw_nine_slice_empty_center.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // screenshot. - sceneElement.remove(); - } - }); - - // Regression test for https://github.com/flutter/flutter/issues/61691 - // - // The bug in bitmap_canvas.dart was that when we transformed and clipped - // the image we did not apply `transform-origin: 0 0 0` to the clipping - // element which resulted in an undesirable offset. - test('Paints clipped and transformed image', () async { - const Rect region = Rect.fromLTRB(0, 0, 60, 70); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.translate(10, 10); - canvas.transform(Matrix4.rotationZ(0.4).storage); - canvas.clipPath( - Path() - ..moveTo(10, 10) - ..lineTo(50, 10) - ..lineTo(50, 30) - ..lineTo(10, 30) - ..close(), - ); - canvas.drawImage(createNineSliceImage(), Offset.zero, SurfacePaint()); - await canvasScreenshot(canvas, 'draw_clipped_and_transformed_image', region: region); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/61245 - test('Should render image with perspective', () async { - const Rect region = Rect.fromLTRB(0, 0, 200, 200); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.translate(10, 10); - canvas.drawImage(createTestImage(), Offset.zero, SurfacePaint()); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.0005); // perspective - canvas.transform(transform.storage); - canvas.drawImage(createTestImage(), const Offset(0, 100), SurfacePaint()); - await canvasScreenshot(canvas, 'draw_3d_image', region: region, setupPerspective: true); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/61245 - test('Should render image with perspective inside clip area', () async { - const Rect region = Rect.fromLTRB(0, 0, 200, 200); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFE0E0E0)); - canvas.translate(10, 10); - canvas.drawImage(createTestImage(), Offset.zero, SurfacePaint()); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.0005); // perspective - canvas.transform(transform.storage); - canvas.clipRect(region, ClipOp.intersect); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 100, 200), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawImage(createTestImage(), const Offset(0, 100), SurfacePaint()); - canvas.drawRect( - const Rect.fromLTWH(50, 150, 50, 20), - SurfacePaint()..color = const Color(0x80000000), - ); - await canvasScreenshot(canvas, 'draw_3d_image_clipped', region: region, setupPerspective: true); - }); - - test('Should render rect with perspective transform', () async { - const Rect region = Rect.fromLTRB(0, 0, 400, 400); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFE0E0E0)); - canvas.translate(20, 20); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 100, 40), - SurfacePaint()..color = const Color(0xFF000000), - ); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.001); // perspective - canvas.transform(transform.storage); - canvas.clipRect(region, ClipOp.intersect); - canvas.drawRect( - const Rect.fromLTWH(0, 60, 120, 40), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawRect( - const Rect.fromLTWH(300, 250, 120, 40), - SurfacePaint()..color = const Color(0x80E010E0), - ); - canvas.drawRRect( - RRect.fromRectAndRadius(const Rect.fromLTWH(0, 120, 160, 40), const Radius.circular(5)), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawRRect( - RRect.fromRectAndRadius(const Rect.fromLTWH(300, 320, 90, 40), const Radius.circular(20)), - SurfacePaint()..color = const Color(0x80E010E0), - ); - await canvasScreenshot(canvas, 'draw_3d_rect_clipped', region: region, setupPerspective: true); - }); - - test('Should render color and ovals with perspective transform', () async { - const Rect region = Rect.fromLTRB(0, 0, 400, 400); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); - canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); - canvas.translate(20, 20); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 100, 40), - SurfacePaint()..color = const Color(0xFF000000), - ); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.001); // perspective - canvas.transform(transform.storage); - canvas.clipRect(region, ClipOp.intersect); - canvas.drawOval( - const Rect.fromLTWH(0, 120, 130, 40), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawOval( - const Rect.fromLTWH(300, 290, 90, 40), - SurfacePaint()..color = const Color(0x80E010E0), - ); - canvas.drawCircle(const Offset(60, 240), 50, SurfacePaint()..color = const Color(0x801080E0)); - canvas.drawCircle(const Offset(360, 370), 30, SurfacePaint()..color = const Color(0x80E010E0)); - await canvasScreenshot(canvas, 'draw_3d_oval_clipped', region: region, setupPerspective: true); - }); - - test('Should render path with perspective transform', () async { - const Rect region = Rect.fromLTRB(0, 0, 400, 400); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); - canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); - canvas.translate(20, 20); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 100, 20), - SurfacePaint()..color = const Color(0xFF000000), - ); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.001); // perspective - canvas.transform(transform.storage); - canvas.drawRect( - const Rect.fromLTWH(0, 120, 130, 40), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawOval( - const Rect.fromLTWH(300, 290, 90, 40), - SurfacePaint()..color = const Color(0x80E010E0), - ); - final Path path = Path(); - path.moveTo(50, 50); - path.lineTo(100, 50); - path.lineTo(100, 100); - path.close(); - canvas.drawPath(path, SurfacePaint()..color = const Color(0x801080E0)); - - canvas.drawCircle(const Offset(50, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); - canvas.drawCircle(const Offset(100, 100), 4, SurfacePaint()..color = const Color(0xFF000000)); - canvas.drawCircle(const Offset(100, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); - await canvasScreenshot(canvas, 'draw_3d_path', region: region, setupPerspective: true); - }); - - test('Should render path with perspective transform', () async { - const Rect region = Rect.fromLTRB(0, 0, 400, 400); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); - canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); - canvas.translate(20, 20); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 100, 20), - SurfacePaint()..color = const Color(0xFF000000), - ); - final Matrix4 transform = - Matrix4.identity() - ..setRotationY(0.8) - ..setEntry(3, 2, 0.001); // perspective - canvas.transform(transform.storage); - //canvas.clipRect(region, ClipOp.intersect); - canvas.drawRect( - const Rect.fromLTWH(0, 120, 130, 40), - SurfacePaint()..color = const Color(0x801080E0), - ); - canvas.drawOval( - const Rect.fromLTWH(300, 290, 90, 40), - SurfacePaint()..color = const Color(0x80E010E0), - ); - final Path path = Path(); - path.moveTo(50, 50); - path.lineTo(100, 50); - path.lineTo(100, 100); - path.close(); - canvas.drawPath(path, SurfacePaint()..color = const Color(0x801080E0)); - - canvas.drawCircle(const Offset(50, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); - canvas.drawCircle(const Offset(100, 100), 4, SurfacePaint()..color = const Color(0xFF000000)); - canvas.drawCircle(const Offset(100, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); - await canvasScreenshot(canvas, 'draw_3d_path_clipped', region: region, setupPerspective: true); - }); -} - -// 9 slice test image that has a shiny/glass look. -const String base64PngData = - 'iVBORw0KGgoAAAANSUh' - 'EUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPo' - 'AAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAApGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQA' - 'AARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAACAAAABah2kABAAAAAEAAAB6AAAAAAAA' - 'AEgAAAABAAAASAAAAAFBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpAAADoAEAAwAA' - 'AAEAAQAAoAIABAAAAAEAAAA8oAMABAAAAAEAAAA8AAAAAKgRPeEAAAAJcEhZcwAACxMAAAs' - 'TAQCanBgAAATqaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOn' - 'g9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+CiAgIDxyZGY6Uk' - 'RGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW' - '5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAg' - 'IHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICA' - 'gICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1J' - 'lc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlL' - 'mNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2' - 'JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmF' - 'kb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAua' - 'WlkOjMxRTc0MTc5ODQwQTExRUE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpJbnN0YW5jZUl' - 'EPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMxRTc0MTdBODQwQTExR' - 'UE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU0' - '6RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c' - '3RSZWY6aW5zdGFuY2VJRD54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTA' - 'zMDNBPC9zdFJlZjppbnN0YW5jZUlEPgogICAgICAgICAgICA8c3RSZWY6ZG9jdW1lbnRJR' - 'D54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTAzMDNBPC9zdFJlZjpkb2N' - '1bWVudElEPgogICAgICAgICA8L3htcE1NOkRlcml2ZWRGcm9tPgogICAgICAgICA8ZXhpZ' - 'jpQaXhlbFlEaW1lbnNpb24+NjA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8' - 'ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl' - '4ZWxYRGltZW5zaW9uPjYwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHhtcD' - 'pDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3Jl' - 'YXRvclRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YX' - 'Rpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZ' - 'XRhPgpq1fpCAAAUDUlEQVRoBd1beYxd11n/3fvu2+fNm9VbPB6PHS+ldt0kNKWJk0YkaVJS' - 'UtRQhFCqSggRCVWgSKUKEgi1FNSKlvIH/1dVoWqatBKkUYqgAVRaNSTN0jg2buIkE4/XmbF' - 'ne/O2u/D7ffedN2/GM9hJCRAf+76z3LN8+/nuOd94jUYjwRWmJFnp6nkeVI+jCJ7vw+Mc9l' - 'Z9+M7qLK+MYHPPOmrvjulp17yunxvr+inXWmvh6Bl+2WJw2R7qwJUFehLHiIUEH58LWxIAa' - 'RerOoBi1Qi8S71AujaXW69O314k3XvXprURhrZ+ijyJ45HYIlLPWm7cevkVIUw+GieThFzN' - 'pAt0EUj0LiYQRNFBxnISs2L/00YRa6NkwHYAdsCLoBqpx7V5WjuTWZEMrat5faIvBgjxjRb' - 'ptG+IsKjJ6ToU5UTZLFdPENaXUL+4gNqFi1iuMT8/jcb8AtqkfBxHQNREVG8CzTaLbbaJGJ' - 'KKlCApQCkRCCHXELycvyMx2VyWawUI8jnEhQIJnEEuk0U2F6AyOopCdQil/ipKQwMsV+GrH' - '9UqabdJjAAZzuWLABukdREWgB5hSjSRR4py0fq5aUwdfxFTzzyDsz9+FrOPfR9NLIIoot2Z' - 'XMsIAeUSeL1zybWpvaMMxr2QdY1RUh89SkYI5qobl5ln+JDsKO89hNFbb8Tm91+P7e+9Hps' - 'm9qBQLiNutZAE0vFUCtn1kuStZ7SEcGgUyyBi/tpTT+HFb3wTk1/7KuqcQgBnc2UEWzfD6+' - '9HJiBlCZokQhSWCIbkaIbzrKRU3AwBa+6gwQaSl91Y55iI3JFeKgkOgo6oIyVeo4XW0hzCU1' - 'MkdkqUYQxj3xc+jet+5R4M7tlt3M6SST6ZtF5ajTAXSAhs1A4RZTy0KarP/O3f4Sd/+Gksc3' - 'Rx/FrkByrItglio4FQFK0vI9PiuM4/P6KeCxjqcIa8T/wM5yTYFAOhLNSka8SG/YigyRgBZ' - 'LNPmfBi9ucEIl7skf8Udb2LcszzeVI6i7DAvEBRZvvykRfQYr7pPdfjji98Hrs+eJidfWS' - 'yGseRtiA7dNIqhEXRiIaACoX6wgU8+eW/xgtf+kuUt4wjPzyIzMISsFgzg5TJS9cyCKg3YC' - '6xFzdowmwNQ81LBVPlkDoiQiipn9W4XtBtVRvnYJtgpDLxl/NJ9yU1VK+wSQvdjii6DUQt2g' - 'zCkAz2o03k54++aCL/kUe+hQN3302KUZcFX8c2aF2lVQgL2TBu09408fRX/gY//LPPoXLgAI' - 'pL5OTcIjIUt6BYQEA9aS/MIbk4Y3oqJkmAhJ7AdA+LloSA3qdcTHVbZSVxx6UURVdL+7ua7I' - 'HGxPz1N29DQm5HFHEZSEljuGkAzZ+d5HzL+NgTj2P/bXcgCVuENTAVc/N0ERZ3k4hL+hGe' - 'feQRPPHJ30bfwQMoTC8gaDSJaAl+u4Vw5hS5QvHGZhQPvxulrVuR3TSKbLmIbDGPDAHJUI' - 'yzfLiaGT0ZvtinFSWnZJGFmCVxU1RSYqNHNUgS8pY6G4qjXC+hpW8vN9BcmEfz7HnUJ09h6' - 'cWnTIdt3NZxtGln2st1tLaOoH7iZTNsn3j+WWzet5/UpS0hHC51EY6ikJIc4OzRo/jmDdcDW' - '8ZRlvI3G0SmBO/kKzbRtt/8HYzdfhjl8TEUaLBy+QK8XJ6IkPJEJvYFuf6rLN6yxP3bF/9FVN' - 'HUkDahVsVgERH06Fc7hDp6FEuVI4q1CBHRZoRUqcbsBUwdewlnHv4Ozr3wY2B4K9qUunB+Ccn' - 'EGM4fO4J9n/wEPvqlv0Kxr2xwS02VDGFxty0LWW/gH//i8zjy5a9g9F0H4J2aRXagD94bL2P' - 'gF2/FdZ96AJsOXY+gr4iWWBNSwORgaPNn0jwCPwV8bSFFTP0um9h1ZR4irYp++Lj9Wos0Z8/' - 'jlSe+h6N//lkkpVE0Kjk0l+po7tiGaSJ936OP4roP30N1Ipwdq20Ia+OOyd3Xf/IMvn34ZpR' - '370d2oYY8N/XM1AmM3H433vfQZ9A3MU7Pjtok4yEgmDLCziCy6srPem0rby9T4qRdqnW6SjS' - '6RXJcHCvSIaERO/kvT+KpT/0eGpu3o91oo1XKYe7MSQzdchPu//o3UN60CVnBw8f4LP5EyzW' - '89Njjphs5ikeWRPDZVkIfbvj9P0Dfrp2o1SgyNAQJrV9Cj8jn9uDTG5LeasvQk2GbPTkSa9WT' - 'RcAxAdvWe1b37dgCN5fNW7C1wPF+Nk8YfLTqNdQpmdtvvxPv+tPPwj83hWy1j/ksqvt/Aad/' - '8COcePbZVNU6BAu0X3o0MLOnT+O1L34RA9vGgLMzFOV+RJOvYNdDf4zKnmvRpEUsFqgPpKwM' - 'SkxDIXeuVadehU3zcvw2jY30je9lLELqnSRfap0ktBHGto5odLglQybeaf+XcZMPrZ3EV5kup' - 'lW05Yl4lDgvTycnyCMfpMRLWnRhucbE7bfh1D/cjOnnfojc5jE6KEvgbo3nHvt77D98C/J9VE' - '3CRjOq7d/HGz990RzFUb7IzNW57yU2YHTvPhLEQ5YTty7OYuH0edROT2HxzHksT0/TanNrWp' - 'xDe47cX2zS11623FukJCR8QPE357PWQXF1JvRTYSUHUWaN+zslIdNHJ7JShkfxDCpFeCznRk' - 'eQHxlFfvMI+rdsQ9+27SiMDFm/fHUE2266CTNEOEvCtJeX0Ld1DCe/+jVMPfAA9hy6zvyHQK' - 'Itakz++w8ovtRJcjJDKnr1OnJEeenlE4j4YbA4OYmZ53+Kucef4t57qrt/Sie0RyoJcO23ZA' - 'vLFD0CH5M7Cbnho2rt9lqdzNCZL9SxwtSvkO5YXIe45l2gpFxIZ' - '+6VCdNBDtdGk9/3Pgx/4L0YPXgQxf4BxPQM5WuH3MpydNKSoQriM8Dkc89j97sPmsGjfPhYmj' - 'mLcw8/TiM1iICdNalPDicDI3j16w8jnJqhDz1tiGWE0vZxUrFIv5nOIMXEo5RojMRX24dhTgd' - 'eX1zypyW06cceO61JtLv2jjuQ9dIuZS3K9dCpIOrpO9vCKJE0nK1aDc3jT2ORz0nOWdq6D+0' - 'cR1a2GNPkoVFfDa43/uMZ1D92H8qVCrzlZjM5+q//hu/e9SEM7NqNwlwNOQJugAj2ptx0cmKE' - 'nfWJSD2FfGhZa+k/ERK3TCyJtRx/NbHRxqm0cXJ9lKcIC71VqbdK5ZauewFdWT6g6CbcqCOq' - 'UXxhiW2UzCLbOEEzDtEqF7AwfQFLYQ2/+9JRXLN7F4KQYnTu1RO2TBDkKJ+LhDoVnFgISI8' - 'oBUmDulhr2GQihv6TiUysSWytlKKZtkpuV5J7v9KikuvjciG9Ura+a6pqk1GM6SiBHqDsix' - 'm3kQF4VAUdPEgyJH0+9+T87u04f/w4zk9NYsvuCaoNnY25o/9psi+vxp1U2MRCih/1ssgxRV' - 'WeU8JHhDAR5Hvxtlvu1CWAaut9tG/31n+uMtcXHInEXfPSxoDISuJc0geKT8Low1X6fvZnL5' - 'sP4dcWFzFNdpO39JWlv5yB/7splc9utbcgz8rkd02flWV7e7/9ZVu3A7syfiSa/y+Ep0+8h' - 'naL3wR1OuW1J/8J5YBWtNmyTdph3MV7DUIOdHsv+VHq9LncmLTz2/TrQOnAI//eq9Vt95k5' - 'dgx17kZ+/eK8MdTbOgyPCK/i7tsE19s9rSO6T5H36AbnCv1Y+NHT/MZfhL8wP297aoZfPT6' - '3pKspybhm6GsHg1V+Zc3y0HERwdJSymFRQ6ZW1s1R6J2MvFxWJV9+An1xmrVUpBsLC7YRyD' - 'lQuhqQNTy0XxtGxEkWneXaInUYy/R9WdnALnWGvDOzlIVyQ7lFEYUGz+T8Fo9GVPG43zqKv' - 'DPRuxRq4SM+K5f/0tYu1KYVU0VOR/d8ifWrIUmP0zPu9CRVx1h+yA96S5JpfYhepcmJN7+3' - '13FWr0Kknbr6Olq1REvmDuOuJnx1iZd+oBIrougnvJW7WpPEWB8yxNl2oozO4YKgkH6B6' - 'VqMMSZedj6HDRL/EMK1Xoq9VgEV2e8gm7YqmEIMtjD6WYp4q6W9WxTIo6aSPyXElyJtD1de' - 'PWtrv3byV3c77JsYYTz6o1PF/q5+VfP48vWYm4KWeJsB3h2P6Vyv2Vzt8Lj3ltbOhtu9J5Nu' - 'r3VmhnBxHEqT27wKNbHsaXijzbrvJCm6skPPtJdA6s9D8JaTrj/8mvpDWiUQ7jeeQOvAdFHl' - 'f55SJv8m+5mZfbPMsyhFNavhmKOvr0jlH553lEITf+rVLL4kt4gC/Xqn9iAkXdgOapyKXde' - 'wIVmfIzqZJHHqTXaeSYi7vHu2oredfeqTKFjvJ1CE/H+4Lb/rpGaf5V63XA4cdMwlQfQ7qU' - 'LZlsIkQhqriI6VEK6OjdNg8bYiy7uh4vgOzLOR0Qr8Hu6cMffI9XqUXk0OrdKTCJCS/XbKPW' - '+vqNg7f+9J7aWD2ZP/hapbTwRSBEEcMHwjatshXmnnNt5pMToox7vd/p3j4AE974dChDwICH' - 'gIL0vtPqJF3bVxVu72sBcAfYAouXEa48rpmyv/dWM1pYU89Qx163SbiK04re8iJ2G6IIh5R' - 'h2dImaVKoZ4NROQ2zzSDVBlCEFmUxWtmQWGD+iMORUNi9USsppZTb2Prdbb4Lb3NE/dOQL' - 'AwQY0+7u2NBeQnXcu5wLuncRT45TU1ru4DuN7m9z8UhuVI74nrS0sohW3UPrQHRgYHGa8V6' - 'B7Yg+VkWEUb72LFwoXKAZB96xZYuGe9HaPQAgQW9Atqjxti7WQTheUs9mdReuGSO9c3eVS' - 'IYmry1172ldzpHOZlLCsddJHc6fv3Fpaz82j4xydnbfZh2E4GDnIOJUKr1HpSfoBbxWK1' - 'KKNxwy6oQy4zzUtkGUnbjzRMwveQRshygmSoZAR3/Y7g7bDZGefqZfHOsQdHnaznFr+ro1' - 'uvkaWLowchxjewymNnfYBu+PtdFu2stwK7qVijgKsgzry+eKGL12L86igIhnXHGO1lp3uy' - 'Y3HLFhkiymfpm68j+XdD/M1eCSvXAV5j39LxnX02394trJ0l5SPr0JeXqjWK7WuTMofeTDG' - 'LyG16q6jKf08nM4QIk36iOMxqnc/xsI52bQYrBKm8FmERUw1UHd/W3wEFpxSAtJPJVbvdN' - '9d62dfpfMs712SgnLPKOHWwar3JIfrYTRg82Y7R40SZV2sywh/7BIV6g826F6qbDeeR4E5e' - 'rVjB00wcs5KHNOK2Yoq29zMSL3I50t0R0bPJVeSqC6bv/rbIIm65lcNH6RXrYpliz9kAJjT' - 'cmkf2lG7HlwCEUc4ot40U7CcCditsQvZH+fBmje/bi9Md/HfVHHkXmGsY/zTUQkHLupNr7f' - '3bo1dk/bEvSNiaDGRLGNiP0xN2xe+/F8PAoSgqmoxcp48eQKSJM2S4WixjkCf3YnXebwVI8' - 'FDWd5l2UpMUU9Yh8Wk7F14mSxOmyj8Tuv3s2msPGCIbV4yPqjcGinC8l4gpQi4YraJycRPH' - 'u7Cd3O1jjFmRCGfMi5RUE2FVioxXLPNrYnRiJzY9+CAWF2YQ9pWIPENEOaFy3YFbmYuEsUSe' - 'Wxj3git5Qo3Z4Iloztefg3rKdwqUSccShs4cEY1lyMjUFoNR236W8doeWoMlLE/xI4jE2Xnf' - 'r6I6NESEKwz8YfgFtyRjLt9ZIS+xrvRjqbKI8cO3YplUmnv0O8iN74Q/v8xbt4YFbsu8amB' - 'qxVlTiM6VpMt16xj71VOlQmsxWRIhCarrR5ui9sjur+maDJQZ2dvkzf8CJj73JxjZuxvV/g' - 'rKjKPuPai070EhoEi1EsW6f6CKBm/Wd37013Bsjlep//x9ZHdsZ1QNY6Pma3bJbArCMRrH' - '7T81zbIITGpJk9OwtIVbZ' - '/dNp0M3036d0iP91QvNopo9Ukg3P8v2wWAv6CuXaIz4TR9OMoaaXtXYZx7EjusY7FIYRJkI' - 'Z/XBQJV1qYuwsZsvyuU+9FWXMRBuw+77fwuvV4ex8O1vGY4Bg1kUkp8wfkLy7dH39hW84hE' - 'KKrhCfB2atgDFz074VaFYbpgkMcJI/V1im7Z4kyb9rQPjtuRTh5QoT1sMjVDCLTVcpN/w2qt' - 'kvI+df/QQdrz/RnJ2AGXaozIZmMsytkvi3Jm3i7omli6X+PU0PDBkccnJNWMIPn4fzk3swpn' - 'vfRf1I0eM4hokAvtFXqLn6aRkeDhGZ0VzWFwIraVO/HVzJ7ETB21AZ9G1meGr+WR9mDS3mC' - 'pLpQhbyi2LJDJvDqKFOkMw0vsw9VaYUumee3HNbb/M6NkJDPDIapB/DzHAs7oU2RWZ05Tda' - 'Fr3nRuRS8u8YJtnZMDsLAPReJ1aUxzI1CnMv/o65l9/A0tnX0Vy/gKi4ycMOIePANAjgJ' - 'cu8pO9VTWe0dxV1/bpnb10TjldirDPO4roHDttciMbEFpxziqu8YxvHMC5aFhcpTMoqEaH' - 'BxEf9+KsZLBcqmLsBqEtE4JZN6XGZi2xNjKixcY9sNQgTrDl5o892rTMLQajHHk+5CE8di' - 'fDKCVVXgvDwLptJiOcT7N5XTXglLdqmtQ1l+h6EqTYmFjJR0KZc5QlBWhY5F5BjP70bgqnD' - 'nPHSVH9zHPrbNEH6LET0HZn37+xUu5xG8DSqp0V7D0ItwVacEikdSjjjJgoqwuGvXNXGOg' - 'Z2x0yEXi0PeqFO87AiFGCkemvOKYkSF/6yS1vnLOWTaHN/VcmnSWt0eHThELBHBiCGisGra' - 'SI5l6R3qD0q05ZR4TNVHES7bnltEgcg6JF3uVlyFsBpdB+lzgdRTMHeejneZR0H1BrnKECH' - '7GyVy1BAmcsr17WxYs78QNqQF4bppFXqX9BBqIrzmcExwueASjAFzlaWncpqEpJCXVc7wv' - 'Nj7eT/BbztCaofk+k0AAAAAyBMj8AAAAAElFTkSuQmCC'; -const String base64ImageUrl = 'data:image/png;base64,$base64PngData'; - -HtmlImage createNineSliceImage() { - return HtmlImage(createDomHTMLImageElement()..src = base64ImageUrl, 60, 60); -} - -HtmlImage createTestImage({int width = 100, int height = 50}) { - final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(33, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(66, 0, 33, 50); - ctx.fill(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} - -Paragraph createTestParagraph(String text, {Color color = const Color(0xFF000000)}) { - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle( - fontFamily: 'Roboto', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - ), - ); - builder.pushStyle(TextStyle(color: color)); - builder.addText(text); - return builder.build(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart deleted file mode 100644 index 1cd8dd8e10..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart +++ /dev/null @@ -1,131 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -const Rect region = Rect.fromLTWH(0, 0, 500, 100); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -SurfacePaint makePaint() => Paint() as SurfacePaint; - -Future testMain() async { - setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); - - setUpAll(() async { - debugShowClipLayers = true; - }); - - setUp(() async { - SurfaceSceneBuilder.debugForgetFrameScene(); - }); - - group('Add picture to scene', () { - test('draw growing picture across frames', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); - - _drawTestPicture(builder, 100, false); - builder.pop(); - - final DomElement elm1 = builder.build().webOnlyRootElement!; - domDocument.body!.append(elm1); - - // Now draw picture again but at larger size. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); - // Now draw the picture at original target size, which will use a - // different code path that should normally not have width/height set - // on image element. - _drawTestPicture(builder2, 20, false); - builder2.pop(); - - elm1.remove(); - await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes', region: region); - }); - - test('draw growing picture across frames clipped', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); - - _drawTestPicture(builder, 100, true); - builder.pop(); - - final DomElement elm1 = builder.build().webOnlyRootElement!; - domDocument.body!.append(elm1); - - // Now draw picture again but at larger size. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); - _drawTestPicture(builder2, 20, true); - builder2.pop(); - - elm1.remove(); - await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes_clipped', region: region); - }); - - test('PictureInPicture', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture greenRectPicture = _drawGreenRectIntoPicture(); - - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); - canvas.drawPicture(greenRectPicture); - builder.addPicture(const Offset(10, 10), recorder.endRecording()); - - await sceneScreenshot(builder, 'canvas_draw_picture_in_picture_rect', region: region); - }); - }); -} - -HtmlImage? sharedImage; - -void _drawTestPicture(SceneBuilder builder, double targetSize, bool clipped) { - sharedImage ??= _createRealTestImage(); - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); - canvas.debugEnforceArbitraryPaint(); - if (clipped) { - canvas.clipRRect(RRect.fromLTRBR(0, 0, targetSize, targetSize, const Radius.circular(4))); - } - canvas.drawImageRect( - sharedImage!, - const Rect.fromLTWH(0, 0, 20, 20), - Rect.fromLTWH(0, 0, targetSize, targetSize), - makePaint(), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(Offset.zero, picture); -} - -Picture _drawGreenRectIntoPicture() { - final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); - canvas.drawRect( - const Rect.fromLTWH(20, 20, 50, 50), - makePaint()..color = const Color(0xFF00FF00), - ); - return recorder.endRecording(); -} - -const String _base64Encoded20x20TestImage = - 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' - 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' - 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; - -HtmlImage _createRealTestImage() { - return HtmlImage( - createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', - 20, - 20, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart deleted file mode 100644 index 859c71f64c..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart +++ /dev/null @@ -1,175 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 300, 300); - - late BitmapCanvas canvas; - late BitmapCanvas domCanvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - // setting isInsideSvgFilterTree true forces use of DOM canvas - domCanvas = BitmapCanvas(region, RenderStrategy()..isInsideSvgFilterTree = true); - }); - - tearDown(() { - canvas.rootElement.remove(); - domCanvas.rootElement.remove(); - }); - - test('draws lines with varying strokeWidth', () async { - paintLines(canvas); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_lines_thickness.png', region: region); - }); - test('draws lines with varying strokeWidth with dom canvas', () async { - paintLines(domCanvas); - - domDocument.body!.append(domCanvas.rootElement); - await matchGoldenFile('canvas_lines_thickness_dom_canvas.png', region: region); - }); - test('draws lines with negative Offset values with dom canvas', () async { - // test rendering lines correctly with negative offset when using DOM - final SurfacePaintData paintWithStyle = - SurfacePaintData() - ..color = - 0xFFE91E63 // Colors.pink - ..style = PaintingStyle.stroke - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; - - // canvas.drawLine ignores paint.style (defaults to fill) according to api docs. - // expect lines are rendered the same regardless of the set paint.style - final SurfacePaintData paintWithoutStyle = - SurfacePaintData() - ..color = - 0xFF4CAF50 // Colors.green - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; - - // test vertical, horizontal, and diagonal lines - final List points = [ - const Offset(-25, 50), - const Offset(45, 50), - const Offset(100, -25), - const Offset(100, 200), - const Offset(-150, -145), - const Offset(100, 200), - ]; - final List shiftedPoints = - points.map((Offset point) => point.translate(20, 20)).toList(); - - paintLinesFromPoints(domCanvas, paintWithStyle, points); - paintLinesFromPoints(domCanvas, paintWithoutStyle, shiftedPoints); - - domDocument.body!.append(domCanvas.rootElement); - await matchGoldenFile('canvas_lines_with_negative_offset.png', region: region); - }); - - test('drawLines method respects strokeCap with dom canvas', () async { - final SurfacePaintData paintStrokeCapRound = - SurfacePaintData() - ..color = - 0xFFE91E63 // Colors.pink - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; - - final SurfacePaintData paintStrokeCapSquare = - SurfacePaintData() - ..color = - 0xFF4CAF50 // Colors.green - ..strokeWidth = 16 - ..strokeCap = StrokeCap.square; - - final SurfacePaintData paintStrokeCapButt = - SurfacePaintData() - ..color = - 0xFFFF9800 // Colors.orange - ..strokeWidth = 16 - ..strokeCap = StrokeCap.butt; - - // test vertical, horizontal, and diagonal lines - final List points = [ - const Offset(5, 50), - const Offset(45, 50), - const Offset(100, 5), - const Offset(100, 200), - const Offset(5, 10), - const Offset(100, 200), - ]; - final List shiftedPoints = - points.map((Offset point) => point.translate(50, 50)).toList(); - final List twiceShiftedPoints = - shiftedPoints.map((Offset point) => point.translate(50, 50)).toList(); - - paintLinesFromPoints(domCanvas, paintStrokeCapRound, points); - paintLinesFromPoints(domCanvas, paintStrokeCapSquare, shiftedPoints); - paintLinesFromPoints(domCanvas, paintStrokeCapButt, twiceShiftedPoints); - - domDocument.body!.append(domCanvas.rootElement); - await matchGoldenFile('canvas_lines_with_strokeCap.png', region: region); - }); -} - -void paintLines(BitmapCanvas canvas) { - final SurfacePaintData nullPaint = - SurfacePaintData() - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final SurfacePaintData paint1 = - SurfacePaintData() - ..color = - 0xFF9E9E9E // Colors.grey - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final SurfacePaintData paint2 = - SurfacePaintData() - ..color = 0x7fff0000 - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final SurfacePaintData paint3 = - SurfacePaintData() - ..color = - 0xFF4CAF50 //Colors.green - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - // Draw markers around 100x100 box - canvas.drawLine(const Offset(50, 40), const Offset(148, 40), nullPaint); - canvas.drawLine(const Offset(50, 50), const Offset(52, 50), paint1); - canvas.drawLine(const Offset(150, 50), const Offset(148, 50), paint1); - canvas.drawLine(const Offset(50, 150), const Offset(52, 150), paint1); - canvas.drawLine(const Offset(150, 150), const Offset(148, 150), paint1); - // Draw diagonal - canvas.drawLine(const Offset(50, 50), const Offset(150, 150), paint2); - // Draw horizontal - paint3.strokeWidth = 1.0; - paint3.color = 0xFFFF0000; - canvas.drawLine(const Offset(50, 55), const Offset(150, 55), paint3); - paint3.strokeWidth = 2.0; - paint3.color = 0xFF2196F3; // Colors.blue; - canvas.drawLine(const Offset(50, 60), const Offset(150, 60), paint3); - paint3.strokeWidth = 4.0; - paint3.color = 0xFFFF9800; // Colors.orange; - canvas.drawLine(const Offset(50, 70), const Offset(150, 70), paint3); -} - -void paintLinesFromPoints(BitmapCanvas canvas, SurfacePaintData paint, List points) { - // points list contains pairs of Offset points, so for loop step is 2 - for (int i = 0; i < points.length - 1; i += 2) { - canvas.drawLine(points[i], points[i + 1], paint); - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart deleted file mode 100644 index a41312f97a..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 150, 420); - - late BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws rect with flipped coordinates L > R, T > B', () async { - paintRects(canvas); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_rect_flipped.png', region: region); - }); -} - -void paintRects(BitmapCanvas canvas) { - canvas.drawRect( - const Rect.fromLTRB(30, 40, 100, 50), - SurfacePaintData() - ..color = - 0xFF4CAF50 //Colors.green - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke, - ); - - // swap left and right. - canvas.drawRect( - const Rect.fromLTRB(100, 150, 30, 140), - SurfacePaintData() - ..color = - 0xFFF44336 //Colors.red - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke, - ); - - // Repeat above for fill - canvas.drawRect( - const Rect.fromLTRB(30, 240, 100, 250), - SurfacePaintData() - ..color = - 0xFF4CAF50 //Colors.green - ..style = PaintingStyle.fill, - ); - - // swap left and right. - canvas.drawRect( - const Rect.fromLTRB(100, 350, 30, 340), - SurfacePaintData() - ..color = - 0xFFF44336 //Colors.red - ..style = PaintingStyle.fill, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart deleted file mode 100644 index f3631c228b..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart +++ /dev/null @@ -1,119 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - late RecordingCanvas rc; - const Rect canvasRect = Rect.fromLTWH(0, 0, 500, 100); - - const Rect region = Rect.fromLTWH(8, 8, 500, 100); // Compensate for old golden tester padding - - final SurfacePaint niceRRectPaint = - SurfacePaint() - ..color = const Color.fromRGBO(250, 186, 218, 1.0) // #fabada - ..style = PaintingStyle.fill; - - // Some values to see how the algo behaves as radius get absurdly large - const List rRectRadii = [0, 10, 20, 80, 8000]; - - const Radius someFixedRadius = Radius.circular(10); - - setUp(() { - rc = RecordingCanvas(const Rect.fromLTWH(0, 0, 500, 100)); - rc.translate(10, 10); // Center - }); - - test('round square with big (equal) radius ends up as a circle', () async { - for (int i = 0; i < 5; i++) { - rc.drawRRect( - RRect.fromRectAndRadius( - Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - Radius.circular(rRectRadii[i]), - ), - niceRRectPaint, - ); - } - await canvasScreenshot(rc, 'canvas_rrect_round_square', canvasRect: canvasRect, region: region); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/62631 - test('round square with flipped left/right coordinates', () async { - rc.translate(35, 320); - rc.drawRRect( - RRect.fromRectAndRadius(const Rect.fromLTRB(-30, -100, 30, -300), const Radius.circular(30)), - niceRRectPaint, - ); - rc.drawPath( - Path() - ..moveTo(0, 0) - ..lineTo(20, 0), - niceRRectPaint, - ); - await canvasScreenshot( - rc, - 'canvas_rrect_flipped', - canvasRect: canvasRect, - region: const Rect.fromLTWH(0, 0, 100, 200), - ); - }); - - test('round rect with big radius scale down smaller radius', () async { - for (int i = 0; i < 5; i++) { - final Radius growingRadius = Radius.circular(rRectRadii[i]); - final RRect rrect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - bottomRight: someFixedRadius, - topRight: growingRadius, - bottomLeft: growingRadius, - ); - - rc.drawRRect(rrect, niceRRectPaint); - } - await canvasScreenshot( - rc, - 'canvas_rrect_overlapping_radius', - canvasRect: canvasRect, - region: region, - ); - }); - - test('diff round rect with big radius scale down smaller radius', () async { - for (int i = 0; i < 5; i++) { - final Radius growingRadius = Radius.circular(rRectRadii[i]); - final RRect outerRRect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - bottomRight: someFixedRadius, - topRight: growingRadius, - bottomLeft: growingRadius, - ); - - // Inner is half of outer, but offset a little so it looks nicer - final RRect innerRRect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble() + 5, 5, 40, 40), - bottomRight: someFixedRadius / 2, - topRight: growingRadius / 2, - bottomLeft: growingRadius / 2, - ); - - rc.drawDRRect(outerRRect, innerRRect, niceRRectPaint); - } - - await canvasScreenshot( - rc, - 'canvas_drrect_overlapping_radius', - canvasRect: canvasRect, - region: region, - ); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart deleted file mode 100644 index 74a1aabef2..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 300, 300); - - late BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws stroke joins', () async { - paintStrokeJoins(canvas); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_stroke_joins.png', region: region); - }); -} - -void paintStrokeJoins(BitmapCanvas canvas) { - canvas.drawRect( - const Rect.fromLTRB(0, 0, 300, 300), - SurfacePaintData() - ..color = 0xFFFFFFFF - ..style = PaintingStyle.fill, - ); // white - - Offset start = const Offset(20, 10); - Offset mid = const Offset(120, 10); - Offset end = const Offset(120, 20); - - final List strokeCaps = [StrokeCap.butt, StrokeCap.round, StrokeCap.square]; - for (final StrokeCap cap in strokeCaps) { - final List joints = [ - StrokeJoin.miter, - StrokeJoin.bevel, - StrokeJoin.round, - ]; - const List colors = [ - Color(0xFFF44336), - Color(0xFF4CAF50), - Color(0xFF2196F3), - ]; // red, green, blue - for (int i = 0; i < joints.length; i++) { - final StrokeJoin join = joints[i]; - final Color color = colors[i % colors.length]; - - final Path path = Path(); - path.moveTo(start.dx, start.dy); - path.lineTo(mid.dx, mid.dy); - path.lineTo(end.dx, end.dy); - canvas.drawPath( - path, - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = color.value - ..strokeJoin = join - ..strokeCap = cap, - ); - - start = start.translate(0, 20); - mid = mid.translate(0, 20); - end = end.translate(0, 20); - } - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart deleted file mode 100644 index f65e933d01..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -// 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:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 300, 300); - - late BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region, RenderStrategy()); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws rects side by side with fill and stroke', () async { - paintSideBySideRects(canvas); - - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('canvas_stroke_rects.png', region: region); - }); -} - -void paintSideBySideRects(BitmapCanvas canvas) { - canvas.drawRect( - const Rect.fromLTRB(0, 0, 300, 300), - SurfacePaintData() - ..color = 0xFFFFFFFF - ..style = PaintingStyle.fill, - ); // white - - canvas.drawRect( - const Rect.fromLTRB(0, 20, 40, 60), - SurfacePaintData() - ..style = PaintingStyle.fill - ..color = 0x7f0000ff, - ); - canvas.drawRect( - const Rect.fromLTRB(40, 20, 80, 60), - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = 0x7fff0000, - ); - - // Rotate 30 degrees (in rad: deg*pi/180) - canvas.transform(Matrix4.rotationZ(30.0 * math.pi / 180.0).storage); - - canvas.drawRect( - const Rect.fromLTRB(100, 60, 140, 100), - SurfacePaintData() - ..style = PaintingStyle.fill - ..color = 0x7fff00ff, - ); - canvas.drawRect( - const Rect.fromLTRB(140, 60, 180, 100), - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = 0x7fffff00, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/conic_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/conic_golden_test.dart deleted file mode 100644 index 1169d05e70..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/conic_golden_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(8, 8, 600, 800); // Compensate for old golden tester padding - - Future testPath(Path path, String goldenFileName) async { - const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 800); - final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, RenderStrategy()); - final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - - SurfacePaint paint = - SurfacePaint() - ..color = const Color(0x7F7F7F7F) - ..style = PaintingStyle.fill; - - canvas.drawPath(path, paint); - - paint = - SurfacePaint() - ..strokeWidth = 2.0 - ..color = const Color(0xFF7F007F) - ..style = PaintingStyle.stroke; - - canvas.drawPath(path, paint); - canvas.endRecording(); - - domDocument.body!.append(bitmapCanvas.rootElement); - canvas.apply(bitmapCanvas, canvasBounds); - await matchGoldenFile('$goldenFileName.png', region: region); - bitmapCanvas.rootElement.remove(); - } - - test('render conic with control point horizontal center', () async { - const double yStart = 20; - - const Offset p0 = Offset(25, yStart + 25); - const Offset pc = Offset(60, yStart + 150); - const Offset p2 = Offset(100, yStart + 50); - - final Path path = Path(); - path.moveTo(p0.dx, p0.dy); - path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); - path.close(); - path.moveTo(p0.dx, p0.dy + 200); - path.conicTo(pc.dx, pc.dy + 200, p2.dx, p2.dy + 200, 10); - path.close(); - - await testPath(path, 'render_conic_1_w10'); - }); - - test('render conic with control point left of start point', () async { - const double yStart = 20; - - const Offset p0 = Offset(60, yStart + 25); - const Offset pc = Offset(25, yStart + 150); - const Offset p2 = Offset(100, yStart + 50); - - final Path path = Path(); - path.moveTo(p0.dx, p0.dy); - path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); - path.close(); - path.moveTo(p0.dx, p0.dy + 200); - path.conicTo(pc.dx, pc.dy + 200, p2.dx, p2.dy + 200, 10); - path.close(); - - await testPath(path, 'render_conic_2_w10'); - }); - - test('render conic with control point above start point', () async { - const double yStart = 20; - - const Offset p0 = Offset(25, yStart + 125); - const Offset pc = Offset(60, yStart + 50); - const Offset p2 = Offset(100, yStart + 150); - - final Path path = Path(); - path.moveTo(p0.dx, p0.dy); - path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); - path.close(); - path.moveTo(p0.dx, p0.dy + 200); - path.conicTo(pc.dx, pc.dy + 200, p2.dx, p2.dy + 200, 10); - path.close(); - - await testPath(path, 'render_conic_2'); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart deleted file mode 100644 index a97c122c0b..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart +++ /dev/null @@ -1,139 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - test('rect stroke with clip', () async { - const Rect region = Rect.fromLTWH(0, 0, 250, 250); - // Set `hasParagraphs` to true to force DOM rendering. - final BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); - - const Rect rect = Rect.fromLTWH(0, 0, 150, 150); - - canvas.clipRect(rect.inflate(10.0), ClipOp.intersect); - - canvas.drawRect( - rect, - SurfacePaintData() - ..color = 0x6fff0000 - ..strokeWidth = 20.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawRect( - rect, - SurfacePaintData() - ..color = 0x6f0000ff - ..strokeWidth = 10.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawRect( - rect, - SurfacePaintData() - ..color = 0xff000000 - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke, - ); - - domDocument.body!.style.margin = '0px'; - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('rect_clip_strokes_dom.png', region: region); - canvas.rootElement.remove(); - }); - - test('rrect stroke with clip', () async { - const Rect region = Rect.fromLTWH(0, 0, 250, 250); - // Set `hasParagraphs` to true to force DOM rendering. - final BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); - - final RRect rrect = RRect.fromRectAndRadius( - const Rect.fromLTWH(0, 0, 150, 150), - const Radius.circular(20), - ); - - canvas.clipRect(rrect.outerRect.inflate(10.0), ClipOp.intersect); - - canvas.drawRRect( - rrect, - SurfacePaintData() - ..color = 0x6fff0000 - ..strokeWidth = 20.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawRRect( - rrect, - SurfacePaintData() - ..color = 0x6f0000ff - ..strokeWidth = 10.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawRRect( - rrect, - SurfacePaintData() - ..color = 0xff000000 - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke, - ); - - domDocument.body!.style.margin = '0px'; - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('rrect_clip_strokes_dom.png', region: region); - canvas.rootElement.remove(); - }); - - test('circle stroke with clip', () async { - const Rect region = Rect.fromLTWH(0, 0, 250, 250); - // Set `hasParagraphs` to true to force DOM rendering. - final BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); - - const Rect rect = Rect.fromLTWH(0, 0, 150, 150); - - canvas.clipRect(rect.inflate(10.0), ClipOp.intersect); - - canvas.drawCircle( - rect.center, - rect.width / 2, - SurfacePaintData() - ..color = 0x6fff0000 - ..strokeWidth = 20.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawCircle( - rect.center, - rect.width / 2, - SurfacePaintData() - ..color = 0x6f0000ff - ..strokeWidth = 10.0 - ..style = PaintingStyle.stroke, - ); - - canvas.drawCircle( - rect.center, - rect.width / 2, - SurfacePaintData() - ..color = 0xff000000 - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke, - ); - - domDocument.body!.style.margin = '0px'; - domDocument.body!.append(canvas.rootElement); - await matchGoldenFile('circle_clip_strokes_dom.png', region: region); - canvas.rootElement.remove(); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart deleted file mode 100644 index cfc5ac4c7b..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart +++ /dev/null @@ -1,461 +0,0 @@ -// 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:async'; -import 'dart:js_util' as js_util; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide ImageShader, TextStyle; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - setUpUnitTests(setUpTestViewDimensions: false); - - setUp(() { - GlContextCache.dispose(); - glRenderer = null; - }); - - Future testVertices( - String fileName, - Vertices vertices, - BlendMode blendMode, - Paint paint, - ) async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.drawVertices(vertices as SurfaceVertices, blendMode, paint as SurfacePaint); - await canvasScreenshot(rc, fileName, canvasRect: screenRect); - } - - test('Should draw green hairline triangles when colors array is null.', () async { - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([ - 20.0, - 20.0, - 220.0, - 10.0, - 110.0, - 220.0, - 220.0, - 320.0, - 20.0, - 310.0, - 200.0, - 420.0, - ]), - ); - await testVertices( - 'draw_vertices_hairline_triangle', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }); - - test('Should draw black hairline triangles when colors array is null' - ' and Paint() has no color.', () async { - // ignore: unused_local_variable - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - ]); - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([ - 20.0, - 20.0, - 220.0, - 10.0, - 110.0, - 220.0, - 220.0, - 320.0, - 20.0, - 310.0, - 200.0, - 420.0, - ]), - ); - await testVertices( - 'draw_vertices_hairline_triangle_black', - vertices, - BlendMode.srcOver, - Paint(), - ); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/71442. - test( - 'Should draw filled triangles when colors array is null' - ' and Paint() has color.', - () async { - // ignore: unused_local_variable - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - ]); - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([ - 20.0, - 20.0, - 220.0, - 10.0, - 110.0, - 220.0, - 220.0, - 320.0, - 20.0, - 310.0, - 200.0, - 420.0, - ]), - ); - await testVertices( - 'draw_vertices_triangle_green_filled', - vertices, - BlendMode.srcOver, - Paint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF00FF00), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test('Should draw hairline triangleFan.', () async { - final Vertices vertices = Vertices.raw( - VertexMode.triangleFan, - Float32List.fromList([ - 150.0, - 150.0, - 20.0, - 10.0, - 80.0, - 20.0, - 220.0, - 15.0, - 280.0, - 30.0, - 300.0, - 420.0, - ]), - ); - - await testVertices( - 'draw_vertices_hairline_triangle_fan', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }); - - test('Should draw hairline triangleStrip.', () async { - final Vertices vertices = Vertices.raw( - VertexMode.triangleStrip, - Float32List.fromList([ - 20.0, - 20.0, - 220.0, - 10.0, - 110.0, - 220.0, - 220.0, - 320.0, - 20.0, - 310.0, - 200.0, - 420.0, - ]), - ); - await testVertices( - 'draw_vertices_hairline_triangle_strip', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }); - - test( - 'Should draw triangles with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - ]); - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([ - 150.0, - 150.0, - 20.0, - 10.0, - 80.0, - 20.0, - 220.0, - 15.0, - 280.0, - 30.0, - 300.0, - 420.0, - ]), - colors: colors, - ); - - await testVertices( - 'draw_vertices_triangles', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw triangles with colors and indices.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF0000FF, - ]); - final Uint16List indices = Uint16List.fromList([0, 1, 2, 3, 4, 0]); - - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([ - 210.0, - 150.0, - 30.0, - 110.0, - 80.0, - 30.0, - 220.0, - 15.0, - 280.0, - 30.0, - ]), - colors: colors, - indices: indices, - ); - - rc.drawVertices(vertices as SurfaceVertices, BlendMode.srcOver, SurfacePaint()); - - await canvasScreenshot(rc, 'draw_vertices_triangles_indexed', canvasRect: screenRect); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw triangleFan with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - ]); - final Vertices vertices = Vertices.raw( - VertexMode.triangleFan, - Float32List.fromList([ - 150.0, - 150.0, - 20.0, - 10.0, - 80.0, - 20.0, - 220.0, - 15.0, - 280.0, - 30.0, - 300.0, - 420.0, - ]), - colors: colors, - ); - - await testVertices( - 'draw_vertices_triangle_fan', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw triangleStrip with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - 0xFFFF0000, - 0xFF00FF00, - 0xFF0000FF, - ]); - final Vertices vertices = Vertices.raw( - VertexMode.triangleStrip, - Float32List.fromList([ - 20.0, - 20.0, - 220.0, - 10.0, - 110.0, - 220.0, - 220.0, - 320.0, - 20.0, - 310.0, - 200.0, - 420.0, - ]), - colors: colors, - ); - await testVertices( - 'draw_vertices_triangle_strip', - vertices, - BlendMode.srcOver, - Paint()..color = const Color.fromARGB(255, 0, 128, 0), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - Future testTexture(TileMode tileMode, String filename) async { - final Uint16List indices = Uint16List.fromList([0, 1, 2, 3, 4, 0]); - - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Vertices vertices = Vertices.raw( - VertexMode.triangles, - Float32List.fromList([210.0, 150.0, 0.0, 0.0, 80.0, 30.0, 220.0, 15.0, 280.0, 30.0]), - indices: indices, - ); - - final Float32List matrix4 = Matrix4.identity().storage; - - final HtmlImage img = await createTestImage(); - final SurfacePaint paint = SurfacePaint(); - - final EngineImageShader imgShader = EngineImageShader( - img, - tileMode, - tileMode, - Float64List.fromList(matrix4), - FilterQuality.high, - ); - - paint.shader = imgShader; - - rc.drawVertices(vertices as SurfaceVertices, BlendMode.srcOver, paint); - await canvasScreenshot(rc, filename, canvasRect: screenRect); - - expect(imgShader.debugDisposed, false); - imgShader.dispose(); - expect(imgShader.debugDisposed, true); - } - - test( - 'Should draw triangle with texture and indices', - () async { - await testTexture(TileMode.clamp, 'draw_vertices_texture'); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw triangle with texture and indices', - () async { - await testTexture(TileMode.mirror, 'draw_vertices_texture_mirror'); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw triangle with texture and indices', - () async { - await testTexture(TileMode.repeated, 'draw_vertices_texture_repeated'); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); -} - -Future createTestImage({int width = 50, int height = 40}) { - final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(width / 3, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(2 * width / 3, 0, width / 3, height); - ctx.fill(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement(); - final Completer completer = Completer(); - imageElement.addEventListener( - 'load', - createDomEventListener((DomEvent event) { - completer.complete(HtmlImage(imageElement, width, height)); - }), - ); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return completer.future; -} diff --git a/engine/src/flutter/lib/web_ui/test/html/image_test.dart b/engine/src/flutter/lib/web_ui/test/html/image_test.dart deleted file mode 100644 index 66b3ff8eb5..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/image_test.dart +++ /dev/null @@ -1,181 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Matcher listEqual(List source, {int tolerance = 0}) { - return predicate((List target) { - if (source.length != target.length) { - return false; - } - for (int i = 0; i < source.length; i += 1) { - if ((source[i] - target[i]).abs() > tolerance) { - return false; - } - } - return true; - }, source.toString()); -} - -// Converts `rawPixels` into a list of bytes that represent raw pixels in rgba8888. -// -// Each element of `rawPixels` represents a bytes in order 0xRRGGBBAA, with -// pixel order Left to right, then top to bottom. -Uint8List _pixelsToBytes(List rawPixels) { - return Uint8List.fromList([ - for (final int pixel in rawPixels) ...[ - (pixel >> 24) & 0xff, // r - (pixel >> 16) & 0xff, // g - (pixel >> 8) & 0xff, // b - (pixel >> 0) & 0xff, // a - ], - ]); -} - -Future _encodeToHtmlThenDecode( - Uint8List rawBytes, - int width, - int height, { - PixelFormat pixelFormat = PixelFormat.rgba8888, -}) async { - final ImageDescriptor descriptor = ImageDescriptor.raw( - await ImmutableBuffer.fromUint8List(rawBytes), - width: width, - height: height, - pixelFormat: pixelFormat, - ); - return (await (await descriptor.instantiateCodec()).getNextFrame()).image; -} - -// This utility function detects how the current Web engine decodes pixel data. -// -// The HTML renderer uses the BMP format to display pixel data, but it used to -// use a wrong implementation. The bug has been fixed, but the fix breaks apps -// that had to provide incorrect data to work around this issue. This function -// is used in the migration guide to assist libraries that would like to run on -// both pre- and post-patch engines by testing the current behavior on a single -// pixel, making use the fact that the patch fixes the pixel order. -// -// The `format` argument is used for testing. In the actual code it should be -// replaced by `PixelFormat.rgba8888`. -// -// See also: -// -// * Patch: https://github.com/flutter/engine/pull/29448 -// * Migration guide: https://docs.flutter.dev/release/breaking-changes/raw-images-on-web-uses-correct-origin-and-colors -Future rawImageUsesCorrectBehavior(PixelFormat format) async { - final ImageDescriptor descriptor = ImageDescriptor.raw( - await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0xFF])), - width: 1, - height: 1, - pixelFormat: format, - ); - final Image image = (await (await descriptor.instantiateCodec()).getNextFrame()).image; - final Uint8List resultPixels = Uint8List.sublistView( - (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!, - ); - return resultPixels[0] == 0xED; -} - -Future testMain() async { - test('Correctly encodes an opaque image', () async { - // A 2x2 testing image without transparency. - final Image sourceImage = await _encodeToHtmlThenDecode( - _pixelsToBytes([0xFF0102FF, 0x04FE05FF, 0x0708FDFF, 0x0A0B0C00]), - 2, - 2, - ); - final Uint8List actualPixels = Uint8List.sublistView( - (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!, - ); - // The `benchmarkPixels` is identical to `sourceImage` except for the fully - // transparent last pixel, whose channels are turned 0. - final Uint8List benchmarkPixels = _pixelsToBytes([ - 0xFF0102FF, - 0x04FE05FF, - 0x0708FDFF, - 0x00000000, - ]); - expect(actualPixels, listEqual(benchmarkPixels)); - }); - - test('Correctly encodes an opaque image in bgra8888', () async { - // A 2x2 testing image without transparency. - final Image sourceImage = await _encodeToHtmlThenDecode( - _pixelsToBytes([0xFF0102FF, 0x04FE05FF, 0x0708FDFF, 0x0A0B0C00]), - 2, - 2, - pixelFormat: PixelFormat.bgra8888, - ); - final Uint8List actualPixels = Uint8List.sublistView( - (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!, - ); - // The `benchmarkPixels` is the same as `sourceImage` except that the R and - // G channels are swapped and the fully transparent last pixel is turned 0. - final Uint8List benchmarkPixels = _pixelsToBytes([ - 0x0201FFFF, - 0x05FE04FF, - 0xFD0807FF, - 0x00000000, - ]); - expect(actualPixels, listEqual(benchmarkPixels)); - }); - - test('Correctly encodes a transparent image', () async { - // A 2x2 testing image with transparency. - final Image sourceImage = await _encodeToHtmlThenDecode( - _pixelsToBytes([0xFF800006, 0xFF800080, 0xFF8000C0, 0xFF8000FF]), - 2, - 2, - ); - final Image blueBackground = await _encodeToHtmlThenDecode( - _pixelsToBytes([0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF]), - 2, - 2, - ); - // The standard way of testing the raw bytes of `sourceImage` is to draw - // the image onto a canvas and fetch its data (see HtmlImage.toByteData). - // But here, we draw an opaque background first before drawing the image, - // and test if the blended result is expected. - // - // This is because, if we only draw the `sourceImage`, the resulting pixels - // will be slightly off from the raw pixels. The reason is unknown, but - // very likely because the canvas.getImageData introduces rounding errors - // if any pixels are left semi-transparent, which might be caused by - // converting to and from pre-multiplied values. See - // https://github.com/flutter/flutter/issues/92958 . - final DomCanvasElement canvas = - createDomCanvasElement() - ..width = 2 - ..height = 2; - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.drawImage((blueBackground as HtmlImage).imgElement, 0, 0); - ctx.drawImage((sourceImage as HtmlImage).imgElement, 0, 0); - - final DomImageData imageData = ctx.getImageData(0, 0, 2, 2); - final List actualPixels = imageData.data; - - final Uint8List benchmarkPixels = _pixelsToBytes([ - 0x0603F9FF, - 0x80407FFF, - 0xC0603FFF, - 0xFF8000FF, - ]); - expect(actualPixels, listEqual(benchmarkPixels, tolerance: 1)); - }); - - test('The behavior detector works correctly', () async { - expect(await rawImageUsesCorrectBehavior(PixelFormat.rgba8888), true); - expect(await rawImageUsesCorrectBehavior(PixelFormat.bgra8888), false); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/path_metrics_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/path_metrics_golden_test.dart deleted file mode 100644 index a536e7788a..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/path_metrics_golden_test.dart +++ /dev/null @@ -1,176 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import '../common/matchers.dart'; -import '../common/test_initialization.dart'; -import 'screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - const Color black12Color = Color(0x1F000000); - const Color redAccentColor = Color(0xFFFF1744); - const double kDashLength = 5.0; - - setUpUnitTests(setUpTestViewDimensions: false); - - test('Should calculate tangent on line', () async { - final Path path = Path(); - path.moveTo(50, 130); - path.lineTo(150, 20); - - final PathMetric metric = path.computeMetrics().first; - final Tangent t = metric.getTangentForOffset(50.0)!; - expect(t.position.dx, within(from: 83.633, distance: 0.01)); - expect(t.position.dy, within(from: 93.0, distance: 0.01)); - expect(t.vector.dx, within(from: 0.672, distance: 0.01)); - expect(t.vector.dy, within(from: -0.739, distance: 0.01)); - }); - - test('Should calculate tangent on cubic curve', () async { - final Path path = Path(); - const double p1x = 240; - const double p1y = 120; - const double p2x = 320; - const double p2y = 25; - path.moveTo(150, 20); - path.quadraticBezierTo(p1x, p1y, p2x, p2y); - final PathMetric metric = path.computeMetrics().first; - final Tangent t = metric.getTangentForOffset(50.0)!; - expect(t.position.dx, within(from: 187.25, distance: 0.01)); - expect(t.position.dy, within(from: 53.33, distance: 0.01)); - expect(t.vector.dx, within(from: 0.82, distance: 0.01)); - expect(t.vector.dy, within(from: 0.56, distance: 0.01)); - }); - - test('Should calculate tangent on quadratic curve', () async { - final Path path = Path(); - const double p0x = 150; - const double p0y = 20; - const double p1x = 320; - const double p1y = 25; - path.moveTo(150, 20); - path.quadraticBezierTo(p0x, p0y, p1x, p1y); - final PathMetric metric = path.computeMetrics().first; - final Tangent t = metric.getTangentForOffset(50.0)!; - expect(t.position.dx, within(from: 199.82, distance: 0.01)); - expect(t.position.dy, within(from: 21.46, distance: 0.01)); - expect(t.vector.dx, within(from: 0.99, distance: 0.01)); - expect(t.vector.dy, within(from: 0.02, distance: 0.01)); - }); - - // Test for extractPath to draw 5 pixel length dashed line using quad curve. - test('Should draw dashed line on quadratic curve.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final SurfacePaint paint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 3 - ..color = black12Color; - final SurfacePaint redPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = redAccentColor; - - final SurfacePath path = SurfacePath(); - path.moveTo(50, 130); - path.lineTo(150, 20); - const double p1x = 240; - const double p1y = 120; - const double p2x = 320; - const double p2y = 25; - path.quadraticBezierTo(p1x, p1y, p2x, p2y); - - rc.drawPath(path, paint); - - const double t0 = 0.2; - const double t1 = 0.7; - - final List metrics = path.computeMetrics().toList(); - double totalLength = 0; - for (final PathMetric m in metrics) { - totalLength += m.length; - } - final Path dashedPath = Path(); - for (final PathMetric measurePath in path.computeMetrics()) { - double distance = totalLength * t0; - bool draw = true; - while (distance < measurePath.length * t1) { - const double length = kDashLength; - if (draw) { - dashedPath.addPath(measurePath.extractPath(distance, distance + length), Offset.zero); - } - distance += length; - draw = !draw; - } - } - rc.drawPath(dashedPath, redPaint); - await canvasScreenshot(rc, 'path_dash_quadratic', canvasRect: screenRect); - }); - - // Test for extractPath to draw 5 pixel length dashed line using cubic curve. - test('Should draw dashed line on cubic curve.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final SurfacePaint paint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 3 - ..color = black12Color; - final SurfacePaint redPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = redAccentColor; - - final Path path = Path(); - path.moveTo(50, 130); - path.lineTo(150, 20); - const double p1x = 40; - const double p1y = 120; - const double p2x = 300; - const double p2y = 130; - const double p3x = 320; - const double p3y = 25; - path.cubicTo(p1x, p1y, p2x, p2y, p3x, p3y); - - rc.drawPath(path, paint); - - const double t0 = 0.2; - const double t1 = 0.7; - - final List metrics = path.computeMetrics().toList(); - double totalLength = 0; - for (final PathMetric m in metrics) { - totalLength += m.length; - } - final Path dashedPath = Path(); - for (final PathMetric measurePath in path.computeMetrics()) { - double distance = totalLength * t0; - bool draw = true; - while (distance < measurePath.length * t1) { - const double length = kDashLength; - if (draw) { - dashedPath.addPath(measurePath.extractPath(distance, distance + length), Offset.zero); - } - distance += length; - draw = !draw; - } - } - rc.drawPath(dashedPath, redPaint); - await canvasScreenshot(rc, 'path_dash_cubic', canvasRect: screenRect); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/path_ref_test.dart b/engine/src/flutter/lib/web_ui/test/html/path_ref_test.dart deleted file mode 100644 index 531ca97d45..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/path_ref_test.dart +++ /dev/null @@ -1,102 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -// TODO(yjbanov): https://github.com/flutter/flutter/issues/76885 -const bool issue76885Exists = true; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - test('PathRef.getRRect with radius', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, const Radius.circular(2)); - path.addRRect(rrect); - expect(path.toRoundedRect(), rrect); - }); - - test('PathRef.getRRect with radius larger than rect', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, const Radius.circular(20)); - path.addRRect(rrect); - expect( - path.toRoundedRect(), - // Path.addRRect will correct the radius to fit the dimensions, so when - // extracting the rrect out of the path we don't get the original. - RRect.fromLTRBR(0, 0, 10, 10, const Radius.circular(5)), - ); - }); - - test('PathRef.getRRect with zero radius', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.zero); - path.addRRect(rrect); - expect(path.toRoundedRect(), isNull); - expect(path.toRect(), rrect.outerRect); - }); - - test('PathRef.getRRect elliptical', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, const Radius.elliptical(2, 4)); - path.addRRect(rrect); - expect(path.toRoundedRect(), rrect); - }); - - test('PathRef.getRRect elliptical zero x', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, const Radius.elliptical(0, 3)); - path.addRRect(rrect); - expect(path.toRoundedRect(), isNull); - expect(path.toRect(), rrect.outerRect); - }); - - test('PathRef.getRRect elliptical zero y', () { - final SurfacePath path = SurfacePath(); - final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, const Radius.elliptical(3, 0)); - path.addRRect(rrect); - expect(path.toRoundedRect(), isNull); - expect(path.toRect(), rrect.outerRect); - }); - - test('PathRef.getRect returns a Rect from a valid Path and null otherwise', () { - final SurfacePath path = SurfacePath(); - // Draw a line - path.moveTo(0, 0); - path.lineTo(10, 0); - expect(path.pathRef.getRect(), isNull); - // Draw two other lines to get a valid rectangle - path.lineTo(10, 10); - path.lineTo(0, 10); - expect(path.pathRef.getRect(), const Rect.fromLTWH(0, 0, 10, 10)); - }); - - // Regression test for https://github.com/flutter/flutter/issues/111750 - test('PathRef.getRect returns Rect with positive width and height', () { - final SurfacePath path = SurfacePath(); - // Draw a rectangle starting from bottom right corner - path.moveTo(10, 10); - path.lineTo(0, 10); - path.lineTo(0, 0); - path.lineTo(10, 0); - // pathRef.getRect() should return a rectangle with positive height and width - expect(path.pathRef.getRect(), const Rect.fromLTWH(0, 0, 10, 10)); - }); - - // This test demonstrates the issue with attempting to reconstruct an RRect - // with imprecision introduced by comparing double values. We should fix this - // by removing the need to reconstruct rrects: - // https://github.com/flutter/flutter/issues/76885 - test('PathRef.getRRect with nearly zero corner', () { - final SurfacePath path = SurfacePath(); - final RRect original = RRect.fromLTRBR(0, 0, 10, 10, const Radius.elliptical(0.00000001, 5)); - path.addRRect(original); - expect(path.toRoundedRect(), original); - }, skip: issue76885Exists); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/path_test.dart b/engine/src/flutter/lib/web_ui/test/html/path_test.dart deleted file mode 100644 index 331bdfaa58..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/path_test.dart +++ /dev/null @@ -1,596 +0,0 @@ -// 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:js_util' as js_util; -import 'dart:typed_data'; -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide window; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; - -import '../common/matchers.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Path', () { - test('Should have no subpaths when created', () { - final SurfacePath path = SurfacePath(); - expect(path.isEmpty, isTrue); - }); - - test('LineTo should add command', () { - final SurfacePath path = SurfacePath(); - path.moveTo(5.0, 10.0); - path.lineTo(20.0, 40.0); - path.lineTo(30.0, 50.0); - expect(path.pathRef.countPoints(), 3); - expect(path.pathRef.atPoint(2).dx, 30.0); - expect(path.pathRef.atPoint(2).dy, 50.0); - }); - - test('LineTo should add moveTo 0,0 when first call to Path API', () { - final SurfacePath path = SurfacePath(); - path.lineTo(20.0, 40.0); - expect(path.pathRef.countPoints(), 2); - expect(path.pathRef.atPoint(0).dx, 0); - expect(path.pathRef.atPoint(0).dy, 0); - expect(path.pathRef.atPoint(1).dx, 20.0); - expect(path.pathRef.atPoint(1).dy, 40.0); - }); - - test('relativeLineTo should increments currentX', () { - final SurfacePath path = SurfacePath(); - path.moveTo(5.0, 10.0); - path.lineTo(20.0, 40.0); - path.relativeLineTo(5.0, 5.0); - expect(path.pathRef.countPoints(), 3); - expect(path.pathRef.atPoint(2).dx, 25.0); - expect(path.pathRef.atPoint(2).dy, 45.0); - }); - - test('Should allow calling relativeLineTo before moveTo', () { - final SurfacePath path = SurfacePath(); - path.relativeLineTo(5.0, 5.0); - path.moveTo(5.0, 10.0); - expect(path.pathRef.countPoints(), 3); - expect(path.pathRef.atPoint(1).dx, 5.0); - expect(path.pathRef.atPoint(1).dy, 5.0); - expect(path.pathRef.atPoint(2).dx, 5.0); - expect(path.pathRef.atPoint(2).dy, 10.0); - }); - - test('Should allow relativeLineTo after reset', () { - final SurfacePath path = SurfacePath(); - final Path subPath = Path(); - subPath.moveTo(50.0, 60.0); - subPath.lineTo(200.0, 200.0); - path.extendWithPath(subPath, Offset.zero); - path.reset(); - path.relativeLineTo(5.0, 5.0); - expect(path.pathRef.countPoints(), 2); - expect(path.pathRef.atPoint(0).dx, 0); - expect(path.pathRef.atPoint(0).dy, 0); - expect(path.pathRef.atPoint(1).dx, 5.0); - }); - - test('Should detect rectangular path', () { - final SurfacePath path = SurfacePath(); - path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - expect(path.toRect(), const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - }); - - test('Should detect horizontal line path', () { - SurfacePath path = SurfacePath(); - path.moveTo(10, 20); - path.lineTo(100, 0); - expect(path.toStraightLine(), null); - path = SurfacePath(); - path.moveTo(10, 20); - path.lineTo(200, 20); - final Rect r = path.toStraightLine()!; - expect(r, equals(const Rect.fromLTRB(10, 20, 200, 20))); - }); - - test('Should detect vertical line path', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 20); - path.lineTo(10, 200); - final Rect r = path.toStraightLine()!; - expect(r, equals(const Rect.fromLTRB(10, 20, 10, 200))); - }); - - test('Should detect non rectangular path if empty', () { - final SurfacePath path = SurfacePath(); - expect(path.toRect(), null); - }); - - test('Should detect non rectangular path if there are multiple subpaths', () { - final SurfacePath path = SurfacePath(); - path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - path.addRect(const Rect.fromLTWH(5.0, 6.0, 7.0, 8.0)); - expect(path.toRect(), null); - }); - - test('Should detect rounded rectangular path', () { - final SurfacePath path = SurfacePath(); - path.addRRect( - RRect.fromRectAndRadius( - const Rect.fromLTRB(1.0, 2.0, 30.0, 40.0), - const Radius.circular(2.0), - ), - ); - expect( - path.toRoundedRect(), - RRect.fromRectAndRadius( - const Rect.fromLTRB(1.0, 2.0, 30.0, 40.0), - const Radius.circular(2.0), - ), - ); - }); - - test('Should detect non rounded rectangular path if empty', () { - final SurfacePath path = SurfacePath(); - expect(path.toRoundedRect(), null); - }); - - test('Should detect rectangular path is not round', () { - final SurfacePath path = SurfacePath(); - path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - expect(path.toRoundedRect(), null); - }); - - test('Should detect non rounded rectangular path if there are ' - 'multiple subpaths', () { - final SurfacePath path = SurfacePath(); - path.addRRect( - RRect.fromRectAndRadius( - const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), - const Radius.circular(2.0), - ), - ); - path.addRRect( - RRect.fromRectAndRadius( - const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), - const Radius.circular(2.0), - ), - ); - expect(path.toRoundedRect(), null); - }); - - test('Should compute bounds as empty for empty and moveTo only path', () { - final Path emptyPath = Path(); - expect(emptyPath.getBounds(), Rect.zero); - - final SurfacePath path = SurfacePath(); - path.moveTo(50, 60); - expect(path.getBounds(), const Rect.fromLTRB(50, 60, 50, 60)); - }); - - test('Should compute bounds for multiple addRect calls', () { - final Path emptyPath = Path(); - expect(emptyPath.getBounds(), Rect.zero); - - final SurfacePath path = SurfacePath(); - path.addRect(const Rect.fromLTWH(0, 0, 270, 45)); - path.addRect(const Rect.fromLTWH(134.5, 0, 1, 45)); - expect(path.getBounds(), const Rect.fromLTRB(0, 0, 270, 45)); - }); - - test('Should compute bounds for addRRect', () { - SurfacePath path = SurfacePath(); - const Rect bounds = Rect.fromLTRB(30, 40, 400, 300); - RRect rrect = RRect.fromRectAndCorners( - bounds, - topLeft: const Radius.elliptical(1, 2), - topRight: const Radius.elliptical(3, 4), - bottomLeft: const Radius.elliptical(5, 6), - bottomRight: const Radius.elliptical(7, 8), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - path = SurfacePath(); - rrect = RRect.fromRectAndCorners( - bounds, - topLeft: const Radius.elliptical(0, 2), - topRight: const Radius.elliptical(3, 4), - bottomLeft: const Radius.elliptical(5, 6), - bottomRight: const Radius.elliptical(7, 8), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - path = SurfacePath(); - rrect = RRect.fromRectAndCorners( - bounds, - topRight: const Radius.elliptical(3, 4), - bottomLeft: const Radius.elliptical(5, 6), - bottomRight: const Radius.elliptical(7, 8), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - path = SurfacePath(); - rrect = RRect.fromRectAndCorners( - bounds, - topLeft: const Radius.elliptical(1, 2), - bottomLeft: const Radius.elliptical(5, 6), - bottomRight: const Radius.elliptical(7, 8), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - path = SurfacePath(); - rrect = RRect.fromRectAndCorners( - bounds, - topLeft: const Radius.elliptical(1, 2), - topRight: const Radius.elliptical(3, 4), - bottomRight: const Radius.elliptical(7, 8), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - path = SurfacePath(); - rrect = RRect.fromRectAndCorners( - bounds, - topLeft: const Radius.elliptical(1, 2), - topRight: const Radius.elliptical(3, 4), - bottomLeft: const Radius.elliptical(5, 6), - ); - path.addRRect(rrect); - expect(path.getBounds(), bounds); - expect(path.toRoundedRect(), rrect); - }); - - test('Should compute bounds for lines', () { - final SurfacePath path = SurfacePath(); - path.moveTo(25, 30); - path.lineTo(100, 200); - expect(path.getBounds(), const Rect.fromLTRB(25, 30, 100, 200)); - - final SurfacePath path2 = SurfacePath(); - path2.moveTo(250, 300); - path2.lineTo(50, 60); - expect(path2.getBounds(), const Rect.fromLTRB(50, 60, 250, 300)); - }); - - test('Should compute bounds for polygon', () { - final SurfacePath path = SurfacePath(); - path.addPolygon(const [ - Offset(50, 100), - Offset(250, 100), - Offset(152, 180), - Offset(159, 200), - Offset(151, 190), - ], true); - expect(path.getBounds(), const Rect.fromLTRB(50, 100, 250, 200)); - }); - - test('Should compute bounds for quadraticBezierTo', () { - final SurfacePath path1 = SurfacePath(); - path1.moveTo(285.2, 682.1); - path1.quadraticBezierTo(432.0, 431.4, 594.9, 681.2); - expect( - path1.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(285.2, 556.5, 594.9, 682.1)), - ); - - // Control point below start , end. - final SurfacePath path2 = SurfacePath(); - path2.moveTo(285.2, 682.1); - path2.quadraticBezierTo(447.4, 946.8, 594.9, 681.2); - expect( - path2.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(285.2, 681.2, 594.9, 814.2)), - ); - - // Control point to the right of end point. - final SurfacePath path3 = SurfacePath(); - path3.moveTo(468.3, 685.6); - path3.quadraticBezierTo(644.7, 555.2, 594.9, 681.2); - expect( - path3.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(468.3, 619.3, 605.9, 685.6)), - ); - }); - - test('Should compute bounds for cubicTo', () { - final SurfacePath path1 = SurfacePath(); - path1.moveTo(220, 300); - path1.cubicTo(230, 120, 400, 125, 410, 280); - expect( - path1.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 164.3, 410.0, 300.0)), - ); - - // control point 1 to the right of control point 2 - final SurfacePath path2 = SurfacePath(); - path2.moveTo(220, 300); - path2.cubicTo(564.2, 13.7, 400.0, 125.0, 410.0, 280.0); - expect( - path2.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 122.8, 440.5, 300.0)), - ); - - // control point 1 to the right of control point 2 inflection - final SurfacePath path3 = SurfacePath(); - path3.moveTo(220, 300); - path3.cubicTo(839.8, 67.9, 400.0, 125.0, 410.0, 280.0); - expect( - path3.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 144.5, 552.1, 300.0)), - ); - - // control point 1 below and between start and end points - final SurfacePath path4 = SurfacePath(); - path4.moveTo(220.0, 300.0); - path4.cubicTo(354.8, 388.3, 400.0, 125.0, 410.0, 280.0); - expect( - path4.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 230.0, 410.0, 318.6)), - ); - - // control points inverted below - final SurfacePath path5 = SurfacePath(); - path5.moveTo(220.0, 300.0); - path5.cubicTo(366.5, 487.3, 256.4, 489.9, 410.0, 280.0); - expect( - path5.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 280.0, 410.0, 439.0)), - ); - - // control points inverted below wide - final SurfacePath path6 = SurfacePath(); - path6.moveTo(220.0, 300.0); - path6.cubicTo(496.1, 485.5, 121.4, 491.6, 410.0, 280.0); - expect( - path6.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 280.0, 410.0, 439.0)), - ); - - // control point 2 and end point swapped - final SurfacePath path7 = SurfacePath(); - path7.moveTo(220.0, 300.0); - path7.cubicTo(230.0, 120.0, 394.5, 296.1, 382.3, 124.1); - expect( - path7.getBounds(), - within(distance: 0.1, from: const Rect.fromLTRB(220.0, 124.1, 382.9, 300.0)), - ); - }); - - // Regression test for https://github.com/flutter/flutter/issues/46813. - test('Should deep copy path', () { - final SurfacePath path = SurfacePath(); - path.moveTo(25, 30); - path.lineTo(100, 200); - expect(path.getBounds(), const Rect.fromLTRB(25, 30, 100, 200)); - - final SurfacePath path2 = SurfacePath.from(path); - path2.lineTo(250, 300); - expect(path2.getBounds(), const Rect.fromLTRB(25, 30, 250, 300)); - // Expect original path to stay the same. - expect(path.getBounds(), const Rect.fromLTRB(25, 30, 100, 200)); - }); - - test('Should handle contains inclusive right,bottom coordinates', () { - final Path path = Path(); - path.moveTo(50, 60); - path.lineTo(110, 60); - path.lineTo(110, 190); - path.lineTo(50, 190); - path.close(); - expect(path.contains(const Offset(80, 190)), isTrue); - expect(path.contains(const Offset(110, 80)), isTrue); - expect(path.contains(const Offset(110, 190)), isTrue); - expect(path.contains(const Offset(110, 191)), isFalse); - }); - - test('Should not contain top-left of beveled border', () { - final Path path = Path(); - path.moveTo(10, 25); - path.lineTo(15, 20); - path.lineTo(25, 20); - path.lineTo(30, 25); - path.lineTo(30, 35); - path.lineTo(25, 40); - path.lineTo(15, 40); - path.lineTo(10, 35); - path.close(); - expect(path.contains(const Offset(10, 20)), isFalse); - }); - - test('Computes contains for cubic curves', () { - final Path path = Path(); - path.moveTo(10, 25); - path.cubicTo(10, 20, 10, 20, 20, 15); - path.lineTo(25, 20); - path.cubicTo(30, 20, 30, 20, 30, 25); - path.lineTo(30, 35); - path.cubicTo(30, 40, 30, 40, 25, 40); - path.lineTo(15, 40); - path.cubicTo(10, 40, 10, 40, 10, 35); - path.close(); - expect(path.contains(const Offset(10, 20)), isFalse); - expect(path.contains(const Offset(30, 40)), isFalse); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44470 - test( - 'Should handle contains for devicepixelratio != 1.0', - () { - js_util.setProperty(domWindow, 'devicePixelRatio', 4.0); - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(4.0); - final Path path = - Path() - ..moveTo(50, 0) - ..lineTo(100, 100) - ..lineTo(0, 100) - ..lineTo(50, 0) - ..close(); - expect(path.contains(const Offset(50, 50)), isTrue); - js_util.setProperty(domWindow, 'devicePixelRatio', 1.0); - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(1.0); - // TODO(ferhat): Investigate failure on CI. Locally this passes. - // [Exception... "Failure" nsresult: "0x80004005 (NS_ERROR_FAILURE)" - }, - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox, - ); - - // Path contains should handle case where invalid RRect with large - // corner radius is used for hit test. Use case is a RenderPhysicalShape - // with a clipper that contains RRect of width/height 50 but corner radius - // of 100. - // - // Regression test for https://github.com/flutter/flutter/issues/48887 - test('Should hit test correctly for malformed rrect', () { - // Correctly formed rrect. - final Path path1 = - Path()..addRRect(RRect.fromLTRBR(50, 50, 100, 100, const Radius.circular(20))); - expect(path1.contains(const Offset(75, 75)), isTrue); - expect(path1.contains(const Offset(52, 75)), isTrue); - expect(path1.contains(const Offset(50, 50)), isFalse); - expect(path1.contains(const Offset(100, 50)), isFalse); - expect(path1.contains(const Offset(100, 100)), isFalse); - expect(path1.contains(const Offset(50, 100)), isFalse); - - final Path path2 = - Path()..addRRect(RRect.fromLTRBR(50, 50, 100, 100, const Radius.circular(100))); - expect(path2.contains(const Offset(75, 75)), isTrue); - expect(path2.contains(const Offset(52, 75)), isTrue); - expect(path2.contains(const Offset(50, 50)), isFalse); - expect(path2.contains(const Offset(100, 50)), isFalse); - expect(path2.contains(const Offset(100, 100)), isFalse); - expect(path2.contains(const Offset(50, 100)), isFalse); - }); - - test('Should set segment masks', () { - final SurfacePath path = SurfacePath(); - path.pathRef.computeSegmentMask(); - expect(path.pathRef.segmentMasks, 0); - path.moveTo(20, 40); - path.pathRef.computeSegmentMask(); - expect(path.pathRef.segmentMasks, 0); - path.lineTo(200, 40); - path.pathRef.computeSegmentMask(); - expect(path.pathRef.segmentMasks, SPathSegmentMask.kLine_SkPathSegmentMask); - }); - - test('Should convert conic to quad when approximation error is small', () { - final Conic conic = Conic( - 120.0, - 20.0, - 160.99470420829266, - 20.0, - 190.19301120261332, - 34.38770865870253, - 0.9252691032413082, - ); - expect(conic.toQuads().length, 3); - }); - - test('Should be able to construct from empty path', () { - final SurfacePath path = SurfacePath(); - expect(path.isEmpty, isTrue); - final SurfacePath path2 = SurfacePath.from(path); - expect(path2.isEmpty, isTrue); - }); - }); - - group('PathRef', () { - test('Should return empty when created', () { - final PathRef pathRef = PathRef(); - expect(pathRef.isEmpty, isTrue); - }); - - test('Should return non-empty when mutated', () { - final PathRef pathRef = PathRef(); - pathRef.growForVerb(SPath.kMoveVerb, 0); - expect(pathRef.isEmpty, isFalse); - }); - }); - group('PathRefIterator', () { - test('Should iterate through empty path', () { - final Float32List points = Float32List(20); - final PathRef pathRef = PathRef(); - final PathRefIterator iter = PathRefIterator(pathRef); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should iterate through verbs', () { - final Float32List points = Float32List(20); - final PathRef pathRef = PathRef(); - pathRef.growForVerb(SPath.kMoveVerb, 0); - pathRef.growForVerb(SPath.kLineVerb, 0); - pathRef.growForVerb(SPath.kQuadVerb, 0); - pathRef.growForVerb(SPath.kCubicVerb, 0); - pathRef.growForVerb(SPath.kConicVerb, 0.8); - pathRef.growForVerb(SPath.kLineVerb, 0.8); - final PathRefIterator iter = PathRefIterator(pathRef); - expect(iter.next(points), SPath.kMoveVerb); - expect(iter.next(points), SPath.kLineVerb); - expect(iter.next(points), SPath.kQuadVerb); - expect(iter.next(points), SPath.kCubicVerb); - expect(iter.next(points), SPath.kConicVerb); - expect(iter.next(points), SPath.kLineVerb); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should iterate by index through empty path', () { - final PathRef pathRef = PathRef(); - final PathRefIterator iter = PathRefIterator(pathRef); - expect(iter.nextIndex(), SPath.kDoneVerb); - }); - - test('Should iterate through contours', () { - final PathRef pathRef = PathRef(); - pathRef.growForVerb(SPath.kMoveVerb, 0); - pathRef.growForVerb(SPath.kLineVerb, 0); - pathRef.growForVerb(SPath.kQuadVerb, 0); - pathRef.growForVerb(SPath.kCubicVerb, 0); - pathRef.growForVerb(SPath.kMoveVerb, 0); - pathRef.growForVerb(SPath.kConicVerb, 0.8); - pathRef.growForVerb(SPath.kLineVerb, 0.8); - pathRef.growForVerb(SPath.kCloseVerb, 0.8); - pathRef.growForVerb(SPath.kMoveVerb, 0); - pathRef.growForVerb(SPath.kLineVerb, 0); - pathRef.growForVerb(SPath.kLineVerb, 0); - final PathRefIterator iter = PathRefIterator(pathRef); - int start = iter.pointIndex; - int end = iter.skipToNextContour(); - expect(end - start, 7); - - start = end; - end = iter.skipToNextContour(); - expect(end - start, 4); - - start = end; - end = iter.skipToNextContour(); - expect(end - start, 3); - - start = end; - end = iter.skipToNextContour(); - expect(start, end); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/68702. - test('Path should return correct bounds after transform', () { - final Path path1 = - Path() - ..moveTo(100, 100) - ..lineTo(200, 100) - ..lineTo(150, 200) - ..close(); - final SurfacePath path2 = Path.from(path1) as SurfacePath; - final Rect bounds = path2.pathRef.getBounds(); - final SurfacePath transformedPath = path2.transform( - Matrix4.identity().scaled(0.5, 0.5).toFloat64(), - ); - expect(transformedPath.pathRef.getBounds(), isNot(bounds)); - }); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/path_to_svg_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/path_to_svg_golden_test.dart deleted file mode 100644 index a2f8e7df5f..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/path_to_svg_golden_test.dart +++ /dev/null @@ -1,229 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -enum PaintMode { kStrokeAndFill, kStroke, kFill, kStrokeWidthOnly } - -Future testMain() async { - const Rect region = Rect.fromLTWH(8, 8, 600, 400); // Compensate for old golden tester padding - - Future testPath( - Path path, - String goldenFileName, { - SurfacePaint? paint, - PaintMode mode = PaintMode.kStrokeAndFill, - }) async { - const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 400); - final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, RenderStrategy()); - final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - - final bool enableFill = mode == PaintMode.kStrokeAndFill || mode == PaintMode.kFill; - if (enableFill) { - paint ??= - SurfacePaint() - ..color = const Color(0x807F7F7F) - ..style = PaintingStyle.fill; - canvas.drawPath(path, paint); - } - - if (mode == PaintMode.kStrokeAndFill || mode == PaintMode.kStroke) { - paint = - SurfacePaint() - ..strokeWidth = 2 - ..color = enableFill ? const Color(0xFFFF0000) : const Color(0xFF000000) - ..style = PaintingStyle.stroke; - } - - if (mode == PaintMode.kStrokeWidthOnly) { - paint = - SurfacePaint() - ..color = const Color(0xFF4060E0) - ..strokeWidth = 10; - } - - canvas.drawPath(path, paint!); - - final DomElement svgElement = pathToSvgElement(path, paint, enableFill); - - canvas.endRecording(); - canvas.apply(bitmapCanvas, canvasBounds); - - final DomElement sceneElement = createDomElement('flt-scene'); - domDocument.body!.append(sceneElement); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - sceneElement.append(bitmapCanvas.rootElement); - sceneElement.append(svgElement); - - await matchGoldenFile('$goldenFileName.png', region: region); - - bitmapCanvas.rootElement.remove(); - svgElement.remove(); - } - - tearDown(() { - domDocument.body!.clearChildren(); - }); - - test('render line strokes', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.lineTo(200, 300); - await testPath( - path, - 'svg_stroke_line', - paint: - SurfacePaint() - ..color = const Color(0xFFFF0000) - ..strokeWidth = 2.0 - ..style = PaintingStyle.stroke, - ); - }); - - test('render quad bezier curve', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.quadraticBezierTo(200, 60, 50, 200); - await testPath(path, 'svg_quad_bezier'); - }); - - test('render cubic curve', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.cubicTo(200, 60, -100, -50, 150, 200); - await testPath(path, 'svg_cubic_bezier'); - }); - - test('render arcs', () async { - final List arcs = [ - ArcSample(Offset.zero, distance: 20), - ArcSample(const Offset(200, 0), largeArc: true, distance: 20), - ArcSample(Offset.zero, clockwise: true, distance: 20), - ArcSample(const Offset(200, 0), largeArc: true, clockwise: true, distance: 20), - ArcSample(Offset.zero, distance: -20), - ArcSample(const Offset(200, 0), largeArc: true, distance: -20), - ArcSample(Offset.zero, clockwise: true, distance: -20), - ArcSample(const Offset(200, 0), largeArc: true, clockwise: true, distance: -20), - ]; - int sampleIndex = 0; - for (final ArcSample sample in arcs) { - ++sampleIndex; - final Path path = sample.createPath(); - await testPath(path, 'svg_arc_$sampleIndex'); - } - }); - - test('render rect', () async { - final Path path = Path(); - path.addRect(const Rect.fromLTRB(15, 15, 60, 20)); - path.addRect(const Rect.fromLTRB(35, 160, 15, 100)); - await testPath(path, 'svg_rect'); - }); - - test('render notch', () async { - final Path path = Path(); - path.moveTo(0, 0); - path.lineTo(83, 0); - path.quadraticBezierTo(98, 0, 99.97, 7.8); - path.arcToPoint(const Offset(162, 7.8), radius: const Radius.circular(32), clockwise: false); - path.lineTo(200, 7.8); - path.lineTo(200, 80); - path.lineTo(0, 80); - path.lineTo(0, 10); - await testPath(path, 'svg_notch'); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/70980 - test('render notch', () async { - const double w = 0.7; - final Path path = Path(); - path.moveTo(0.5, 14); - path.conicTo(0.5, 10.5, 4, 10.5, w); - path.moveTo(4, 10.5); - path.lineTo(6.5, 10.5); - path.moveTo(36.0, 10.5); - path.lineTo(158, 10.5); - path.conicTo(161.5, 10.5, 161.5, 14, w); - path.moveTo(161.5, 14); - path.lineTo(161.5, 48); - path.conicTo(161.5, 51.5, 158, 51.5, w); - path.lineTo(4, 51.5); - path.conicTo(0.5, 51.5, 0.5, 48, w); - path.lineTo(0.5, 14); - await testPath(path, 'svg_editoutline', mode: PaintMode.kStroke); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/74416 - test('render stroke', () async { - final Path path = Path(); - path.moveTo(20, 20); - path.lineTo(200, 200); - await testPath(path, 'svg_stroke_width', mode: PaintMode.kStrokeWidthOnly); - }); -} - -DomElement pathToSvgElement(Path path, Paint paint, bool enableFill) { - final Rect bounds = path.getBounds(); - final SVGSVGElement root = createSVGSVGElement(); - root.style.transform = 'translate(200px, 0px)'; - root.setAttribute('viewBox', '0 0 ${bounds.right} ${bounds.bottom}'); - root.width!.baseVal!.newValueSpecifiedUnits(svgLengthTypeNumber, bounds.right); - root.height!.baseVal!.newValueSpecifiedUnits(svgLengthTypeNumber, bounds.bottom); - - final SVGPathElement pathElement = createSVGPathElement(); - root.append(pathElement); - if (paint.style == PaintingStyle.stroke || paint.strokeWidth != 0.0) { - pathElement.setAttribute('stroke', paint.color.toCssString()); - pathElement.setAttribute('stroke-width', paint.strokeWidth); - if (!enableFill) { - pathElement.setAttribute('fill', 'none'); - } - } - if (paint.style == PaintingStyle.fill) { - pathElement.setAttribute('fill', paint.color.toCssString()); - } - pathElement.setAttribute( - 'd', - pathToSvg((path as SurfacePath).pathRef), - ); // This is what we're testing! - return root; -} - -class ArcSample { - ArcSample(this.offset, {this.largeArc = false, this.clockwise = false, this.distance = 0}); - - final Offset offset; - final bool largeArc; - final bool clockwise; - final double distance; - - Path createPath() { - final Offset startP = Offset(75 - distance + offset.dx, 75 - distance + offset.dy); - final Offset endP = Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); - final Path path = Path(); - path.moveTo(startP.dx, startP.dy); - path.arcToPoint( - endP, - rotation: 60, - radius: const Radius.elliptical(40, 60), - largeArc: largeArc, - clockwise: clockwise, - ); - return path; - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/path_transform_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/path_transform_golden_test.dart deleted file mode 100644 index 1e4c9d3075..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/path_transform_golden_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -// 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:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; - -import '../common/test_initialization.dart'; -import 'screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - setUpUnitTests(setUpTestViewDimensions: false); - - test('Should draw transformed line.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.moveTo(0, 0); - path.lineTo(300, 200); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, 20); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_line', canvasRect: screenRect); - }); - - test('Should draw transformed line.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.addRect(const Rect.fromLTRB(50, 40, 300, 100)); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, 20); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_rect', canvasRect: screenRect); - }); - - test('Should draw transformed quadratic curve.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.moveTo(100, 100); - path.quadraticBezierTo(100, 300, 400, 300); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, -80); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_quadratic_curve', canvasRect: screenRect); - }); - - test('Should draw transformed conic.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - const double yStart = 20; - - const Offset p0 = Offset(25, yStart + 25); - const Offset pc = Offset(60, yStart + 150); - const Offset p2 = Offset(100, yStart + 50); - - final Path path = Path(); - path.moveTo(p0.dx, p0.dy); - path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); - path.close(); - path.moveTo(p0.dx, p0.dy + 100); - path.conicTo(pc.dx, pc.dy + 100, p2.dx, p2.dy + 100, 10); - path.close(); - - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, -80); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_conic', canvasRect: screenRect); - }); - - test('Should draw transformed arc.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Path path = Path(); - path.moveTo(350, 280); - path.arcToPoint( - const Offset(450, 90), - radius: const Radius.elliptical(200, 50), - rotation: -math.pi / 6.0, - largeArc: true, - ); - path.close(); - - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, 10); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_arc', canvasRect: screenRect); - }); - - test('Should draw transformed rrect.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Path path = Path(); - path.addRRect(RRect.fromLTRBR(50, 50, 300, 200, const Radius.elliptical(4, 8))); - - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = Matrix4.rotationZ(math.pi * 30.0 / 180.0) - ..translate(100, -80); - transformedPath.addPath(path, Offset.zero, matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0), - ); - await canvasScreenshot(rc, 'path_transform_with_rrect', canvasRect: screenRect); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/picture_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/picture_golden_test.dart deleted file mode 100644 index c99c9db327..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/picture_golden_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Picture', () { - test('toImage produces an image', () async { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording( - const ui.Rect.fromLTRB(0, 0, 200, 100), - ); - canvas.drawCircle( - const ui.Offset(100, 50), - 40, - SurfacePaint()..color = const ui.Color.fromARGB(255, 255, 100, 100), - ); - final ui.Picture picture = recorder.endRecording(); - final HtmlImage image = await picture.toImage(200, 100) as HtmlImage; - expect(image, isNotNull); - domDocument.body! - ..style.margin = '0' - ..append(image.imgElement); - try { - await matchGoldenFile( - 'picture_to_image.png', - region: const ui.Rect.fromLTRB(0, 0, 200, 100), - ); - } finally { - image.imgElement.remove(); - } - }); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/recording_canvas_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/recording_canvas_golden_test.dart deleted file mode 100644 index a2fc1b945f..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/recording_canvas_golden_test.dart +++ /dev/null @@ -1,777 +0,0 @@ -// 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:math' as math; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' hide ColorSpace; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; -import 'package:web_engine_tester/golden_tester.dart'; - -import '../common/matchers.dart'; -import '../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); - - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - final SurfacePaint testPaint = SurfacePaint()..color = const Color(0xFFFF0000); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future checkScreenshot( - RecordingCanvas rc, - String fileName, { - Rect region = const Rect.fromLTWH(0, 0, 500, 500), - }) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect, RenderStrategy()); - - // Draws the estimated bounds so we can spot the bug in Gold. - engineCanvas - ..save() - ..drawRect( - rc.pictureBounds!, - SurfacePaintData() - ..color = const Color.fromRGBO(0, 0, 255, 1.0).value - ..style = PaintingStyle.stroke - ..strokeWidth = 1.0, - ) - ..restore(); - - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final DomElement sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - try { - sceneElement.append(engineCanvas.rootElement); - domDocument.body!.append(sceneElement); - await matchGoldenFile('paint_bounds_for_$fileName.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // screenshot. - sceneElement.remove(); - } - } - - test('Empty canvas reports correct paint bounds', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - rc.endRecording(); - expect(rc.pictureBounds, Rect.zero); - await checkScreenshot(rc, 'empty_canvas'); - }); - - test('Computes paint bounds for draw line', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawLine(const Offset(50, 100), const Offset(120, 140), testPaint); - rc.endRecording(); - // The off by one is due to the minimum stroke width of 1. - expect(rc.pictureBounds, const Rect.fromLTRB(49, 99, 121, 141)); - await checkScreenshot(rc, 'draw_line'); - }); - - test('Computes paint bounds for draw line when line exceeds limits', () async { - // Uses max bounds when computing paint bounds - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawLine(const Offset(50, 100), const Offset(screenWidth + 100.0, 140), testPaint); - rc.endRecording(); - // The off by one is due to the minimum stroke width of 1. - expect(rc.pictureBounds, const Rect.fromLTRB(49.0, 99.0, screenWidth, 141.0)); - await checkScreenshot(rc, 'draw_line_exceeding_limits'); - }); - - test('Computes paint bounds for draw rect', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); - await checkScreenshot(rc, 'draw_rect'); - }); - - test('Computes paint bounds for draw rect when exceeds limits', () async { - // Uses max bounds when computing paint bounds - RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawRect(const Rect.fromLTRB(10, 20, 30 + screenWidth, 40 + screenHeight), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, screenWidth, screenHeight)); - - rc = RecordingCanvas(screenRect); - rc.drawRect(const Rect.fromLTRB(-200, -100, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(0, 0, 30, 40)); - await checkScreenshot(rc, 'draw_rect_exceeding_limits'); - }); - - test('Computes paint bounds for translate', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(5, 7); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(15, 27, 35, 47)); - await checkScreenshot(rc, 'translate'); - }); - - test('Computes paint bounds for scale', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.scale(2, 2); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(20, 40, 60, 80)); - await checkScreenshot(rc, 'scale'); - }); - - test('Computes paint bounds for rotate', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.rotate(math.pi / 4.0); - rc.drawLine(const Offset(1, 0), Offset(50 * math.sqrt(2) - 1, 0), testPaint); - rc.endRecording(); - // The extra 0.7 is due to stroke width of 1 rotated by 45 degrees. - expect(rc.pictureBounds, within(distance: 0.1, from: const Rect.fromLTRB(0, 0, 50.7, 50.7))); - await checkScreenshot(rc, 'rotate'); - }); - - test('Computes paint bounds for horizontal skew', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.skew(1.0, 0.0); - rc.drawRect(const Rect.fromLTRB(20, 20, 40, 40), testPaint); - rc.endRecording(); - expect( - rc.pictureBounds, - within(distance: 0.1, from: const Rect.fromLTRB(40.0, 20.0, 80.0, 40.0)), - ); - await checkScreenshot(rc, 'skew_horizontally'); - }); - - test('Computes paint bounds for vertical skew', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.skew(0.0, 1.0); - rc.drawRect(const Rect.fromLTRB(20, 20, 40, 40), testPaint); - rc.endRecording(); - expect( - rc.pictureBounds, - within(distance: 0.1, from: const Rect.fromLTRB(20.0, 40.0, 40.0, 80.0)), - ); - await checkScreenshot(rc, 'skew_vertically'); - }); - - test('Computes paint bounds for a complex transform', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - final Float32List matrix = Float32List(16); - // translate(210, 220) , scale(2, 3), rotate(math.pi / 4.0) - matrix[0] = 1.4; - matrix[1] = 2.12; - matrix[2] = 0.0; - matrix[3] = 0.0; - matrix[4] = -1.4; - matrix[5] = 2.12; - matrix[6] = 0.0; - matrix[7] = 0.0; - matrix[8] = 0.0; - matrix[9] = 0.0; - matrix[10] = 2.0; - matrix[11] = 0.0; - matrix[12] = 210.0; - matrix[13] = 220.0; - matrix[14] = 0.0; - matrix[15] = 1.0; - rc.transform(matrix); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect( - rc.pictureBounds, - within(distance: 0.001, from: const Rect.fromLTRB(168.0, 283.6, 224.0, 368.4)), - ); - await checkScreenshot(rc, 'complex_transform'); - }); - - test('drawPaint should cover full size', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawPaint(testPaint); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, screenRect); - await checkScreenshot(rc, 'draw_paint'); - }); - - test('drawColor should cover full size', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - final SurfacePaint testPaint = SurfacePaint()..color = const Color(0xFF80FF00); - rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.drawColor(const Color(0xFFFF0000), BlendMode.multiply); - rc.drawRect(const Rect.fromLTRB(10, 60, 30, 80), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, screenRect); - await checkScreenshot(rc, 'draw_color'); - }); - - test('Computes paint bounds for draw oval', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawOval(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); - await checkScreenshot(rc, 'draw_oval'); - }); - - test('Computes paint bounds for draw round rect', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawRRect( - RRect.fromRectAndRadius(const Rect.fromLTRB(10, 20, 30, 40), const Radius.circular(5.0)), - testPaint, - ); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); - await checkScreenshot(rc, 'draw_round_rect'); - }); - - test('Computes empty paint bounds when inner rect outside of outer rect for ' - 'drawDRRect', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawDRRect( - RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40)), - RRect.fromRectAndCorners(const Rect.fromLTRB(1, 2, 3, 4)), - testPaint, - ); - rc.endRecording(); - expect(rc.pictureBounds, Rect.zero); - await checkScreenshot(rc, 'draw_drrect_empty'); - }); - - test('Computes paint bounds using outer rect for drawDRRect', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawDRRect( - RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40)), - RRect.fromRectAndCorners(const Rect.fromLTRB(12, 22, 28, 38)), - testPaint, - ); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); - await checkScreenshot(rc, 'draw_drrect'); - }); - - test('Computes paint bounds for draw circle', () async { - // Paint bounds of one circle. - RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawCircle(const Offset(20, 20), 10.0, testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10.0, 10.0, 30.0, 30.0)); - - // Paint bounds of a union of two circles. - rc = RecordingCanvas(screenRect); - rc.drawCircle(const Offset(20, 20), 10.0, testPaint); - rc.drawCircle(const Offset(200, 300), 100.0, testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(10.0, 10.0, 300.0, 400.0)); - await checkScreenshot(rc, 'draw_circle'); - }); - - test('Computes paint bounds for draw image', () { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawImage(TestImage(), const Offset(50, 100), SurfacePaint()); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(50.0, 100.0, 70.0, 110.0)); - }); - - test('Computes paint bounds for draw image rect', () { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawImageRect( - TestImage(), - const Rect.fromLTRB(1, 1, 20, 10), - const Rect.fromLTRB(5, 6, 400, 500), - SurfacePaint(), - ); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(5.0, 6.0, 400.0, 500.0)); - }); - - test( - 'Computes paint bounds for single-line draw paragraph', - () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - final Paragraph paragraph = createTestParagraph(); - const double textLeft = 5.0; - const double textTop = 7.0; - const double widthConstraint = 300.0; - paragraph.layout(const ParagraphConstraints(width: widthConstraint)); - rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); - rc.endRecording(); - expect( - rc.pictureBounds!.width, - lessThan(widthConstraint), - reason: - 'The given width constraint $widthConstraint is more than the ' - 'test string needs, so the width of the visible text is actually ' - 'smaller than the given width.', - ); - expect( - rc.pictureBounds, - Rect.fromLTRB(textLeft, textTop, textLeft + paragraph.maxIntrinsicWidth, 21.0), - ); - await checkScreenshot(rc, 'draw_paragraph'); - }, // TODO(mdebbar): https://github.com/flutter/flutter/issues/65789 - skip: - ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && - ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs, - ); - - test( - 'Computes paint bounds for multi-line draw paragraph', - () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - final Paragraph paragraph = createTestParagraph(); - const double textLeft = 5.0; - const double textTop = 7.0; - // Do not go lower than the shortest word. - const double widthConstraint = 130.0; - paragraph.layout(const ParagraphConstraints(width: widthConstraint)); - rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); - rc.endRecording(); - - const double fontWidth = 14; - const int lettersInLongestWord = 9; - const double longestLineWidth = lettersInLongestWord * fontWidth; - expect( - rc.pictureBounds!.width, - lessThan(widthConstraint), - reason: - 'The test string "A short sentence." is broken up into two lines, ' - '"A short" and "sentence.". The longest line contains ' - '$lettersInLongestWord characters, each ${fontWidth}px wide. ' - 'That line is ${longestLineWidth}px wide, which is less than ' - '$widthConstraint.', - ); - expect( - rc.pictureBounds, - const Rect.fromLTRB(textLeft, textTop, textLeft + longestLineWidth, 35.0), - ); - await checkScreenshot(rc, 'draw_paragraph_multi_line'); - }, // TODO(mdebbar): https://github.com/flutter/flutter/issues/65789 - skip: - ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && - ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs, - ); - - test('Should exclude painting outside simple clipRect', () async { - // One clipped line. - RecordingCanvas rc = RecordingCanvas(screenRect); - rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100), ClipOp.intersect); - rc.drawLine(const Offset(10, 11), const Offset(20, 21), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, Rect.zero); - - // Two clipped lines. - rc = RecordingCanvas(screenRect); - rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100), ClipOp.intersect); - rc.drawLine(const Offset(10, 11), const Offset(20, 21), testPaint); - rc.drawLine(const Offset(52, 53), const Offset(55, 56), testPaint); - rc.endRecording(); - - // Extra pixel due to default line length - expect(rc.pictureBounds, const Rect.fromLTRB(51, 52, 56, 57)); - await checkScreenshot(rc, 'clip_rect_simple'); - }); - - test('Should include intersection of clipRect and painting', () async { - RecordingCanvas rc = RecordingCanvas(screenRect); - rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100), ClipOp.intersect); - rc.drawRect(const Rect.fromLTRB(20, 60, 120, 70), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(50, 60, 100, 70)); - await checkScreenshot(rc, 'clip_rect_intersects_paint_left_to_right'); - - rc = RecordingCanvas(screenRect); - rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100), ClipOp.intersect); - rc.drawRect(const Rect.fromLTRB(60, 20, 70, 200), testPaint); - rc.endRecording(); - expect(rc.pictureBounds, const Rect.fromLTRB(60, 50, 70, 100)); - await checkScreenshot(rc, 'clip_rect_intersects_paint_top_to_bottom'); - }); - - test('Should intersect rects for multiple clipRect calls', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100), ClipOp.intersect); - rc.scale(2.0, 2.0); - rc.clipRect(const Rect.fromLTRB(30, 30, 45, 45), ClipOp.intersect); - rc.drawRect(const Rect.fromLTRB(10, 30, 60, 35), testPaint); - rc.endRecording(); - - expect(rc.pictureBounds, const Rect.fromLTRB(60, 60, 90, 70)); - await checkScreenshot(rc, 'clip_rects_intersect'); - }); - - // drawShadow - test('Computes paint bounds for drawShadow', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - final Path path = Path(); - path.addRect(const Rect.fromLTRB(20, 30, 100, 110)); - rc.drawShadow(path, const Color(0xFFFF0000), 2.0, true); - rc.endRecording(); - - expect( - rc.pictureBounds, - within(distance: 0.05, from: const Rect.fromLTRB(0.0, 8.5, 123.5, 134.1)), - ); - await checkScreenshot(rc, 'path_with_shadow'); - }); - - test('Clip with negative scale reports correct paint bounds', () async { - // The following draws a filled rectangle that occupies the bottom half of - // the canvas. Notice that both the clip and the rectangle are drawn - // forward. What makes them appear at the bottom is the translation and a - // vertical flip via a negative scale. This replicates the Material - // overscroll glow effect at the bottom of a list, where it is drawn upside - // down. - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); - rc - ..translate(0, 100) - ..scale(1, -1) - ..clipRect(const Rect.fromLTRB(0, 0, 100, 50), ClipOp.intersect) - ..drawRect(const Rect.fromLTRB(0, 0, 100, 100), SurfacePaint()); - rc.endRecording(); - - expect(rc.pictureBounds, const Rect.fromLTRB(0.0, 50.0, 100.0, 100.0)); - await checkScreenshot(rc, 'scale_negative'); - }); - - test('Clip with a rotation reports correct paint bounds', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); - rc - ..translate(50, 50) - ..rotate(math.pi / 4.0) - ..clipRect(const Rect.fromLTWH(-20, -20, 40, 40), ClipOp.intersect) - ..drawRect(const Rect.fromLTWH(-80, -80, 160, 160), SurfacePaint()); - rc.endRecording(); - - expect( - rc.pictureBounds, - within( - distance: 0.001, - from: Rect.fromCircle(center: const Offset(50, 50), radius: 20 * math.sqrt(2)), - ), - ); - await checkScreenshot(rc, 'clip_rect_rotated'); - }); - - test('Rotated line reports correct paint bounds', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); - rc - ..translate(50, 50) - ..rotate(math.pi / 4.0) - ..drawLine(Offset.zero, const Offset(20, 20), SurfacePaint()); - rc.endRecording(); - - expect( - rc.pictureBounds, - within(distance: 0.1, from: const Rect.fromLTRB(34.4, 48.6, 65.6, 79.7)), - ); - await checkScreenshot(rc, 'line_rotated'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/46339. - test('Should draw a Rect for straight line when strokeWidth is zero.', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); - - final Path path = Path(); - final SurfacePaint paint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 0.0 - ..color = const Color(0xFFFF0000); - path.moveTo(10, 10); - path.lineTo(90, 10); - rc.drawPath(path, paint); - rc.endRecording(); - // Should draw a Rect - final List commands = rc.debugPaintCommands; - expect(commands.length, 1); - expect(commands.first, isA()); - // Should inflate picture bounds - expect(rc.pictureBounds, within(distance: 0.1, from: const Rect.fromLTRB(10, 10, 90, 11))); - await checkScreenshot(rc, 'path_straight_line_with_zero_stroke_width'); - }); - - test('Should support reusing path and reset when drawing into canvas.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); - - final Path path = Path(); - path.moveTo(3, 0); - path.lineTo(100, 97); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFFFF0000), - ); - path.reset(); - path.moveTo(0, 3); - path.lineTo(97, 100); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF00FF00), - ); - rc.endRecording(); - await checkScreenshot(rc, 'reuse_path'); - }); - - test('Should draw RRect after line when beginning new path.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); - rc.save(); - rc.translate(50.0, 100.0); - final Path path = Path(); - // Draw a vertical small line (caret). - path.moveTo(8, 4); - path.lineTo(8, 24); - // Draw round rect below caret. - path.addRRect(RRect.fromLTRBR(0.5, 100.5, 80.7, 150.7, const Radius.circular(10))); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - rc.restore(); - rc.endRecording(); - await checkScreenshot(rc, 'path_with_line_and_roundrect'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/64371. - test('Should draw line following a polygon without closing path.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); - rc.save(); - rc.translate(50.0, 100.0); - final Path path = Path(); - // Draw a vertical small line (caret). - path.addPolygon(const [Offset(0, 10), Offset(20, 5), Offset(50, 10)], false); - path.lineTo(60, 80); - path.lineTo(0, 80); - path.close(); - rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000), - ); - rc.restore(); - rc.endRecording(); - await checkScreenshot(rc, 'path_with_addpolygon'); - }); - - test('should include paint spread in bounds estimates', () async { - final SurfaceSceneBuilder sb = SurfaceSceneBuilder(); - - final List painters = [ - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawLine(Offset.zero, const Offset(20.0, 20.0), paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawRect(const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0), paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawRRect(RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, const Radius.circular(7.0)), paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawDRRect( - RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, const Radius.circular(5.0)), - RRect.fromLTRBR(4.0, 4.0, 16.0, 16.0, const Radius.circular(5.0)), - paint, - ); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawOval(const Rect.fromLTRB(0.0, 5.0, 20.0, 15.0), paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawCircle(const Offset(10.0, 10.0), 10.0, paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - final SurfacePath path = - SurfacePath() - ..moveTo(10, 0) - ..lineTo(20, 10) - ..lineTo(10, 20) - ..lineTo(0, 10) - ..close(); - canvas.drawPath(path, paint); - }, - - // Images are not affected by mask filter or stroke width. They use image - // filter instead. - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawImage(_createRealTestImage(), Offset.zero, paint); - }, - (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawImageRect( - _createRealTestImage(), - const Rect.fromLTRB(0, 0, 20, 20), - const Rect.fromLTRB(5, 5, 15, 15), - paint, - ); - }, - ]; - - Picture drawBounds(Rect bounds) { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - canvas.drawRect( - bounds, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1.0 - ..color = const Color.fromARGB(255, 0, 255, 0), - ); - return recorder.endRecording(); - } - - for (int i = 0; i < painters.length; i++) { - sb.pushOffset(0.0, 20.0 + 60.0 * i); - final PaintSpreadPainter painter = painters[i]; - - // Paint with zero paint spread. - { - sb.pushOffset(20.0, 0.0); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - final SurfacePaint zeroSpreadPaint = SurfacePaint(); - painter(canvas, zeroSpreadPaint); - sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); - sb.pop(); - } - - // Paint with a thick stroke paint. - { - sb.pushOffset(80.0, 0.0); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - final SurfacePaint thickStrokePaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 5.0; - painter(canvas, thickStrokePaint); - sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); - sb.pop(); - } - - // Paint with a mask filter blur. - { - sb.pushOffset(140.0, 0.0); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - final SurfacePaint maskFilterBlurPaint = - SurfacePaint()..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); - painter(canvas, maskFilterBlurPaint); - sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); - sb.pop(); - } - - // Paint with a thick stroke paint and a mask filter blur. - { - sb.pushOffset(200.0, 0.0); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - final SurfacePaint thickStrokeAndBlurPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 5.0 - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); - painter(canvas, thickStrokeAndBlurPaint); - sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); - sb.pop(); - } - - sb.pop(); - } - - final DomElement sceneElement = sb.build().webOnlyRootElement!; - domDocument.body!.append(sceneElement); - try { - await matchGoldenFile('paint_spread_bounds.png', region: const Rect.fromLTRB(0, 0, 250, 600)); - } finally { - sceneElement.remove(); - } - }); -} - -typedef PaintSpreadPainter = void Function(RecordingCanvas canvas, SurfacePaint paint); - -const String _base64Encoded20x20TestImage = - 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUC' - 'AIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' - 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' - 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; - -HtmlImage _createRealTestImage() { - return HtmlImage( - createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', - 20, - 20, - ); -} - -class TestImage implements Image { - @override - int get width => 20; - - @override - int get height => 10; - - @override - Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) async { - throw UnsupportedError('Cannot encode test image'); - } - - @override - String toString() => '[$width\u00D7$height]'; - - @override - void dispose() {} - - @override - bool get debugDisposed => false; - - @override - Image clone() => this; - - @override - bool isCloneOf(Image other) => other == this; - - @override - List /*?*/ debugGetOpenHandleStackTraces() => []; - - @override - ColorSpace get colorSpace => ColorSpace.sRGB; -} - -Paragraph createTestParagraph() { - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - ), - ); - builder.addText('A short sentence.'); - return builder.build(); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/recording_canvas_test.dart b/engine/src/flutter/lib/web_ui/test/html/recording_canvas_test.dart deleted file mode 100644 index 190da34418..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/recording_canvas_test.dart +++ /dev/null @@ -1,324 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../common/mock_engine_canvas.dart'; -import '../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); - - late RecordingCanvas underTest; - late MockEngineCanvas mockCanvas; - const Rect screenRect = Rect.largest; - - setUp(() { - underTest = RecordingCanvas(screenRect); - mockCanvas = MockEngineCanvas(); - }); - - group('paragraph bounds', () { - Paragraph paragraphForBoundsTest(TextAlign alignment) { - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle(fontFamily: 'Ahem', fontSize: 20, textAlign: alignment), - ); - builder.addText('A AAAAA AAA'); - return builder.build(); - } - - test('not laid out', () { - final Paragraph paragraph = paragraphForBoundsTest(TextAlign.start); - underTest.drawParagraph(paragraph, Offset.zero); - underTest.endRecording(); - expect(underTest.pictureBounds, Rect.zero); - }); - - test('finite width', () { - final Paragraph paragraph = paragraphForBoundsTest(TextAlign.start); - paragraph.layout(const ParagraphConstraints(width: 110)); - underTest.drawParagraph(paragraph, Offset.zero); - underTest.endRecording(); - expect(paragraph.width, 110); - expect(paragraph.height, 60); - expect(underTest.pictureBounds, const Rect.fromLTRB(0, 0, 100, 60)); - }); - - test('finite width center-aligned', () { - final Paragraph paragraph = paragraphForBoundsTest(TextAlign.center); - paragraph.layout(const ParagraphConstraints(width: 110)); - underTest.drawParagraph(paragraph, Offset.zero); - underTest.endRecording(); - expect(paragraph.width, 110); - expect(paragraph.height, 60); - expect(underTest.pictureBounds, const Rect.fromLTRB(5, 0, 105, 60)); - }); - - test('infinite width', () { - final Paragraph paragraph = paragraphForBoundsTest(TextAlign.start); - paragraph.layout(const ParagraphConstraints(width: double.infinity)); - underTest.drawParagraph(paragraph, Offset.zero); - underTest.endRecording(); - expect(paragraph.width, double.infinity); - expect(paragraph.height, 20); - expect(underTest.pictureBounds, const Rect.fromLTRB(0, 0, 220, 20)); - }); - }); - - group('drawDRRect', () { - final RRect rrect = RRect.fromLTRBR(10, 10, 50, 50, const Radius.circular(3)); - final SurfacePaint somePaint = SurfacePaint()..color = const Color(0xFFFF0000); - - test('Happy case', () { - underTest.drawDRRect(rrect, rrect.deflate(1), somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - - _expectDrawDRRectCall(mockCanvas, { - 'path': - 'Path(' - 'MoveTo(${10.0}, ${47.0}) ' - 'LineTo(${10.0}, ${13.0}) ' - 'Conic(${10.0}, ${10.0}, ${10.0}, ${13.0}, w = ${0.7071067690849304}) ' - 'LineTo(${47.0}, ${10.0}) ' - 'Conic(${50.0}, ${10.0}, ${10.0}, ${50.0}, w = ${0.7071067690849304}) ' - 'LineTo(${50.0}, ${47.0}) ' - 'Conic(${50.0}, ${50.0}, ${50.0}, ${47.0}, w = ${0.7071067690849304}) ' - 'LineTo(${13.0}, ${50.0}) ' - 'Conic(${10.0}, ${50.0}, ${50.0}, ${10.0}, w = ${0.7071067690849304}) ' - 'Close() ' - 'MoveTo(${11.0}, ${47.0}) ' - 'LineTo(${11.0}, ${13.0}) ' - 'Conic(${11.0}, ${11.0}, ${11.0}, ${13.0}, w = ${0.7071067690849304}) ' - 'LineTo(${47.0}, ${11.0}) ' - 'Conic(${49.0}, ${11.0}, ${11.0}, ${49.0}, w = ${0.7071067690849304}) ' - 'LineTo(${49.0}, ${47.0}) ' - 'Conic(${49.0}, ${49.0}, ${49.0}, ${47.0}, w = ${0.7071067690849304}) ' - 'LineTo(${13.0}, ${49.0}) ' - 'Conic(${11.0}, ${49.0}, ${49.0}, ${11.0}, w = ${0.7071067690849304}) ' - 'Close()' - ')', - 'paint': somePaint.paintData, - }); - }); - - test('Inner RRect > Outer RRect', () { - underTest.drawDRRect(rrect, rrect.inflate(1), somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - // Expect nothing to be called - expect(mockCanvas.methodCallLog.length, equals(1)); - expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); - }); - - test('Inner RRect not completely inside Outer RRect', () { - underTest.drawDRRect(rrect, rrect.deflate(1).shift(const Offset(0.0, 10)), somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - // Expect nothing to be called - expect(mockCanvas.methodCallLog.length, equals(1)); - expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); - }); - - test('Inner RRect same as Outer RRect', () { - underTest.drawDRRect(rrect, rrect, somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - // Expect nothing to be called - expect(mockCanvas.methodCallLog.length, equals(1)); - expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); - }); - - test('deflated corners in inner RRect get passed through to draw', () { - // This comes from github issue #40728 - final RRect outer = RRect.fromRectAndCorners( - const Rect.fromLTWH(0, 0, 88, 48), - topLeft: const Radius.circular(6), - bottomLeft: const Radius.circular(6), - ); - final RRect inner = outer.deflate(1); - - expect(inner.brRadius, equals(Radius.zero)); - expect(inner.trRadius, equals(Radius.zero)); - - underTest.drawDRRect(outer, inner, somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - - // Expect to draw, even when inner has negative radii (which get ignored by canvas) - _expectDrawDRRectCall(mockCanvas, { - 'path': - 'Path(' - 'MoveTo(${0.0}, ${42.0}) ' - 'LineTo(${0.0}, ${6.0}) ' - 'Conic(${0.0}, ${0.0}, ${0.0}, ${6.0}, w = ${0.7071067690849304}) ' - 'LineTo(${88.0}, ${0.0}) ' - 'Conic(${88.0}, ${0.0}, ${0.0}, ${88.0}, w = ${0.7071067690849304}) ' - 'LineTo(${88.0}, ${48.0}) ' - 'Conic(${88.0}, ${48.0}, ${48.0}, ${88.0}, w = ${0.7071067690849304}) ' - 'LineTo(${6.0}, ${48.0}) ' - 'Conic(${0.0}, ${48.0}, ${48.0}, ${0.0}, w = ${0.7071067690849304}) ' - 'Close() ' - 'MoveTo(${1.0}, ${42.0}) ' - 'LineTo(${1.0}, ${6.0}) ' - 'Conic(${1.0}, ${1.0}, ${1.0}, ${6.0}, w = ${0.7071067690849304}) ' - 'LineTo(${87.0}, ${1.0}) ' - 'Conic(${87.0}, ${1.0}, ${1.0}, ${87.0}, w = ${0.7071067690849304}) ' - 'LineTo(${87.0}, ${47.0}) ' - 'Conic(${87.0}, ${47.0}, ${47.0}, ${87.0}, w = ${0.7071067690849304}) ' - 'LineTo(${6.0}, ${47.0}) ' - 'Conic(${1.0}, ${47.0}, ${47.0}, ${1.0}, w = ${0.7071067690849304}) ' - 'Close()' - ')', - 'paint': somePaint.paintData, - }); - }); - - test('preserve old golden test behavior', () { - final RRect outer = RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40)); - final RRect inner = RRect.fromRectAndCorners(const Rect.fromLTRB(12, 22, 28, 38)); - - underTest.drawDRRect(outer, inner, somePaint); - underTest.endRecording(); - underTest.apply(mockCanvas, screenRect); - - _expectDrawDRRectCall(mockCanvas, { - 'path': - 'Path(' - 'MoveTo(${10.0}, ${20.0}) ' - 'LineTo(${30.0}, ${20.0}) ' - 'LineTo(${30.0}, ${40.0}) ' - 'LineTo(${10.0}, ${40.0}) ' - 'Close() ' - 'MoveTo(${12.0}, ${22.0}) ' - 'LineTo(${28.0}, ${22.0}) ' - 'LineTo(${28.0}, ${38.0}) ' - 'LineTo(${12.0}, ${38.0}) ' - 'Close()' - ')', - 'paint': somePaint.paintData, - }); - }); - }); - - test('Filters out paint commands outside the clip rect', () { - // Outside to the left - underTest.drawRect(const Rect.fromLTWH(0.0, 20.0, 10.0, 10.0), SurfacePaint()); - - // Outside above - underTest.drawRect(const Rect.fromLTWH(20.0, 0.0, 10.0, 10.0), SurfacePaint()); - - // Visible - underTest.drawRect(const Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), SurfacePaint()); - - // Inside the layer clip rect but zero-size - underTest.drawRect(const Rect.fromLTRB(20.0, 20.0, 30.0, 20.0), SurfacePaint()); - - // Inside the layer clip but clipped out by a canvas clip - underTest.save(); - underTest.clipRect(const Rect.fromLTWH(0, 0, 10, 10), ClipOp.intersect); - underTest.drawRect(const Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), SurfacePaint()); - underTest.restore(); - - // Outside to the right - underTest.drawRect(const Rect.fromLTWH(40.0, 20.0, 10.0, 10.0), SurfacePaint()); - - // Outside below - underTest.drawRect(const Rect.fromLTWH(20.0, 40.0, 10.0, 10.0), SurfacePaint()); - - underTest.endRecording(); - - expect(underTest.debugPaintCommands, hasLength(10)); - final PaintDrawRect outsideLeft = underTest.debugPaintCommands[0] as PaintDrawRect; - expect(outsideLeft.isClippedOut, isFalse); - expect(outsideLeft.leftBound, 0); - expect(outsideLeft.topBound, 20); - expect(outsideLeft.rightBound, 10); - expect(outsideLeft.bottomBound, 30); - - final PaintDrawRect outsideAbove = underTest.debugPaintCommands[1] as PaintDrawRect; - expect(outsideAbove.isClippedOut, isFalse); - - final PaintDrawRect visible = underTest.debugPaintCommands[2] as PaintDrawRect; - expect(visible.isClippedOut, isFalse); - - final PaintDrawRect zeroSize = underTest.debugPaintCommands[3] as PaintDrawRect; - expect(zeroSize.isClippedOut, isTrue); - - expect(underTest.debugPaintCommands[4], isA()); - - final PaintClipRect clip = underTest.debugPaintCommands[5] as PaintClipRect; - expect(clip.isClippedOut, isFalse); - - final PaintDrawRect clippedOut = underTest.debugPaintCommands[6] as PaintDrawRect; - expect(clippedOut.isClippedOut, isTrue); - - expect(underTest.debugPaintCommands[7], isA()); - - final PaintDrawRect outsideRight = underTest.debugPaintCommands[8] as PaintDrawRect; - expect(outsideRight.isClippedOut, isFalse); - - final PaintDrawRect outsideBelow = underTest.debugPaintCommands[9] as PaintDrawRect; - expect(outsideBelow.isClippedOut, isFalse); - - // Give it the entire screen so everything paints. - underTest.apply(mockCanvas, screenRect); - expect(mockCanvas.methodCallLog, hasLength(11)); - expect(mockCanvas.methodCallLog[0].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[1].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[2].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[3].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[4].methodName, 'save'); - expect(mockCanvas.methodCallLog[5].methodName, 'clipRect'); - expect(mockCanvas.methodCallLog[6].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[7].methodName, 'restore'); - expect(mockCanvas.methodCallLog[8].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[9].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[10].methodName, 'endOfPaint'); - - // Clip out a middle region that only contains 'drawRect' - mockCanvas.methodCallLog.clear(); - underTest.apply(mockCanvas, const Rect.fromLTRB(15, 15, 35, 35)); - expect(mockCanvas.methodCallLog, hasLength(4)); - expect(mockCanvas.methodCallLog[0].methodName, 'drawRect'); - expect(mockCanvas.methodCallLog[1].methodName, 'save'); - expect(mockCanvas.methodCallLog[2].methodName, 'restore'); - expect(mockCanvas.methodCallLog[3].methodName, 'endOfPaint'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/61697. - test('Allows restore calls after recording has ended', () { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); - rc.endRecording(); - // Should not throw exception on restore. - expect(() => rc.restore(), returnsNormally); - }); - - // Regression test for https://github.com/flutter/flutter/issues/61697. - test('Allows restore calls even if recording is not ended', () { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); - // Should not throw exception on restore. - expect(() => rc.restore(), returnsNormally); - }); -} - -// Expect a drawDRRect call to be registered in the mock call log, with the expectedArguments -void _expectDrawDRRectCall(MockEngineCanvas mock, Map expectedArguments) { - expect(mock.methodCallLog.length, equals(2)); - final MockCanvasCall mockCall = mock.methodCallLog[0]; - expect(mockCall.methodName, equals('drawPath')); - final Map argMap = mockCall.arguments as Map; - final Map argContents = {}; - argMap.forEach((String key, dynamic value) { - argContents[key] = value is SurfacePath ? value.toString() : value; - }); - expect(argContents, equals(expectedArguments)); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/resource_manager_test.dart b/engine/src/flutter/lib/web_ui/test/html/resource_manager_test.dart deleted file mode 100644 index 4a864975df..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/resource_manager_test.dart +++ /dev/null @@ -1,86 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - final DomElement hostElement = createDomHTMLDivElement(); - late DomManager domManager; - late ResourceManager resourceManager; - - setUp(() { - domManager = DomManager(devicePixelRatio: 3); - hostElement.appendChild(domManager.rootElement); - resourceManager = ResourceManager(domManager); - }); - - tearDown(() { - hostElement.clearChildren(); - }); - - test('prepends resources host as sibling to root element (webkit)', () { - ui_web.browser.debugBrowserEngineOverride = ui_web.BrowserEngine.webkit; - - // Resource host hasn't been inserted yet. - expect( - hostElement.children.map((DomElement e) => e.tagName.toLowerCase()), - isNot(contains(ResourceManager.resourcesHostTagName.toLowerCase())), - ); - - final List resources = [ - createDomHTMLDivElement()..setAttribute('test-resource', 'r1'), - createDomHTMLDivElement()..setAttribute('test-resource', 'r2'), - createDomHTMLDivElement()..setAttribute('test-resource', 'r3'), - ]; - resources.forEach(resourceManager.addResource); - - final DomElement resourcesHost = hostElement.firstElementChild!; - expect(resourcesHost.tagName.toLowerCase(), ResourceManager.resourcesHostTagName.toLowerCase()); - // Make sure the resources were correctly inserted into the host. - expect(resourcesHost.children, resources); - - ui_web.browser.debugBrowserEngineOverride = null; - }); - - test('prepends resources host inside the shadow root (non-webkit)', () { - ui_web.browser.debugBrowserEngineOverride = ui_web.BrowserEngine.blink; - - // Resource host hasn't been inserted yet. - expect( - hostElement.children.map((DomElement e) => e.tagName.toLowerCase()), - isNot(contains(ResourceManager.resourcesHostTagName.toLowerCase())), - ); - - final List resources = [ - createDomHTMLDivElement()..setAttribute('test-resource', 'r1'), - createDomHTMLDivElement()..setAttribute('test-resource', 'r2'), - createDomHTMLDivElement()..setAttribute('test-resource', 'r3'), - ]; - resources.forEach(resourceManager.addResource); - - final DomElement resourcesHost = domManager.renderingHost.firstElementChild!; - expect(resourcesHost.tagName.toLowerCase(), ResourceManager.resourcesHostTagName.toLowerCase()); - // Make sure the resources were correctly inserted into the host. - expect(resourcesHost.children, resources); - - ui_web.browser.debugBrowserEngineOverride = null; - }); - - test('can remove resource', () { - final DomHTMLDivElement resource = createDomHTMLDivElement(); - resourceManager.addResource(resource); - final DomElement? resourceRoot = resource.parent; - expect(resourceRoot, isNotNull); - expect(resourceRoot!.childNodes.length, 1); - resourceManager.removeResource(resource); - expect(resourceRoot.childNodes.length, 0); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/screenshot.dart b/engine/src/flutter/lib/web_ui/test/html/screenshot.dart deleted file mode 100644 index f56d594b3a..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/screenshot.dart +++ /dev/null @@ -1,70 +0,0 @@ -// 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:async'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; -import 'package:web_engine_tester/golden_tester.dart'; - -/// Commit a recording canvas to a bitmap, and compare with the expected. -/// -/// [region] specifies the area of the canvas that will be included in the -/// golden. -/// -/// If [canvasRect] is omitted, it defaults to the value of [region]. -Future canvasScreenshot( - RecordingCanvas rc, - String fileName, { - ui.Rect region = const ui.Rect.fromLTWH(0, 0, 500, 500), - ui.Rect? canvasRect, - bool setupPerspective = false, -}) async { - canvasRect ??= region; - final EngineCanvas engineCanvas = BitmapCanvas(canvasRect, RenderStrategy()); - - rc.endRecording(); - rc.apply(engineCanvas, region); - - // Wrap in so that our CSS selectors kick in. - final DomElement sceneElement = createDomElement('flt-scene'); - if (isIosSafari) { - // Shrink to fit on the iPhone screen. - sceneElement.style.position = 'absolute'; - sceneElement.style.transformOrigin = '0 0 0'; - sceneElement.style.transform = 'scale(0.3)'; - } - try { - if (setupPerspective) { - // iFrame disables perspective, set it explicitly for test. - engineCanvas.rootElement.style.perspective = '400px'; - for (final DomElement element in engineCanvas.rootElement.querySelectorAll('div')) { - element.style.perspective = '400px'; - } - } - sceneElement.append(engineCanvas.rootElement); - domDocument.body!.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // screenshot. - sceneElement.remove(); - } -} - -Future sceneScreenshot( - SurfaceSceneBuilder sceneBuilder, - String fileName, { - ui.Rect region = const ui.Rect.fromLTWH(0, 0, 600, 800), -}) async { - DomElement? sceneElement; - try { - sceneElement = sceneBuilder.build().webOnlyRootElement; - domDocument.body!.append(sceneElement!); - await matchGoldenFile('$fileName.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // screenshot. - sceneElement?.remove(); - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/shaders/gradient_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shaders/gradient_golden_test.dart deleted file mode 100644 index c75b513542..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shaders/gradient_golden_test.dart +++ /dev/null @@ -1,921 +0,0 @@ -// 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:math' as math; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. -// https://github.com/flutter/flutter/issues/86623 - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - const Rect region = Rect.fromLTWH(0, 0, 500, 240); - - setUpUnitTests(withImplicitView: true); - - test('Paints sweep gradient rectangles', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - GradientSweep sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - null, - ); - - final GradientSweep sweepGradientRotated = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - Matrix4.rotationZ(math.pi / 6.0).storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with shifted center and rotation. - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - canvas.drawRect( - rectBounds, - SurfacePaint() - ..shader = engineGradientToShader( - sweepGradientRotated, - Rect.fromLTWH( - rectBounds.center.dx, - rectBounds.top, - rectBounds.width / 2, - rectBounds.height, - ), - ), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with start/endangle. - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode repeat - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.repeated, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode mirror - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.mirror, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot(canvas, 'sweep_gradient_rect', canvasRect: screenRect, region: region); - }, skip: isFirefox); - - test('Paints sweep gradient ovals', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - final List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - GradientSweep sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - null, - ); - - final GradientSweep sweepGradientRotated = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - Matrix4.rotationZ(math.pi / 6.0).storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with shifted center and rotation. - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - canvas.drawOval( - rectBounds, - SurfacePaint() - ..shader = engineGradientToShader( - sweepGradientRotated, - Rect.fromLTWH( - rectBounds.center.dx, - rectBounds.top, - rectBounds.width / 2, - rectBounds.height, - ), - ), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with start/endangle. - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode repeat - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.repeated, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode mirror - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.mirror, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot(canvas, 'sweep_gradient_oval', canvasRect: screenRect, region: region); - }, skip: isFirefox); - - test('Paints sweep gradient paths', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - GradientSweep sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - null, - ); - - final GradientSweep sweepGradientRotated = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0, - 360.0 / 180.0 * math.pi, - Matrix4.rotationZ(math.pi / 6.0).storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - Path path = samplePathFromRect(rectBounds); - canvas.drawPath( - path, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with shifted center and rotation. - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - path = samplePathFromRect(rectBounds); - canvas.drawPath( - path, - SurfacePaint() - ..shader = engineGradientToShader( - sweepGradientRotated, - Rect.fromLTWH( - rectBounds.center.dx, - rectBounds.top, - rectBounds.width / 2, - rectBounds.height, - ), - ), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Gradient with start/endangle. - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - path = samplePathFromRect(rectBounds); - canvas.drawPath( - path, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode repeat - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.repeated, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - - path = samplePathFromRect(rectBounds); - canvas.drawPath( - path, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode mirror - rectBounds = rectBounds.translate(kBoxWidth + 10, 0); - sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.mirror, - math.pi / 6, - 3 * math.pi / 4, - null, - ); - path = samplePathFromRect(rectBounds); - canvas.drawPath( - path, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot(canvas, 'sweep_gradient_path', canvasRect: screenRect, region: region); - }, skip: isFirefox); - - /// Regression test for https://github.com/flutter/flutter/issues/74137. - test('Paints rotated and shifted linear gradient', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - GradientLinear linearGradient = GradientLinear( - const Offset(50, 50), - const Offset(200, 130), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode repeat - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - linearGradient = GradientLinear( - const Offset(50, 50), - const Offset(200, 130), - colors, - stops, - TileMode.repeated, - Matrix4.identity().storage, - ); - - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'linear_gradient_rect_shifted', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - /// Regression test for https://github.com/flutter/flutter/issues/82748. - test('Paints gradient with gradient stop outside range', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [Color(0xFF000000), Color(0xFFFF3C38)]; - const List stops = [0.0, 10.0]; - - final GradientLinear linearGradient = GradientLinear( - const Offset(50, 50), - const Offset(200, 130), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - const Rect rectBounds = Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - canvas.restore(); - - final EngineCanvas engineCanvas = BitmapCanvas(screenRect, RenderStrategy()); - canvas.endRecording(); - canvas.apply(engineCanvas, screenRect); - }, skip: isFirefox); - - test("Creating lots of gradients doesn't create too many webgl contexts", () async { - final DomCanvasElement sideCanvas = createDomCanvasElement(width: 5, height: 5); - final DomCanvasRenderingContextWebGl? context = - sideCanvas.getContext('webgl') as DomCanvasRenderingContextWebGl?; - expect(context, isNotNull); - - final EngineCanvas engineCanvas = BitmapCanvas( - const Rect.fromLTRB(0, 0, 100, 100), - RenderStrategy(), - ); - for (double x = 0; x < 100; x += 10) { - for (double y = 0; y < 100; y += 10) { - const List colors = [Color(0xFFFF0000), Color(0xFF0000FF)]; - - final GradientLinear linearGradient = GradientLinear( - Offset.zero, - const Offset(10, 10), - colors, - null, - TileMode.clamp, - Matrix4.identity().storage, - ); - engineCanvas.drawRect( - Rect.fromLTWH(x, y, 10, 10), - SurfacePaintData()..shader = linearGradient, - ); - } - } - - expect(context!.isContextLost(), isFalse); - }, skip: isFirefox); - - test('Paints clamped, rotated and shifted linear gradient', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - GradientLinear linearGradient = GradientLinear( - const Offset(50, 50), - const Offset(200, 130), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - // Gradient with default center. - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - // Tile mode repeat - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - linearGradient = GradientLinear( - const Offset(50, 50), - const Offset(200, 130), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'linear_gradient_rect_clamp_rotated', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - test('Paints linear gradient properly when within svg context', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 240)); - canvas.save(); - - canvas.renderStrategy.isInsideSvgFilterTree = true; - - final SurfacePaint borderPaint = - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color(0xFF000000); - - const List colors = [Color(0xFFFF0000), Color(0xFF0000FF)]; - - final GradientLinear linearGradient = GradientLinear( - const Offset(125, 75), - const Offset(175, 125), - colors, - null, - TileMode.clamp, - Matrix4.identity().storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 100; - // Gradient with default center. - const Rect rectBounds = Rect.fromLTWH(100, 50, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - canvas.drawRect(rectBounds, borderPaint); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'linear_gradient_in_svg_context', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - test('Paints transformed linear gradient', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - final Matrix4 transform = - Matrix4.identity() - ..translate(50, 50) - ..scale(0.3, 0.7) - ..rotateZ(0.5); - - final GradientLinear linearGradient = GradientLinear( - const Offset(5, 5), - const Offset(200, 130), - colors, - stops, - TileMode.clamp, - transform.storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), - ); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'linear_gradient_clamp_transformed', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - test('Paints transformed sweep gradient', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - final Matrix4 transform = - Matrix4.identity() - ..translate(100, 150) - ..scale(0.3, 0.7) - ..rotateZ(0.5); - - final GradientSweep sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0.0, - 2 * math.pi, - transform.storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), - ); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'sweep_gradient_clamp_transformed', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - test('Paints transformed radial gradient', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - final Matrix4 transform = - Matrix4.identity() - ..translate(50, 50) - ..scale(0.3, 0.7) - ..rotateZ(0.5); - - final GradientRadial radialGradient = GradientRadial( - const Offset(0.5, 0.5), - 400, - colors, - stops, - TileMode.clamp, - transform.storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight = 80; - - Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); - canvas.drawRect( - rectBounds, - SurfacePaint()..shader = engineRadialGradientToShader(radialGradient, rectBounds), - ); - - rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); - canvas.drawOval( - rectBounds, - SurfacePaint()..shader = engineRadialGradientToShader(radialGradient, rectBounds), - ); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'radial_gradient_clamp_transformed', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); - - test('Paints two gradient with same width and different height', () async { - final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - canvas.save(); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - final Matrix4 transform = - Matrix4.identity() - ..translate(100, 150) - ..scale(0.3, 0.7) - ..rotateZ(0.5); - - final GradientSweep sweepGradient = GradientSweep( - const Offset(0.5, 0.5), - colors, - stops, - TileMode.clamp, - 0.0, - 2 * math.pi, - transform.storage, - ); - - const double kBoxWidth = 150; - const double kBoxHeight1 = 40; - const double kBoxHeight2 = 80; - - const Rect rectBounds1 = Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight1); - const Rect rectBounds2 = Rect.fromLTWH(10, 80, kBoxWidth, kBoxHeight2); - canvas.drawRect( - rectBounds1, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds1), - ); - canvas.drawRect( - rectBounds2, - SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds2), - ); - - canvas.restore(); - await canvasScreenshot( - canvas, - 'radial_gradient_double_item', - canvasRect: screenRect, - region: region, - ); - }, skip: isFirefox); -} - -Shader engineGradientToShader(GradientSweep gradient, Rect rect) { - return Gradient.sweep( - Offset( - rect.left + gradient.center.dx * rect.width, - rect.top + gradient.center.dy * rect.height, - ), - gradient.colors, - gradient.colorStops, - gradient.tileMode, - gradient.startAngle, - gradient.endAngle, - gradient.matrix4 == null ? null : Float64List.fromList(gradient.matrix4!), - ); -} - -Shader engineLinearGradientToShader(GradientLinear gradient, Rect rect) { - return Gradient.linear( - gradient.from, - gradient.to, - gradient.colors, - gradient.colorStops, - gradient.tileMode, - gradient.matrix4 == null ? null : Float64List.fromList(gradient.matrix4!.matrix), - ); -} - -Shader engineRadialGradientToShader(GradientRadial gradient, Rect rect) { - return Gradient.radial( - Offset( - rect.left + gradient.center.dx * rect.width, - rect.top + gradient.center.dy * rect.height, - ), - gradient.radius, - gradient.colors, - gradient.colorStops, - gradient.tileMode, - gradient.matrix4 == null ? null : Float64List.fromList(gradient.matrix4!), - ); -} - -Path samplePathFromRect(Rect rectBounds) => - Path() - ..moveTo(rectBounds.center.dx, rectBounds.top) - ..lineTo(rectBounds.left, rectBounds.bottom) - ..quadraticBezierTo( - rectBounds.center.dx + 20, - rectBounds.bottom - 40, - rectBounds.right, - rectBounds.bottom, - ) - ..close(); diff --git a/engine/src/flutter/lib/web_ui/test/html/shaders/image_shader_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shaders/image_shader_golden_test.dart deleted file mode 100644 index c7a6d037c6..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shaders/image_shader_golden_test.dart +++ /dev/null @@ -1,137 +0,0 @@ -// 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: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/ui.dart' hide TextStyle; - -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. -// https://github.com/flutter/flutter/issues/86623 - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const double screenWidth = 400.0; - const double screenHeight = 400.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - final HtmlImage testImage = createTestImage(); - - setUpUnitTests(setUpTestViewDimensions: false); - - void drawShapes(RecordingCanvas rc, SurfacePaint paint, Rect shaderRect) { - /// Rect. - rc.drawRect(shaderRect, paint); - shaderRect = shaderRect.translate(100, 0); - - /// Circle. - rc.drawCircle(shaderRect.center, shaderRect.width / 2, paint); - shaderRect = shaderRect.translate(110, 0); - - /// Oval. - rc.drawOval( - Rect.fromLTWH(shaderRect.left, shaderRect.top, shaderRect.width, shaderRect.height / 2), - paint, - ); - shaderRect = shaderRect.translate(-210, 120); - - /// Path. - final Path path = - Path() - ..moveTo(shaderRect.center.dx, shaderRect.top) - ..lineTo(shaderRect.right, shaderRect.bottom) - ..lineTo(shaderRect.left, shaderRect.bottom) - ..close(); - rc.drawPath(path, paint); - shaderRect = shaderRect.translate(100, 0); - - /// RRect. - rc.drawRRect(RRect.fromRectXY(shaderRect, 10, 20), paint); - shaderRect = shaderRect.translate(110, 0); - - /// DRRect. - rc.drawDRRect( - RRect.fromRectXY(shaderRect, 20, 30), - RRect.fromRectXY(shaderRect.deflate(24), 16, 24), - paint, - ); - shaderRect = shaderRect.translate(-200, 120); - } - - Future testImageShader(TileMode tmx, TileMode tmy, String fileName) async { - final RecordingCanvas rc = RecordingCanvas( - const Rect.fromLTRB(0, 0, screenWidth, screenHeight), - ); - //Rect shaderRect = const Rect.fromLTRB(20, 20, 100, 100); - const Rect shaderRect = Rect.fromLTRB(0, 0, 100, 100); - final SurfacePaint paint = Paint() as SurfacePaint; - paint.shader = ImageShader( - testImage, - tmx, - tmy, - Matrix4.identity().toFloat64(), - filterQuality: FilterQuality.high, - ); - - drawShapes(rc, paint, shaderRect); - - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, fileName, region: screenRect); - } - - test('Should draw with tiled imageshader.', () async { - await testImageShader(TileMode.repeated, TileMode.repeated, 'image_shader_tiled'); - }); - - test('Should draw with horizontally mirrored imageshader.', () async { - await testImageShader(TileMode.mirror, TileMode.repeated, 'image_shader_horiz_mirror'); - }); - - test('Should draw with vertically mirrored imageshader.', () async { - await testImageShader(TileMode.repeated, TileMode.mirror, 'image_shader_vert_mirror'); - }); - - test('Should draw with mirrored imageshader.', () async { - await testImageShader(TileMode.mirror, TileMode.mirror, 'image_shader_mirror'); - }); - - test('Should draw with horizontal clamp imageshader.', () async { - await testImageShader(TileMode.clamp, TileMode.repeated, 'image_shader_clamp_horiz'); - }, skip: isFirefox); - - test('Should draw with vertical clamp imageshader.', () async { - await testImageShader(TileMode.repeated, TileMode.clamp, 'image_shader_clamp_vertical'); - }, skip: isFirefox); - - test('Should draw with clamp imageshader.', () async { - await testImageShader(TileMode.clamp, TileMode.clamp, 'image_shader_clamp'); - }, skip: isFirefox); -} - -HtmlImage createTestImage() { - const int width = 16; - const int width2 = width ~/ 2; - const int height = 16; - final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, width2, width2); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(width2, 0, width2, width2); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(width2, width2, width2, width2); - ctx.fill(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart deleted file mode 100644 index fea11c8216..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart +++ /dev/null @@ -1,154 +0,0 @@ -// 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:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. -// https://github.com/flutter/flutter/issues/86623 - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - test('Should draw linear gradient using rectangle.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - const [Color(0xFFcfdfd2), Color(0xFF042a85)], - ); - rc.drawRect(shaderRect, paint); - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_rect'); - }); - - test('Should blend linear gradient with alpha channel correctly.', () async { - const Rect canvasRect = Rect.fromLTRB(0, 0, 500, 500); - final RecordingCanvas rc = RecordingCanvas(canvasRect); - final SurfacePaint backgroundPaint = - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFFFF0000); - rc.drawRect(canvasRect, backgroundPaint); - - const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - const [Color(0x00000000), Color(0xFF0000FF)], - ); - rc.drawRect(shaderRect, paint); - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_rect_alpha'); - }); - - test('Should draw linear gradient with transform.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final List angles = [0.0, 90.0, 180.0]; - double yOffset = 0; - for (final double angle in angles) { - final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); - final Matrix4 matrix = Matrix4.identity(); - matrix.translate(shaderRect.left, shaderRect.top); - matrix.multiply(Matrix4.rotationZ((angle / 180) * math.pi)); - final Matrix4 post = Matrix4.identity(); - post.translate(-shaderRect.left, -shaderRect.top); - matrix.multiply(post); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - const [Color(0xFFFF0000), Color(0xFF042a85)], - null, - TileMode.clamp, - matrix.toFloat64(), - ); - rc.drawRect(shaderRect, SurfacePaint()..color = const Color(0xFF000000)); - rc.drawOval(shaderRect, paint); - yOffset += 120; - } - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_oval_matrix'); - }, skip: isFirefox); - - // Regression test for https://github.com/flutter/flutter/issues/50010 - test('Should draw linear gradient using rounded rect.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - const [Color(0xFFcfdfd2), Color(0xFF042a85)], - ); - rc.drawRRect(RRect.fromRectAndRadius(shaderRect, const Radius.circular(16)), paint); - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_rounded_rect'); - }); - - test('Should draw tiled repeated linear gradient with transform.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final List angles = [0.0, 30.0, 210.0]; - double yOffset = 0; - for (final double angle in angles) { - final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.left + shaderRect.width / 2, shaderRect.top), - const [Color(0xFFFF0000), Color(0xFF042a85)], - null, - TileMode.repeated, - Matrix4.rotationZ((angle / 180) * math.pi).toFloat64(), - ); - rc.drawRect(shaderRect, SurfacePaint()..color = const Color(0xFF000000)); - rc.drawOval(shaderRect, paint); - yOffset += 120; - } - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_tiled_repeated_rect'); - }, skip: isFirefox); - - test('Should draw tiled mirrored linear gradient with transform.', () async { - final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final List angles = [0.0, 30.0, 210.0]; - double yOffset = 0; - for (final double angle in angles) { - final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); - final SurfacePaint paint = - SurfacePaint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.left + shaderRect.width / 2, shaderRect.top), - const [Color(0xFFFF0000), Color(0xFF042a85)], - null, - TileMode.mirror, - Matrix4.rotationZ((angle / 180) * math.pi).toFloat64(), - ); - rc.drawRect(shaderRect, SurfacePaint()..color = const Color(0xFF000000)); - rc.drawOval(shaderRect, paint); - yOffset += 120; - } - expect(rc.renderStrategy.hasArbitraryPaint, isTrue); - await canvasScreenshot(rc, 'linear_gradient_tiled_mirrored_rect'); - }, skip: isFirefox); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart deleted file mode 100644 index b81299a049..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart +++ /dev/null @@ -1,128 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; -import '../screenshot.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(setUpTestViewDimensions: false); - - Future testGradient( - String fileName, - Shader shader, { - Rect paintRect = const Rect.fromLTRB(50, 50, 300, 300), - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300), - Rect region = const Rect.fromLTWH(0, 0, 500, 500), - }) async { - final RecordingCanvas rc = RecordingCanvas(region); - final SurfacePaint paint = SurfacePaint()..shader = shader; - final Path path = Path(); - path.addRect(paintRect); - rc.drawPath(path, paint); - await canvasScreenshot(rc, fileName, region: region); - } - - test('Should draw centered radial gradient.', () async { - const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); - await testGradient( - 'radial_gradient_centered', - Gradient.radial( - Offset((shaderRect.left + shaderRect.right) / 2, (shaderRect.top + shaderRect.bottom) / 2), - shaderRect.width / 2, - [const Color.fromARGB(255, 0, 0, 0), const Color.fromARGB(255, 0, 0, 255)], - ), - ); - }); - - test('Should draw right bottom centered radial gradient.', () async { - const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); - await testGradient( - 'radial_gradient_right_bottom', - Gradient.radial(Offset(shaderRect.right, shaderRect.bottom), shaderRect.width / 2, [ - const Color.fromARGB(255, 0, 0, 0), - const Color.fromARGB(255, 0, 0, 255), - ]), - ); - }); - - test('Should draw with radial gradient with TileMode.clamp.', () async { - const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); - await testGradient( - 'radial_gradient_tilemode_clamp', - Gradient.radial( - Offset((shaderRect.left + shaderRect.right) / 2, (shaderRect.top + shaderRect.bottom) / 2), - shaderRect.width / 2, - [const Color.fromARGB(255, 0, 0, 0), const Color.fromARGB(255, 0, 0, 255)], - [0.0, 1.0], - ), - shaderRect: shaderRect, - ); - }); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List colorStops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - test( - 'Should draw with radial gradient with TileMode.repeated.', - () async { - const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); - await testGradient( - 'radial_gradient_tilemode_repeated', - Gradient.radial( - Offset( - (shaderRect.left + shaderRect.right) / 2, - (shaderRect.top + shaderRect.bottom) / 2, - ), - shaderRect.width / 2, - colors, - colorStops, - TileMode.repeated, - ), - shaderRect: shaderRect, - region: const Rect.fromLTWH(0, 0, 600, 800), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); - - test( - 'Should draw with radial gradient with TileMode.mirrored.', - () async { - const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); - await testGradient( - 'radial_gradient_tilemode_mirror', - Gradient.radial( - Offset( - (shaderRect.left + shaderRect.right) / 2, - (shaderRect.top + shaderRect.bottom) / 2, - ), - shaderRect.width / 2, - colors, - colorStops, - TileMode.mirror, - ), - shaderRect: shaderRect, - region: const Rect.fromLTWH(0, 0, 600, 800), - ); - }, - // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 - skip: isFirefox, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart deleted file mode 100644 index 3c7e304b1f..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart +++ /dev/null @@ -1,248 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' hide BackdropFilterEngineLayer, ClipRectEngineLayer; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../../common/test_initialization.dart'; - -/// To debug compositing failures on browsers, set this flag to true and run -/// flutter run -d chrome --web-renderer=html -/// test/golden_tests/engine/shader_mask_golden_test.dart --profile -const bool debugTest = false; - -DomElement get sceneHost => - EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost.querySelector( - DomManager.sceneHostTagName, - )!; - -Future main() async { - if (!debugTest) { - internalBootstrapBrowserTest(() => testMain); - } else { - _renderCirclesScene(BlendMode.color); - } -} - -// TODO(ferhat): unskip webkit tests once flakiness is resolved. See -// https://github.com/flutter/flutter/issues/76713 -// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. -// https://github.com/flutter/flutter/issues/86623 - -Future testMain() async { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - setUpAll(() async { - debugShowClipLayers = true; - }); - - setUp(() async { - SurfaceSceneBuilder.debugForgetFrameScene(); - for (final DomNode scene in sceneHost.querySelectorAll('flt-scene').cast()) { - scene.remove(); - } - initWebGl(); - }); - - /// Should render the picture unmodified. - test('Renders shader mask with linear gradient BlendMode dst', () async { - _renderCirclesScene(BlendMode.dst); - await matchGoldenFile('shadermask_linear_dst.png', region: const Rect.fromLTWH(0, 0, 360, 200)); - }, skip: isSafari || isFirefox); - - /// Should render the gradient only where circles have alpha channel. - test('Renders shader mask with linear gradient BlendMode srcIn', () async { - _renderCirclesScene(BlendMode.srcIn); - await matchGoldenFile( - 'shadermask_linear_srcin.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); - - test('Renders shader mask with linear gradient BlendMode color', () async { - _renderCirclesScene(BlendMode.color); - await matchGoldenFile( - 'shadermask_linear_color.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); - - test('Renders shader mask with linear gradient BlendMode xor', () async { - _renderCirclesScene(BlendMode.xor); - await matchGoldenFile('shadermask_linear_xor.png', region: const Rect.fromLTWH(0, 0, 360, 200)); - }, skip: isSafari || isFirefox); - - test('Renders shader mask with linear gradient BlendMode plus', () async { - _renderCirclesScene(BlendMode.plus); - await matchGoldenFile( - 'shadermask_linear_plus.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); - - test('Renders shader mask with linear gradient BlendMode modulate', () async { - _renderCirclesScene(BlendMode.modulate); - await matchGoldenFile( - 'shadermask_linear_modulate.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); - - test('Renders shader mask with linear gradient BlendMode overlay', () async { - _renderCirclesScene(BlendMode.overlay); - await matchGoldenFile( - 'shadermask_linear_overlay.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); - - /// Should render the gradient opaque on top of content. - test('Renders shader mask with linear gradient BlendMode src', () async { - _renderCirclesScene(BlendMode.src); - await matchGoldenFile('shadermask_linear_src.png', region: const Rect.fromLTWH(0, 0, 360, 200)); - }, skip: isSafari || isFirefox); - - /// Should render text with gradient. - test('Renders text with linear gradient shader mask', () async { - _renderTextScene(BlendMode.srcIn); - await matchGoldenFile( - 'shadermask_linear_text.png', - region: const Rect.fromLTWH(0, 0, 360, 200), - ); - }, skip: isSafari || isFirefox); -} - -Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(region); - canvas.drawCircle( - Offset(offsetX + 30, offsetY + 30), - 30, - SurfacePaint()..style = PaintingStyle.fill, - ); - canvas.drawCircle( - Offset(offsetX + 110, offsetY + 30), - 30, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFFFF0000), - ); - canvas.drawCircle( - Offset(offsetX + 30, offsetY + 110), - 30, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF00FF00), - ); - canvas.drawCircle( - Offset(offsetX + 110, offsetY + 110), - 30, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF0000FF), - ); - return recorder.endRecording(); -} - -void _renderCirclesScene(BlendMode blendMode) { - const Rect region = Rect.fromLTWH(0, 0, 400, 400); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture circles1 = _drawTestPictureWithCircles(region, 10, 10); - builder.addPicture(Offset.zero, circles1); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - const Rect shaderBounds = Rect.fromLTWH(180, 10, 140, 140); - - final EngineGradient shader = GradientLinear( - Offset(200 - shaderBounds.left, 30 - shaderBounds.top), - Offset(320 - shaderBounds.left, 150 - shaderBounds.top), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - builder.pushShaderMask(shader, shaderBounds, blendMode); - final Picture circles2 = _drawTestPictureWithCircles(region, 180, 10); - builder.addPicture(Offset.zero, circles2); - builder.pop(); - - sceneHost.append(builder.build().webOnlyRootElement!); -} - -Picture _drawTestPictureWithText(Rect region, double offsetX, double offsetY) { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(region); - const String text = 'Shader test'; - - final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 40.0, - ); - - final CanvasParagraphBuilder builder = CanvasParagraphBuilder(paragraphStyle); - builder.pushStyle(EngineTextStyle.only(color: const Color(0xFFFF0000))); - builder.addText(text); - final CanvasParagraph paragraph = builder.build(); - - const double maxWidth = 200 - 10; - paragraph.layout(const ParagraphConstraints(width: maxWidth)); - canvas.drawParagraph(paragraph, Offset(offsetX, offsetY)); - return recorder.endRecording(); -} - -void _renderTextScene(BlendMode blendMode) { - const Rect region = Rect.fromLTWH(0, 0, 600, 400); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture textPicture = _drawTestPictureWithText(region, 10, 10); - builder.addPicture(Offset.zero, textPicture); - - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38), - Color(0xFFFF8C42), - Color(0xFFFFF275), - Color(0xFF6699CC), - Color(0xFF656D78), - ]; - const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; - - const Rect shaderBounds = Rect.fromLTWH(180, 10, 140, 140); - - final EngineGradient shader = GradientLinear( - Offset(200 - shaderBounds.left, 30 - shaderBounds.top), - Offset(320 - shaderBounds.left, 150 - shaderBounds.top), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - - builder.pushShaderMask(shader, shaderBounds, blendMode); - - final Picture textPicture2 = _drawTestPictureWithText(region, 180, 10); - builder.addPicture(Offset.zero, textPicture2); - builder.pop(); - - sceneHost.append(builder.build().webOnlyRootElement!); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/shadow_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/shadow_golden_test.dart deleted file mode 100644 index e527a18396..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/shadow_golden_test.dart +++ /dev/null @@ -1,136 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import '../common/test_initialization.dart'; - -const Color _kShadowColor = Color.fromARGB(255, 0, 0, 0); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - const Rect region = Rect.fromLTWH(0, 0, 550, 300); - - late SurfaceSceneBuilder builder; - - setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); - - setUp(() { - builder = SurfaceSceneBuilder(); - }); - - void paintShapeOutline() { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - canvas.drawRect( - const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0), - SurfacePaint() - ..color = const Color.fromARGB(255, 0, 0, 255) - ..style = PaintingStyle.stroke - ..strokeWidth = 1.0, - ); - builder.addPicture(Offset.zero, recorder.endRecording()); - } - - void paintShadowBounds(SurfacePath path, double elevation) { - final Rect shadowBounds = computePenumbraBounds(path.getBounds(), elevation); - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - canvas.drawRect( - shadowBounds, - SurfacePaint() - ..color = const Color.fromARGB(255, 0, 255, 0) - ..style = PaintingStyle.stroke - ..strokeWidth = 1.0, - ); - builder.addPicture(Offset.zero, recorder.endRecording()); - } - - void paintBitmapCanvasShadow(double elevation, Offset offset, bool transparentOccluder) { - final SurfacePath path = SurfacePath()..addRect(const Rect.fromLTRB(0, 0, 20, 20)); - builder.pushOffset(offset.dx, offset.dy); - - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - canvas.debugEnforceArbitraryPaint(); // make sure DOM canvas doesn't take over - canvas.drawShadow(path, _kShadowColor, elevation, transparentOccluder); - builder.addPicture(Offset.zero, recorder.endRecording()); - paintShapeOutline(); - paintShadowBounds(path, elevation); - - builder.pop(); // offset - } - - void paintBitmapCanvasComplexPathShadow(double elevation, Offset offset) { - final SurfacePath path = - SurfacePath() - ..moveTo(10, 0) - ..lineTo(20, 10) - ..lineTo(10, 20) - ..lineTo(0, 10) - ..close(); - builder.pushOffset(offset.dx, offset.dy); - - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); - canvas.debugEnforceArbitraryPaint(); // make sure DOM canvas doesn't take over - canvas.drawShadow(path, _kShadowColor, elevation, false); - canvas.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = const Color.fromARGB(255, 0, 0, 255), - ); - builder.addPicture(Offset.zero, recorder.endRecording()); - paintShadowBounds(path, elevation); - - builder.pop(); // offset - } - - test('renders shadows correctly', () async { - // Physical shape clips. We want to see that clipping in the screenshot. - debugShowClipLayers = false; - - builder.pushOffset(10, 20); - - for (int i = 0; i < 10; i++) { - paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 60), false); - } - - for (int i = 0; i < 10; i++) { - paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 120), true); - } - - for (int i = 0; i < 10; i++) { - paintBitmapCanvasComplexPathShadow(i.toDouble(), Offset(50.0 * i, 180)); - } - - builder.pop(); - - final DomElement sceneElement = builder.build().webOnlyRootElement!; - domDocument.body!.append(sceneElement); - - await matchGoldenFile('shadows.png', region: region); - }, testOn: 'chrome'); - - /// For dart testing having `no tests ran` in a file is considered an error - /// and result in exit code 1. - /// See: https://github.com/dart-lang/test/pull/1173 - /// - /// Since screenshot tests run one by one and exit code is checked immediately - /// after that a test file that only runs in chrome will break the other - /// browsers. This method is added as a bandaid solution. - test('dummy tests to pass on other browsers', () async { - expect(2 + 2, 4); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/path/path_iterator_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/path/path_iterator_test.dart deleted file mode 100644 index 155a0f6972..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/path/path_iterator_test.dart +++ /dev/null @@ -1,90 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - final Float32List points = Float32List(PathIterator.kMaxBufferSize); - - group('PathIterator', () { - test('Should return done verb for empty path', () { - final SurfacePath path = SurfacePath(); - final PathIterator iter = PathIterator(path.pathRef, false); - expect(iter.peek(), SPath.kDoneVerb); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should return done when moveTo is last instruction', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 10); - final PathIterator iter = PathIterator(path.pathRef, false); - expect(iter.peek(), SPath.kMoveVerb); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should return lineTo', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 10); - path.lineTo(20, 20); - final PathIterator iter = PathIterator(path.pathRef, false); - expect(iter.peek(), SPath.kMoveVerb); - expect(iter.next(points), SPath.kMoveVerb); - expect(points[0], 10); - expect(iter.next(points), SPath.kLineVerb); - expect(points[2], 20); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should return extra lineTo if iteration is closed', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 10); - path.lineTo(20, 20); - final PathIterator iter = PathIterator(path.pathRef, true); - expect(iter.peek(), SPath.kMoveVerb); - expect(iter.next(points), SPath.kMoveVerb); - expect(points[0], 10); - expect(iter.next(points), SPath.kLineVerb); - expect(points[2], 20); - expect(iter.next(points), SPath.kLineVerb); - expect(points[2], 10); - expect(iter.next(points), SPath.kCloseVerb); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('Should not return extra lineTo if last point is starting point', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 10); - path.lineTo(20, 20); - path.lineTo(10, 10); - final PathIterator iter = PathIterator(path.pathRef, true); - expect(iter.peek(), SPath.kMoveVerb); - expect(iter.next(points), SPath.kMoveVerb); - expect(points[0], 10); - expect(iter.next(points), SPath.kLineVerb); - expect(points[2], 20); - expect(iter.next(points), SPath.kLineVerb); - expect(points[2], 10); - expect(iter.next(points), SPath.kCloseVerb); - expect(iter.next(points), SPath.kDoneVerb); - }); - - test('peek should return lineTo if iteration is closed', () { - final SurfacePath path = SurfacePath(); - path.moveTo(10, 10); - path.lineTo(20, 20); - final PathIterator iter = PathIterator(path.pathRef, true); - expect(iter.next(points), SPath.kMoveVerb); - expect(iter.next(points), SPath.kLineVerb); - expect(iter.peek(), SPath.kLineVerb); - }); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/path/path_winding_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/path/path_winding_test.dart deleted file mode 100644 index 8e3b059082..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/path/path_winding_test.dart +++ /dev/null @@ -1,473 +0,0 @@ -// 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:math' as math; -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide window; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -/// Test winding and convexity of a path. -void testMain() { - group('Convexity', () { - test('Empty path should be convex', () { - final SurfacePath path = SurfacePath(); - expect(path.isConvex, isTrue); - }); - - test('Circle should be convex', () { - final SurfacePath path = SurfacePath(); - path.addOval(const Rect.fromLTRB(0, 0, 20, 20)); - expect(path.isConvex, isTrue); - // 2nd circle. - path.addOval(const Rect.fromLTRB(0, 0, 20, 20)); - expect(path.isConvex, isFalse); - }); - - test('addRect should be convex', () { - SurfacePath path = SurfacePath(); - path.addRect(const Rect.fromLTRB(0, 0, 20, 20)); - expect(path.isConvex, isTrue); - - path = SurfacePath(); - path.addRectWithDirection(const Rect.fromLTRB(0, 0, 20, 20), SPathDirection.kCW, 0); - expect(path.isConvex, isTrue); - - path = SurfacePath(); - path.addRectWithDirection(const Rect.fromLTRB(0, 0, 20, 20), SPathDirection.kCCW, 0); - expect(path.isConvex, isTrue); - }); - - test('Quad should be convex', () { - final SurfacePath path = SurfacePath(); - path.quadraticBezierTo(100, 100, 50, 50); - expect(path.isConvex, isTrue); - }); - - test('moveto/lineto convexity', () { - final List testCases = [ - LineTestCase('', SPathConvexityType.kConvex), - LineTestCase('0 0', SPathConvexityType.kConvex), - LineTestCase('0 0 10 10', SPathConvexityType.kConvex), - LineTestCase('0 0 10 10 20 20 0 0 10 10', SPathConvexityType.kConcave), - LineTestCase('0 0 10 10 10 20', SPathConvexityType.kConvex), - LineTestCase('0 0 10 10 10 0', SPathConvexityType.kConvex), - LineTestCase('0 0 10 10 10 0 0 10', SPathConvexityType.kConcave), - LineTestCase('0 0 10 0 0 10 -10 -10', SPathConvexityType.kConcave), - ]; - - for (final LineTestCase testCase in testCases) { - final SurfacePath path = SurfacePath(); - setFromString(path, testCase.pathContent); - expect(path.convexityType, testCase.convexity); - } - }); - - test('Convexity of path with infinite points should return unknown', () { - const List nonFinitePts = [ - Offset(double.infinity, 0), - Offset(0, double.infinity), - Offset.infinite, - Offset(double.negativeInfinity, 0), - Offset(0, double.negativeInfinity), - Offset(double.negativeInfinity, double.negativeInfinity), - Offset(double.negativeInfinity, double.infinity), - Offset(double.infinity, double.negativeInfinity), - Offset(double.nan, 0), - Offset(0, double.nan), - Offset(double.nan, double.nan), - ]; - final int nonFinitePointsCount = nonFinitePts.length; - - const List axisAlignedPts = [ - Offset(kScalarMax, 0), - Offset(0, kScalarMax), - Offset(kScalarMin, 0), - Offset(0, kScalarMin), - ]; - final int axisAlignedPointsCount = axisAlignedPts.length; - - final SurfacePath path = SurfacePath(); - - for (int index = 0; index < (13 * nonFinitePointsCount * axisAlignedPointsCount); index++) { - final int i = index % nonFinitePointsCount; - final int f = index % axisAlignedPointsCount; - final int g = (f + 1) % axisAlignedPointsCount; - path.reset(); - switch (index % 13) { - case 0: - path.lineTo(nonFinitePts[i].dx, nonFinitePts[i].dy); - case 1: - path.quadraticBezierTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 2: - path.quadraticBezierTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 3: - path.quadraticBezierTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 4: - path.cubicTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 5: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 6: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 7: - path.cubicTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 8: - path.cubicTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 9: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 10: - path.cubicTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - nonFinitePts[i].dx, - nonFinitePts[i].dy, - ); - case 11: - path.cubicTo( - nonFinitePts[i].dx, - nonFinitePts[i].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - ); - case 12: - path.moveTo(nonFinitePts[i].dx, nonFinitePts[i].dy); - } - expect(path.convexityType, SPathConvexityType.kUnknown); - } - - for (int index = 0; index < (11 * axisAlignedPointsCount); ++index) { - final int f = index % axisAlignedPointsCount; - final int g = (f + 1) % axisAlignedPointsCount; - path.reset(); - final int curveSelect = index % 11; - switch (curveSelect) { - case 0: - path.moveTo(axisAlignedPts[f].dx, axisAlignedPts[f].dy); - case 1: - path.lineTo(axisAlignedPts[f].dx, axisAlignedPts[f].dy); - case 2: - path.quadraticBezierTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 3: - path.quadraticBezierTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - ); - case 4: - path.quadraticBezierTo( - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 5: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 6: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - ); - case 7: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 8: - path.cubicTo( - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - ); - case 9: - path.cubicTo( - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - ); - case 10: - path.cubicTo( - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - axisAlignedPts[f].dx, - axisAlignedPts[f].dy, - axisAlignedPts[g].dx, - axisAlignedPts[g].dy, - ); - } - if (curveSelect != 7 && curveSelect != 10) { - final int result = path.convexityType; - expect(result, SPathConvexityType.kConvex); - } else { - // we make a copy so that we don't cache the result on the passed - // in path. - final SurfacePath path2 = SurfacePath.from(path); - final int c = path2.convexityType; - assert(SPathConvexityType.kUnknown == c || SPathConvexityType.kConcave == c); - } - } - }); - - test('Concave lines path', () { - final SurfacePath path = SurfacePath(); - path.moveTo(-0.284071773, -0.0622361786); - path.lineTo(-0.284072, -0.0622351); - path.lineTo(-0.28407, -0.0622307); - path.lineTo(-0.284067, -0.0622182); - path.lineTo(-0.284084, -0.0622269); - path.lineTo(-0.284072, -0.0622362); - path.close(); - expect(path.convexityType, SPathConvexityType.kConcave); - }); - - test('Single moveTo origin', () { - final SurfacePath path = SurfacePath(); - path.moveTo(0, 0); - expect(path.convexityType, SPathConvexityType.kConvex); - }); - - test('Single diagonal line', () { - final SurfacePath path = SurfacePath(); - path.moveTo(12, 20); - path.lineTo(-12, -20); - expect(path.convexityType, SPathConvexityType.kConvex); - }); - - test('TriLeft', () { - final SurfacePath path = SurfacePath(); - path.moveTo(0, 0); - path.lineTo(1, 0); - path.lineTo(1, 1); - path.close(); - expect(path.convexityType, SPathConvexityType.kConvex); - }); - - test('TriRight', () { - final SurfacePath path = SurfacePath(); - path.moveTo(0, 0); - path.lineTo(-1, 0); - path.lineTo(1, 1); - path.close(); - expect(path.convexityType, SPathConvexityType.kConvex); - }); - - test('square', () { - final SurfacePath path = SurfacePath(); - path.moveTo(0, 0); - path.lineTo(1, 0); - path.lineTo(1, 1); - path.lineTo(0, 1); - path.close(); - expect(path.convexityType, SPathConvexityType.kConvex); - }); - - test('redundant square', () { - final SurfacePath redundantSquare = SurfacePath(); - redundantSquare.moveTo(0, 0); - redundantSquare.lineTo(0, 0); - redundantSquare.lineTo(0, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(0, 1); - redundantSquare.lineTo(0, 1); - redundantSquare.lineTo(0, 1); - redundantSquare.close(); - expect(redundantSquare.convexityType, SPathConvexityType.kConvex); - }); - - test('bowtie', () { - final SurfacePath bowTie = SurfacePath(); - bowTie.moveTo(0, 0); - bowTie.lineTo(0, 0); - bowTie.lineTo(0, 0); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); - bowTie.close(); - expect(bowTie.convexityType, SPathConvexityType.kConcave); - }); - - test('sprial', () { - final SurfacePath spiral = SurfacePath(); - spiral.moveTo(0, 0); - spiral.lineTo(100, 0); - spiral.lineTo(100, 100); - spiral.lineTo(0, 100); - spiral.lineTo(0, 50); - spiral.lineTo(50, 50); - spiral.lineTo(50, 75); - spiral.close(); - expect(spiral.convexityType, SPathConvexityType.kConcave); - }); - - test('dent', () { - final SurfacePath dent = SurfacePath(); - dent.moveTo(0, 0); - dent.lineTo(100, 100); - dent.lineTo(0, 100); - dent.lineTo(-50, 200); - dent.lineTo(-200, 100); - dent.close(); - expect(dent.convexityType, SPathConvexityType.kConcave); - }); - - test('degenerate segments1', () { - final SurfacePath strokedSin = SurfacePath(); - for (int i = 0; i < 2000; i++) { - final double x = i.toDouble() / 2.0; - final double y = 500 - (x + math.sin(x / 100) * 40) / 3; - if (0 == i) { - strokedSin.moveTo(x, y); - } else { - strokedSin.lineTo(x, y); - } - } - expect(strokedSin.convexityType, SPathConvexityType.kConcave); - }); - - /// Regression test for https://github.com/flutter/flutter/issues/66560. - test('Quadratic', () { - final SurfacePath path = SurfacePath(); - path.moveTo(100.0, 0.0); - path.quadraticBezierTo(200.0, 0.0, 200.0, 100.0); - path.quadraticBezierTo(200.0, 200.0, 100.0, 200.0); - path.quadraticBezierTo(0.0, 200.0, 0.0, 100.0); - path.quadraticBezierTo(0.0, 0.0, 100.0, 0.0); - path.close(); - expect(path.contains(const Offset(100, 20)), isTrue); - expect(path.contains(const Offset(100, 120)), isTrue); - expect(path.contains(const Offset(100, -10)), isFalse); - }); - }); -} - -class LineTestCase { - LineTestCase(this.pathContent, this.convexity); - final String pathContent; - final int convexity; -} - -/// Parses a string of the format "mx my lx1 ly1 lx2 ly2..." into a path -/// with moveTo/lineTo instructions for points. -void setFromString(SurfacePath path, String value) { - bool first = true; - final List points = value.split(' '); - if (points.length < 2) { - return; - } - for (int i = 0; i < points.length; i += 2) { - if (first) { - path.moveTo(double.parse(points[i]), double.parse(points[i + 1])); - first = false; - } else { - path.lineTo(double.parse(points[i]), double.parse(points[i + 1])); - } - } -} - -// Scalar max is based on 32 bit float since [PathRef] stores values in -// Float32List. -const double kScalarMax = 3.402823466e+38; -const double kScalarMin = -kScalarMax; diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/platform_view_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/platform_view_test.dart deleted file mode 100644 index 4e66d6fddf..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/platform_view_test.dart +++ /dev/null @@ -1,93 +0,0 @@ -// 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:async'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; - -import '../../common/matchers.dart'; -import '../../common/test_initialization.dart'; - -const MethodCodec codec = StandardMethodCodec(); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - await bootstrapAndRunApp(withImplicitView: true); - - late PersistedPlatformView view; - - group('PersistedPlatformView', () { - setUp(() async { - ui_web.platformViewRegistry.registerViewFactory( - 'test-0', - (int viewId) => createDomHTMLDivElement(), - ); - ui_web.platformViewRegistry.registerViewFactory( - 'test-1', - (int viewId) => createDomHTMLDivElement(), - ); - // Ensure the views are created... - await Future.wait(>[ - _createPlatformView(0, 'test-0'), - _createPlatformView(1, 'test-1'), - ]); - view = PersistedPlatformView(0, 0, 0, 100, 100)..build(); - }); - - group('update', () { - test('throws assertion error if called with different viewIds', () { - final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100) - ..build(); - expect(() { - view.update(differentView); - }, throwsAssertionError); - }); - }); - - group('canUpdateAsMatch', () { - test('returns true when viewId is the same', () { - final PersistedPlatformView sameView = PersistedPlatformView(0, 1, 1, 100, 100)..build(); - expect(view.canUpdateAsMatch(sameView), isTrue); - }); - - test('returns false when viewId is different', () { - final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100) - ..build(); - expect(view.canUpdateAsMatch(differentView), isFalse); - }); - - test('returns false when other view is not a PlatformView', () { - final PersistedOpacity anyView = PersistedOpacity(null, 1, ui.Offset.zero)..build(); - expect(view.canUpdateAsMatch(anyView), isFalse); - }); - }); - - group('createElement', () { - test('creates slot element that can receive pointer events', () { - final DomElement element = view.createElement(); - - expect(element.tagName, equalsIgnoringCase('flt-platform-view-slot')); - expect(element.style.pointerEvents, 'auto'); - }); - }); - }); -} - -// Sends a platform message to create a Platform View with the given id and viewType. -Future _createPlatformView(int id, String viewType) { - final Completer completer = Completer(); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform_views', - codec.encodeMethodCall(MethodCall('create', {'id': id, 'viewType': viewType})), - (dynamic _) => completer.complete(), - ); - return completer.future; -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/scene_builder_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/scene_builder_test.dart deleted file mode 100644 index d17cd36687..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/scene_builder_test.dart +++ /dev/null @@ -1,961 +0,0 @@ -// 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. - -@TestOn('chrome || firefox') -library; - -import 'dart:async'; -import 'dart:js_interop'; -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/ui.dart' as ui; - -import '../../common/matchers.dart'; -import '../../common/rendering.dart'; -import '../../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - setUpAll(() async { - await bootstrapAndRunApp(withImplicitView: true); - setUpRenderingForTests(); - }); - - group('SceneBuilder', () { - test('pushOffset implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushOffset(10, 20, oldLayer: oldLayer as ui.OffsetEngineLayer?); - }, - () { - return ''''''; - }, - ); - }); - - test('pushTransform implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushTransform( - (Matrix4.identity()..scale(EngineFlutterDisplay.instance.browserDevicePixelRatio)) - .toFloat64(), - ); - }, - () { - return ''''''; - }, - ); - }); - - test('pushClipRect implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushClipRect( - const ui.Rect.fromLTRB(10, 20, 30, 40), - oldLayer: oldLayer as ui.ClipRectEngineLayer?, - ); - }, - () { - return ''' - - - -'''; - }, - ); - }); - - test('pushClipRRect implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushClipRRect( - ui.RRect.fromLTRBR(10, 20, 30, 40, const ui.Radius.circular(3)), - oldLayer: oldLayer as ui.ClipRRectEngineLayer?, - clipBehavior: ui.Clip.none, - ); - }, - () { - return ''' - - - - - -'''; - }, - ); - }); - - test('pushClipPath implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - final ui.Path path = ui.Path()..addRect(const ui.Rect.fromLTRB(10, 20, 30, 40)); - return sceneBuilder.pushClipPath(path, oldLayer: oldLayer as ui.ClipPathEngineLayer?); - }, - () { - return ''' - - - - - -'''; - }, - ); - }); - - test('pushOpacity implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushOpacity(10, oldLayer: oldLayer as ui.OpacityEngineLayer?); - }, - () { - return ''''''; - }, - ); - }); - test('pushBackdropFilter implements surface lifecycle', () { - testLayerLifeCycle( - (ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { - return sceneBuilder.pushBackdropFilter( - ui.ImageFilter.blur(sigmaX: 1.0, sigmaY: 1.0), - oldLayer: oldLayer as ui.BackdropFilterEngineLayer?, - ); - }, - () { - return ''' - - - - - -'''; - }, - ); - }); - }); - - group('parent child lifecycle', () { - test('build, retain, update, and applyPaint are called the right number of times', () { - final PersistedScene scene1 = PersistedScene(null); - final PersistedClipRect clip1 = PersistedClipRect( - null, - const ui.Rect.fromLTRB(10, 10, 20, 20), - ui.Clip.antiAlias, - ); - final PersistedOpacity opacity = PersistedOpacity(null, 100, ui.Offset.zero); - final MockPersistedPicture picture = MockPersistedPicture(); - - scene1.appendChild(clip1); - clip1.appendChild(opacity); - opacity.appendChild(picture); - - expect(picture.retainCount, 0); - expect(picture.buildCount, 0); - expect(picture.updateCount, 0); - expect(picture.applyPaintCount, 0); - - scene1.preroll(PrerollSurfaceContext()); - scene1.build(); - commitScene(scene1); - expect(picture.retainCount, 0); - expect(picture.buildCount, 1); - expect(picture.updateCount, 0); - expect(picture.applyPaintCount, 1); - - // The second scene graph retains the opacity, but not the clip. However, - // because the clip didn't change no repaints should happen. - final PersistedScene scene2 = PersistedScene(scene1); - final PersistedClipRect clip2 = PersistedClipRect( - clip1, - const ui.Rect.fromLTRB(10, 10, 20, 20), - ui.Clip.antiAlias, - ); - clip1.state = PersistedSurfaceState.pendingUpdate; - scene2.appendChild(clip2); - opacity.state = PersistedSurfaceState.pendingRetention; - clip2.appendChild(opacity); - - scene2.preroll(PrerollSurfaceContext()); - scene2.update(scene1); - commitScene(scene2); - expect(picture.retainCount, 1); - expect(picture.buildCount, 1); - expect(picture.updateCount, 0); - expect(picture.applyPaintCount, 1); - - // The third scene graph retains the opacity, and produces a new clip. - // This should cause the picture to repaint despite being retained. - final PersistedScene scene3 = PersistedScene(scene2); - final PersistedClipRect clip3 = PersistedClipRect( - clip2, - const ui.Rect.fromLTRB(10, 10, 50, 50), - ui.Clip.antiAlias, - ); - clip2.state = PersistedSurfaceState.pendingUpdate; - scene3.appendChild(clip3); - opacity.state = PersistedSurfaceState.pendingRetention; - clip3.appendChild(opacity); - - scene3.preroll(PrerollSurfaceContext()); - scene3.update(scene2); - commitScene(scene3); - expect(picture.retainCount, 2); - expect(picture.buildCount, 1); - expect(picture.updateCount, 0); - expect(picture.applyPaintCount, 2); - }); - }); - - group('Compositing order', () { - // Regression test for https://github.com/flutter/flutter/issues/55058 - // - // When BitmapCanvas uses multiple elements to paint, the very first - // canvas needs to have a -1 zIndex so it can preserve compositing order. - test('Canvas element should retain -1 zIndex after update', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPicture(); - final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( - const ui.Rect.fromLTRB(10, 10, 300, 300), - ); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelector('canvas')!.style.zIndex, '-1'); - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect(const ui.Rect.fromLTRB(5, 10, 300, 300), oldLayer: oldLayer); - final ui.Picture picture2 = _drawPicture(); - builder2.addPicture(ui.Offset.zero, picture2); - builder2.pop(); - - final DomElement contentAfterReuse = builder2.build().webOnlyRootElement!; - expect(contentAfterReuse.querySelector('canvas')!.style.zIndex, '-1'); - }); - - test('Multiple canvas elements should retain zIndex after update', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPathImagePath(); - final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( - const ui.Rect.fromLTRB(10, 10, 300, 300), - ); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - - final DomElement content = builder.build().webOnlyRootElement!; - domDocument.body!.append(content); - expect(content.querySelector('canvas')!.style.zIndex, '-1'); - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect(const ui.Rect.fromLTRB(5, 10, 300, 300), oldLayer: oldLayer); - final ui.Picture picture2 = _drawPathImagePath(); - builder2.addPicture(ui.Offset.zero, picture2); - builder2.pop(); - - final DomElement contentAfterReuse = builder2.build().webOnlyRootElement!; - final List list = - contentAfterReuse.querySelectorAll('canvas').cast().toList(); - expect(list[0].style.zIndex, '-1'); - expect(list[1].style.zIndex, ''); - }); - }); - - /// Verify elementCache is passed during update to reuse existing - /// image elements. - test('Should retain same image element', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPathImagePath(); - final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( - const ui.Rect.fromLTRB(10, 10, 300, 300), - ); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - - final DomElement content = builder.build().webOnlyRootElement!; - domDocument.body!.append(content); - List list = - content.querySelectorAll('img').cast().toList(); - for (final DomHTMLImageElement image in list) { - image.alt = 'marked'; - } - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect(const ui.Rect.fromLTRB(5, 10, 300, 300), oldLayer: oldLayer); - final ui.Picture picture2 = _drawPathImagePath(); - builder2.addPicture(ui.Offset.zero, picture2); - builder2.pop(); - - final DomElement contentAfterReuse = builder2.build().webOnlyRootElement!; - list = contentAfterReuse.querySelectorAll('img').cast().toList(); - for (final DomHTMLImageElement image in list) { - expect(image.alt, 'marked'); - } - expect(list.length, 1); - }); - - PersistedPicture? findPictureSurfaceChild(PersistedContainerSurface parent) { - PersistedPicture? pictureSurface; - parent.visitChildren((PersistedSurface child) { - pictureSurface = child as PersistedPicture; - }); - return pictureSurface; - } - - test('skips painting picture when picture fully clipped out', () async { - final ui.Picture picture = _drawPicture(); - - // Picture not clipped out, so we should see a `` - { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - builder.addPicture(ui.Offset.zero, picture); - builder.pop(); - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); - } - - // Picture fully clipped out, so we should not see a `` - { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - final PersistedContainerSurface clip = - builder.pushClipRect(const ui.Rect.fromLTRB(1000, 1000, 2000, 2000)) - as PersistedContainerSurface; - builder.addPicture(ui.Offset.zero, picture); - builder.pop(); - builder.pop(); - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(clip)!.canvas, isNull); - } - }); - - test('does not skip painting picture when picture is ' - 'inside transform with offset', () async { - final ui.Picture picture = _drawPicture(); - // Picture should not be clipped out since transform will offset it to 500,500 - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - builder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; - builder.pushTransform((Matrix4.identity()..scale(0.5, 0.5)).toFloat64()); - builder.addPicture(const ui.Offset(1000, 1000), picture); - builder.pop(); - builder.pop(); - builder.pop(); - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); - }); - - test('does not skip painting picture when picture is ' - 'inside transform', () async { - final ui.Picture picture = _drawPicture(); - // Picture should not be clipped out since transform will offset it to 500,500 - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - builder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; - builder.pushTransform((Matrix4.identity()..scale(0.5, 0.5)).toFloat64()); - builder.pushOffset(1000, 1000); - builder.addPicture(ui.Offset.zero, picture); - builder.pop(); - builder.pop(); - builder.pop(); - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); - }); - - test('skips painting picture when picture fully clipped out with' - ' transform and offset', () async { - final ui.Picture picture = _drawPicture(); - // Picture should be clipped out since transform will offset it to 500,500 - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(50, 50); - builder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; - builder.pushTransform((Matrix4.identity()..scale(2, 2)).toFloat64()); - builder.pushOffset(500, 500); - builder.addPicture(ui.Offset.zero, picture); - builder.pop(); - builder.pop(); - builder.pop(); - builder.pop(); - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - }); - - test('releases old canvas when picture is fully clipped out after addRetained', () async { - final ui.Picture picture = _drawPicture(); - - // Frame 1: picture visible - final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); - final PersistedOffset offset1 = builder1.pushOffset(0, 0) as PersistedOffset; - builder1.addPicture(ui.Offset.zero, picture); - builder1.pop(); - final DomElement content1 = builder1.build().webOnlyRootElement!; - expect(content1.querySelectorAll('flt-picture').single.children, isNotEmpty); - expect(findPictureSurfaceChild(offset1)!.canvas, isNotNull); - - // Frame 2: picture is clipped out after an update - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - final PersistedOffset offset2 = - builder2.pushOffset(-10000, -10000, oldLayer: offset1) as PersistedOffset; - builder2.addPicture(ui.Offset.zero, picture); - builder2.pop(); - final DomElement content = builder2.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(offset2)!.canvas, isNull); - }); - - test('releases old canvas when picture is fully clipped out after addRetained', () async { - final ui.Picture picture = _drawPicture(); - - // Frame 1: picture visible - final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); - final PersistedOffset offset1 = builder1.pushOffset(0, 0) as PersistedOffset; - final PersistedOffset subOffset1 = builder1.pushOffset(0, 0) as PersistedOffset; - builder1.addPicture(ui.Offset.zero, picture); - builder1.pop(); - builder1.pop(); - final DomElement content1 = builder1.build().webOnlyRootElement!; - expect(content1.querySelectorAll('flt-picture').single.children, isNotEmpty); - expect(findPictureSurfaceChild(subOffset1)!.canvas, isNotNull); - - // Frame 2: picture is clipped out after addRetained - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushOffset(-10000, -10000, oldLayer: offset1); - - // Even though the child offset is added as retained, the parent - // is updated with a value that causes the picture to move out of - // the clipped area. We should see the canvas being released. - builder2.addRetained(subOffset1); - builder2.pop(); - final DomElement content = builder2.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(subOffset1)!.canvas, isNull); - }); - - test('auto-pops pushed layers', () async { - final ui.Picture picture = _drawPicture(); - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - builder.pushOffset(0, 0); - builder.pushOffset(0, 0); - builder.pushOffset(0, 0); - builder.pushOffset(0, 0); - builder.addPicture(ui.Offset.zero, picture); - - // Intentionally pop fewer layers than we pushed - builder.pop(); - builder.pop(); - builder.pop(); - - // Expect as many layers as we pushed (not popped). - final DomElement content = builder.build().webOnlyRootElement!; - expect(content.querySelectorAll('flt-offset'), hasLength(5)); - }); - - test('updates child lists efficiently', () async { - // Pushes a single child that renders one character. - // - // If the character is a number, pushes an offset layer. Otherwise, pushes - // an offset layer. Test cases use this to control how layers are reused. - // Layers of the same type can be reused even if they are not explicitly - // updated. Conversely, layers of different types are never reused. - ui.EngineLayer pushChild(SurfaceSceneBuilder builder, String char, {ui.EngineLayer? oldLayer}) { - // Numbers use opacity layers, letters use offset layers. This is used to - // control DOM reuse. Layers of the same type can reuse DOM nodes from other - // dropped layers. - final bool useOffset = int.tryParse(char) == null; - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording( - const ui.Rect.fromLTRB(0, 0, 400, 400), - ); - final ui.Paragraph paragraph = - (ui.ParagraphBuilder(ui.ParagraphStyle()) - ..pushStyle(ui.TextStyle(decoration: ui.TextDecoration.lineThrough)) - ..addText(char)) - .build(); - paragraph.layout(const ui.ParagraphConstraints(width: 1000)); - canvas.drawParagraph(paragraph, ui.Offset.zero); - final ui.EngineLayer newLayer = - useOffset - ? builder.pushOffset( - 0, - 0, - oldLayer: oldLayer == null ? null : oldLayer as ui.OffsetEngineLayer, - ) - : builder.pushOpacity( - 100, - oldLayer: oldLayer == null ? null : oldLayer as ui.OpacityEngineLayer, - ); - builder.addPicture(ui.Offset.zero, recorder.endRecording()); - builder.pop(); - return newLayer; - } - - // Maps letters to layers used to render them in the last frame, used to - // supply `oldLayer` to guarantee update. - final Map renderedLayers = {}; - - // Pump an empty scene to reset it, otherwise the first frame will attempt - // to diff left-overs from a previous test, which results in unpredictable - // DOM mutations. - await renderScene(SurfaceSceneBuilder().build()); - - // Renders a `string` by breaking it up into individual characters and - // rendering each character into its own layer. - Future testCase( - String string, - String description, { - int deletions = 0, - int additions = 0, - int moves = 0, - }) { - final Set actualDeletions = {}; - final Set actualAdditions = {}; - - // Watches DOM mutations and counts deletions and additions to the child - // list of the `` element. - final DomMutationObserver observer = createDomMutationObserver(( - JSArray mutations, - _, - ) { - for (final DomMutationRecord record in mutations.toDart.cast()) { - actualDeletions.addAll(record.removedNodes!); - actualAdditions.addAll(record.addedNodes!); - } - }); - observer.observe(SurfaceSceneBuilder.debugLastFrameScene!.rootElement!, childList: true); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - for (int i = 0; i < string.length; i++) { - final String char = string[i]; - renderedLayers[char] = pushChild(builder, char, oldLayer: renderedLayers[char]); - } - final SurfaceScene scene = builder.build(); - final List pTags = - scene.webOnlyRootElement!.querySelectorAll('flt-paragraph').toList(); - expect(pTags, hasLength(string.length)); - expect( - scene.webOnlyRootElement! - .querySelectorAll('flt-paragraph') - .map((DomElement p) => p.innerText) - .join(), - string, - ); - renderedLayers.removeWhere((String key, ui.EngineLayer value) => !string.contains(key)); - - // Inject a zero-duration timer to allow mutation observers to receive notification. - return Future.delayed(Duration.zero).then((_) { - observer.disconnect(); - - // Nodes that are removed then added are classified as "moves". - final int actualMoves = actualAdditions.intersection(actualDeletions).length; - // Compare all at once instead of one by one because when it fails, it's - // much more useful to see all numbers, not just the one that failed to - // match. - expect( - { - 'additions': actualAdditions.length - actualMoves, - 'deletions': actualDeletions.length - actualMoves, - 'moves': actualMoves, - }, - {'additions': additions, 'deletions': deletions, 'moves': moves}, - ); - }); - } - - // Adding - await testCase('', 'noop'); - await testCase('', 'noop'); - await testCase('be', 'zero-to-many', additions: 2); - await testCase('bcde', 'insert in the middle', additions: 2); - await testCase('abcde', 'prepend', additions: 1); - await testCase('abcdef', 'append', additions: 1); - - // Moving - await testCase('fbcdea', 'swap at ends', moves: 2); - await testCase('fecdba', 'swap in the middle', moves: 2); - await testCase('fedcba', 'swap adjacent in one move', moves: 1); - await testCase('fedcba', 'non-empty noop'); - await testCase('afedcb', 'shift right by 1', moves: 1); - await testCase('fedcba', 'shift left by 1', moves: 1); - await testCase('abcdef', 'reverse', moves: 5); - await testCase('efabcd', 'shift right by 2', moves: 2); - await testCase('abcdef', 'shift left by 2', moves: 2); - - // Scrolling without DOM reuse (numbers and letters use different types of layers) - await testCase('9abcde', 'scroll right by 1', additions: 1, deletions: 1); - await testCase('789abc', 'scroll right by 2', additions: 2, deletions: 2); - await testCase('89abcd', 'scroll left by 1', additions: 1, deletions: 1); - await testCase('abcdef', 'scroll left by 2', additions: 2, deletions: 2); - - // Scrolling with DOM reuse - await testCase('zabcde', 'scroll right by 1', moves: 1); - await testCase('xyzabc', 'scroll right by 2', moves: 2); - await testCase('yzabcd', 'scroll left by 1', moves: 1); - await testCase('abcdef', 'scroll left by 2', moves: 2); - - // Removing - await testCase('bcdef', 'remove as start', deletions: 1); - await testCase('bcde', 'remove as end', deletions: 1); - await testCase('be', 'remove in the middle', deletions: 2); - await testCase('', 'remove all', deletions: 2); - }); - - test('Canvas should allocate fewer pixels when zoomed out', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPicture(); - builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - - final DomElement content = builder.build().webOnlyRootElement!; - final DomCanvasElement canvas = content.querySelector('canvas')! as DomCanvasElement; - final int unscaledWidth = canvas.width!.toInt(); - final int unscaledHeight = canvas.height!.toInt(); - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushOffset(0, 0); - builder2.pushTransform(Matrix4.identity().scaled(0.5, 0.5).toFloat64()); - builder2.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); - builder2.addPicture(ui.Offset.zero, picture1); - builder2.pop(); - builder2.pop(); - builder2.pop(); - - final DomElement contentAfterScale = builder2.build().webOnlyRootElement!; - final DomCanvasElement canvas2 = contentAfterScale.querySelector('canvas')! as DomCanvasElement; - // Although we are drawing same picture, due to scaling the new canvas - // should have fewer pixels. - expect(canvas2.width! < unscaledWidth, isTrue); - expect(canvas2.height! < unscaledHeight, isTrue); - }); - - test('Canvas should allocate more pixels when zoomed in', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPicture(); - builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - - final DomElement content = builder.build().webOnlyRootElement!; - final DomCanvasElement canvas = content.querySelector('canvas')! as DomCanvasElement; - final int unscaledWidth = canvas.width!.toInt(); - final int unscaledHeight = canvas.height!.toInt(); - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushOffset(0, 0); - builder2.pushTransform(Matrix4.identity().scaled(2, 2).toFloat64()); - builder2.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); - builder2.addPicture(ui.Offset.zero, picture1); - builder2.pop(); - builder2.pop(); - builder2.pop(); - - final DomElement contentAfterScale = builder2.build().webOnlyRootElement!; - final DomCanvasElement canvas2 = contentAfterScale.querySelector('canvas')! as DomCanvasElement; - // Although we are drawing same picture, due to scaling the new canvas - // should have more pixels. - expect(canvas2.width! > unscaledWidth, isTrue); - expect(canvas2.height! > unscaledHeight, isTrue); - }); - - test('Should recycle canvas once', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final ui.Picture picture1 = _drawPicture(); - final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( - const ui.Rect.fromLTRB(10, 10, 300, 300), - ); - builder.addPicture(ui.Offset.zero, picture1); - builder.pop(); - builder.build(); - - // Force update to scene which will utilize reuse code path. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - final ui.ClipRectEngineLayer oldLayer2 = builder2.pushClipRect( - const ui.Rect.fromLTRB(5, 10, 300, 300), - oldLayer: oldLayer, - ); - builder2.addPicture(ui.Offset.zero, _drawEmptyPicture()); - builder2.pop(); - - final DomElement contentAfterReuse = builder2.build().webOnlyRootElement!; - expect(contentAfterReuse, isNotNull); - - final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); - builder3.pushClipRect(const ui.Rect.fromLTRB(25, 10, 300, 300), oldLayer: oldLayer2); - builder3.addPicture(ui.Offset.zero, _drawEmptyPicture()); - builder3.pop(); - // This build will crash if canvas gets recycled twice. - final DomElement contentAfterReuse2 = builder3.build().webOnlyRootElement!; - expect(contentAfterReuse2, isNotNull); - }); -} - -typedef TestLayerBuilder = - ui.EngineLayer Function(ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer); -typedef ExpectedHtmlGetter = String Function(); - -void testLayerLifeCycle(TestLayerBuilder layerBuilder, ExpectedHtmlGetter expectedHtmlGetter) { - // Force scene builder to start from scratch. This guarantees that the first - // scene starts from the "build" phase. - SurfaceSceneBuilder.debugForgetFrameScene(); - - // Build: builds a brand new layer. - SurfaceSceneBuilder sceneBuilder = SurfaceSceneBuilder(); - final ui.EngineLayer layer1 = layerBuilder(sceneBuilder, null); - final Type surfaceType = layer1.runtimeType; - sceneBuilder.pop(); - - SceneTester tester = SceneTester(sceneBuilder.build()); - tester.expectSceneHtml(expectedHtmlGetter()); - - PersistedSurface findSurface() { - return enumerateSurfaces().where((PersistedSurface s) => s.runtimeType == surfaceType).single; - } - - final PersistedSurface surface1 = findSurface(); - final DomElement surfaceElement1 = surface1.rootElement!; - - // Retain: reuses a layer as is along with its DOM elements. - sceneBuilder = SurfaceSceneBuilder(); - sceneBuilder.addRetained(layer1); - - tester = SceneTester(sceneBuilder.build()); - tester.expectSceneHtml(expectedHtmlGetter()); - - final PersistedSurface surface2 = findSurface(); - final DomElement surfaceElement2 = surface2.rootElement!; - - expect(surface2, same(surface1)); - expect(surfaceElement2, same(surfaceElement1)); - - // Reuse: reuses a layer's DOM elements by matching it. - sceneBuilder = SurfaceSceneBuilder(); - final ui.EngineLayer layer3 = layerBuilder(sceneBuilder, layer1); - sceneBuilder.pop(); - expect(layer3, isNot(same(layer1))); - tester = SceneTester(sceneBuilder.build()); - tester.expectSceneHtml(expectedHtmlGetter()); - - final PersistedSurface surface3 = findSurface(); - expect(surface3, same(layer3)); - final DomElement surfaceElement3 = surface3.rootElement!; - expect(surface3, isNot(same(surface2))); - expect(surfaceElement3, isNotNull); - expect(surfaceElement3, same(surfaceElement2)); - - // Recycle: discards all the layers. - sceneBuilder = SurfaceSceneBuilder(); - tester = SceneTester(sceneBuilder.build()); - tester.expectSceneHtml(''); - - expect(surface3.rootElement, isNull); // offset3 should be recycled. - - // Retain again: the framework should be able to request that a layer is added - // as retained even after it has been recycled. In this case the - // engine would "rehydrate" the layer with new DOM elements. - sceneBuilder = SurfaceSceneBuilder(); - sceneBuilder.addRetained(layer3); - tester = SceneTester(sceneBuilder.build()); - tester.expectSceneHtml(expectedHtmlGetter()); - expect(surface3.rootElement, isNotNull); // offset3 should be rehydrated. - - // Make sure we clear retained surface list. - expect(retainedSurfaces, isEmpty); -} - -class MockPersistedPicture extends PersistedPicture { - factory MockPersistedPicture() { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - // Use the largest cull rect so that layer clips are effective. The tests - // rely on this. - recorder.beginRecording(ui.Rect.largest).drawPaint(SurfacePaint()); - return MockPersistedPicture._(recorder.endRecording()); - } - - MockPersistedPicture._(EnginePicture picture) : super(0, 0, picture, 0); - - int retainCount = 0; - int buildCount = 0; - int updateCount = 0; - int applyPaintCount = 0; - - final BitmapCanvas _fakeCanvas = BitmapCanvas( - const ui.Rect.fromLTRB(0, 0, 10, 10), - RenderStrategy(), - ); - - @override - EngineCanvas get canvas { - return _fakeCanvas; - } - - @override - double matchForUpdate(PersistedPicture existingSurface) { - return identical(existingSurface.picture, picture) ? 0.0 : 1.0; - } - - @override - Matrix4 get localTransformInverse => Matrix4.identity(); - - @override - void build() { - super.build(); - buildCount++; - } - - @override - void retain() { - super.retain(); - retainCount++; - } - - @override - void applyPaint(EngineCanvas? oldCanvas) { - applyPaintCount++; - } - - @override - void update(PersistedPicture oldSurface) { - super.update(oldSurface); - updateCount++; - } - - @override - int get bitmapPixelCount => 0; -} - -/// Draw 4 circles within 50, 50, 120, 120 bounds -ui.Picture _drawPicture() { - const double offsetX = 50; - const double offsetY = 50; - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); - final ui.Shader gradient = ui.Gradient.radial(const ui.Offset(100, 100), 50, const [ - ui.Color.fromARGB(255, 0, 0, 0), - ui.Color.fromARGB(255, 0, 0, 255), - ]); - canvas.drawCircle( - const ui.Offset(offsetX + 10, offsetY + 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..shader = gradient, - ); - canvas.drawCircle( - const ui.Offset(offsetX + 60, offsetY + 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(255, 0, 0, 1), - ); - canvas.drawCircle( - const ui.Offset(offsetX + 10, offsetY + 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 255, 0, 1), - ); - canvas.drawCircle( - const ui.Offset(offsetX + 60, offsetY + 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 0, 255, 1), - ); - return recorder.endRecording(); -} - -EnginePicture _drawEmptyPicture() { - final EnginePictureRecorder recorder = EnginePictureRecorder(); - recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); - return recorder.endRecording(); -} - -EnginePicture _drawPathImagePath() { - const double offsetX = 50; - const double offsetY = 50; - final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); - final ui.Shader gradient = ui.Gradient.radial(const ui.Offset(100, 100), 50, const [ - ui.Color.fromARGB(255, 0, 0, 0), - ui.Color.fromARGB(255, 0, 0, 255), - ]); - canvas.drawCircle( - const ui.Offset(offsetX + 10, offsetY + 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..shader = gradient, - ); - canvas.drawCircle( - const ui.Offset(offsetX + 60, offsetY + 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(255, 0, 0, 1), - ); - canvas.drawCircle( - const ui.Offset(offsetX + 10, offsetY + 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 255, 0, 1), - ); - canvas.drawImage(createTestImage(), ui.Offset.zero, SurfacePaint()); - canvas.drawCircle( - const ui.Offset(offsetX + 10, offsetY + 10), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..shader = gradient, - ); - canvas.drawCircle( - const ui.Offset(offsetX + 60, offsetY + 60), - 10, - SurfacePaint() - ..style = ui.PaintingStyle.fill - ..color = const ui.Color.fromRGBO(0, 0, 255, 1), - ); - return recorder.endRecording(); -} - -HtmlImage createTestImage({int width = 100, int height = 50}) { - final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); - final DomCanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(33, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(66, 0, 33, 50); - ctx.fill(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} - -class SceneTester { - SceneTester(this.scene); - - final SurfaceScene scene; - - void expectSceneHtml(String expectedHtml) { - expect(scene.webOnlyRootElement, hasHtml(expectedHtml)); - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart deleted file mode 100644 index 83a37e0e13..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart +++ /dev/null @@ -1,133 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine/html/shaders/normalized_gradient.dart'; -import 'package:ui/ui.dart' as ui hide window; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Shader Normalized Gradient', () { - test('3 stop at start', () { - final NormalizedGradient gradient = NormalizedGradient( - const [ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)], - stops: [0.0, 0.5], - ); - int res = _computeColorAt(gradient, 0.0); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.25); - assert(res == 0xFF7f3f1f); - res = _computeColorAt(gradient, 0.5); - assert(res == 0xFFFF7f3f); - res = _computeColorAt(gradient, 0.7); - assert(res == 0xFFFF7f3f); - res = _computeColorAt(gradient, 1.0); - assert(res == 0xFFFF7f3f); - }); - - test('3 stop at end', () { - final NormalizedGradient gradient = NormalizedGradient( - const [ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)], - stops: [0.5, 1.0], - ); - int res = _computeColorAt(gradient, 0.0); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.25); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.5); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.75); - assert(res == 0xFF7f3f1f); - res = _computeColorAt(gradient, 1.0); - assert(res == 0xFFFF7f3f); - }); - - test('4 stop', () { - final NormalizedGradient gradient = NormalizedGradient( - const [ui.Color(0xFF000000), ui.Color(0xFFFF7f3f)], - stops: [0.25, 0.5], - ); - int res = _computeColorAt(gradient, 0.0); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.25); - assert(res == 0xFF000000); - res = _computeColorAt(gradient, 0.4); - assert(res == 0xFF994c25); - res = _computeColorAt(gradient, 0.5); - assert(res == 0xFFFF7f3f); - res = _computeColorAt(gradient, 0.75); - assert(res == 0xFFFF7f3f); - res = _computeColorAt(gradient, 1.0); - assert(res == 0xFFFF7f3f); - }); - - test('5 stop', () { - final NormalizedGradient gradient = NormalizedGradient( - const [ - ui.Color(0x10000000), - ui.Color(0x20FF0000), - ui.Color(0x4000FF00), - ui.Color(0x800000FF), - ui.Color(0xFFFFFFFF), - ], - stops: [0.0, 0.1, 0.2, 0.5, 1.0], - ); - int res = _computeColorAt(gradient, 0.0); - assert(res == 0x10000000); - res = _computeColorAt(gradient, 0.05); - assert(res == 0x187f0000); - res = _computeColorAt(gradient, 0.1); - assert(res == 0x20ff0000); - res = _computeColorAt(gradient, 0.15); - assert(res == 0x307f7f00); - res = _computeColorAt(gradient, 0.2); - assert(res == 0x4000ff00); - res = _computeColorAt(gradient, 0.4); - assert(res == 0x6a0054a9); - res = _computeColorAt(gradient, 0.5); - assert(res == 0x800000fe); - res = _computeColorAt(gradient, 0.9); - assert(res == 0xe5ccccff); - res = _computeColorAt(gradient, 1.0); - assert(res == 0xffffffff); - }); - - test('2 stops at ends', () { - final NormalizedGradient gradient = NormalizedGradient(const [ - ui.Color(0x00000000), - ui.Color(0xFFFFFFFF), - ]); - int res = _computeColorAt(gradient, 0.0); - assert(res == 0); - res = _computeColorAt(gradient, 1.0); - assert(res == 0xFFFFFFFF); - res = _computeColorAt(gradient, 0.5); - assert(res == 0x7f7f7f7f); - }); - }); -} - -int _computeColorAt(NormalizedGradient gradient, double t) { - int i = 0; - while (t > gradient.thresholdAt(i + 1)) { - ++i; - } - final double r = t * gradient.scaleAt(i * 4) + gradient.biasAt(i * 4); - final double g = t * gradient.scaleAt(i * 4 + 1) + gradient.biasAt(i * 4 + 1); - final double b = t * gradient.scaleAt(i * 4 + 2) + gradient.biasAt(i * 4 + 2); - final double a = t * gradient.scaleAt(i * 4 + 3) + gradient.biasAt(i * 4 + 3); - int val = 0; - val |= (a * 0xFF).toInt() & 0xFF; - val <<= 8; - val |= (r * 0xFF).toInt() & 0xFF; - val <<= 8; - val |= (g * 0xFF).toInt() & 0xFF; - val <<= 8; - val |= (b * 0xFF).toInt() & 0xFF; - return val; -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart deleted file mode 100644 index 876ee74169..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart +++ /dev/null @@ -1,217 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; - -import '../../../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - const String mat2Sample = 'mat2(1.1, 2.1, 1.2, 2.2)'; - const String mat3Sample = - 'mat3(1.1, 2.1, 3.1, // first column (not row!)\n' - '1.2, 2.2, 3.2, // second column\n' - '1.3, 2.3, 3.3 // third column\n' - ')'; - const String mat4Sample = - 'mat3(1.1, 2.1, 3.1, 4.1,\n' - '1.2, 2.2, 3.2, 4.2,\n' - '1.3, 2.3, 3.3, 4.3,\n' - '1.4, 2.4, 3.4, 4.4,\n' - ')'; - - setUpAll(() async { - await bootstrapAndRunApp(); - }); - - group('Shader Declarations', () { - test('Constant declaration WebGL1', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); - builder.addConst(ShaderType.kBool, 'false'); - builder.addConst(ShaderType.kInt, '0'); - builder.addConst(ShaderType.kFloat, '1.0'); - builder.addConst(ShaderType.kBVec2, 'bvec2(false, false)'); - builder.addConst(ShaderType.kBVec3, 'bvec3(false, false, true)'); - builder.addConst(ShaderType.kBVec4, 'bvec4(true, true, false, false)'); - builder.addConst(ShaderType.kIVec2, 'ivec2(1, 2)'); - builder.addConst(ShaderType.kIVec3, 'ivec3(1, 2, 3)'); - builder.addConst(ShaderType.kIVec4, 'ivec4(1, 2, 3, 4)'); - builder.addConst(ShaderType.kVec2, 'vec2(1.0, 2.0)'); - builder.addConst(ShaderType.kVec3, 'vec3(1.0, 2.0, 3.0)'); - builder.addConst(ShaderType.kVec4, 'vec4(1.0, 2.0, 3.0, 4.0)'); - builder.addConst(ShaderType.kMat2, mat2Sample); - builder.addConst(ShaderType.kMat2, mat2Sample, name: 'transform1'); - builder.addConst(ShaderType.kMat3, mat3Sample); - builder.addConst(ShaderType.kMat4, mat4Sample); - expect( - builder.build(), - 'const bool c_0 = false;\n' - 'const int c_1 = 0;\n' - 'const float c_2 = 1.0;\n' - 'const bvec2 c_3 = bvec2(false, false);\n' - 'const bvec3 c_4 = bvec3(false, false, true);\n' - 'const bvec4 c_5 = bvec4(true, true, false, false);\n' - 'const ivec2 c_6 = ivec2(1, 2);\n' - 'const ivec3 c_7 = ivec3(1, 2, 3);\n' - 'const ivec4 c_8 = ivec4(1, 2, 3, 4);\n' - 'const vec2 c_9 = vec2(1.0, 2.0);\n' - 'const vec3 c_10 = vec3(1.0, 2.0, 3.0);\n' - 'const vec4 c_11 = vec4(1.0, 2.0, 3.0, 4.0);\n' - 'const mat2 c_12 = $mat2Sample;\n' - 'const mat2 transform1 = $mat2Sample;\n' - 'const mat3 c_13 = $mat3Sample;\n' - 'const mat4 c_14 = $mat4Sample;\n', - ); - }); - - test('Constant declaration WebGL2', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - builder.addConst(ShaderType.kBool, 'false'); - builder.addConst(ShaderType.kInt, '0'); - builder.addConst(ShaderType.kFloat, '1.0'); - builder.addConst(ShaderType.kBVec2, 'bvec2(false, false)'); - builder.addConst(ShaderType.kBVec3, 'bvec3(false, false, true)'); - builder.addConst(ShaderType.kBVec4, 'bvec4(true, true, false, false)'); - builder.addConst(ShaderType.kIVec2, 'ivec2(1, 2)'); - builder.addConst(ShaderType.kIVec3, 'ivec3(1, 2, 3)'); - builder.addConst(ShaderType.kIVec4, 'ivec4(1, 2, 3, 4)'); - builder.addConst(ShaderType.kVec2, 'vec2(1.0, 2.0)'); - builder.addConst(ShaderType.kVec3, 'vec3(1.0, 2.0, 3.0)'); - builder.addConst(ShaderType.kVec4, 'vec4(1.0, 2.0, 3.0, 4.0)'); - builder.addConst(ShaderType.kMat2, mat2Sample); - builder.addConst(ShaderType.kMat2, mat2Sample, name: 'transform2'); - builder.addConst(ShaderType.kMat3, mat3Sample); - builder.addConst(ShaderType.kMat4, mat4Sample); - expect( - builder.build(), - '#version 300 es\n' - 'const bool c_0 = false;\n' - 'const int c_1 = 0;\n' - 'const float c_2 = 1.0;\n' - 'const bvec2 c_3 = bvec2(false, false);\n' - 'const bvec3 c_4 = bvec3(false, false, true);\n' - 'const bvec4 c_5 = bvec4(true, true, false, false);\n' - 'const ivec2 c_6 = ivec2(1, 2);\n' - 'const ivec3 c_7 = ivec3(1, 2, 3);\n' - 'const ivec4 c_8 = ivec4(1, 2, 3, 4);\n' - 'const vec2 c_9 = vec2(1.0, 2.0);\n' - 'const vec3 c_10 = vec3(1.0, 2.0, 3.0);\n' - 'const vec4 c_11 = vec4(1.0, 2.0, 3.0, 4.0);\n' - 'const mat2 c_12 = $mat2Sample;\n' - 'const mat2 transform2 = $mat2Sample;\n' - 'const mat3 c_13 = $mat3Sample;\n' - 'const mat4 c_14 = $mat4Sample;\n', - ); - }); - - test('Attribute declaration WebGL1', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); - builder.addIn(ShaderType.kVec4, name: 'position'); - builder.addIn(ShaderType.kVec4); - expect( - builder.build(), - 'attribute vec4 position;\n' - 'attribute vec4 attr_0;\n', - ); - }); - - test('in declaration WebGL1', () { - final ShaderBuilder builder = ShaderBuilder.fragment(WebGLVersion.webgl1); - builder.addIn(ShaderType.kVec4, name: 'position'); - builder.addIn(ShaderType.kVec4); - expect( - builder.build(), - 'varying vec4 position;\n' - 'varying vec4 attr_0;\n', - ); - }); - - test('Attribute declaration WebGL2', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - builder.addIn(ShaderType.kVec4, name: 'position'); - builder.addIn(ShaderType.kVec4); - expect( - builder.build(), - '#version 300 es\n' - 'in vec4 position;\n' - 'in vec4 attr_0;\n', - ); - }); - - test('Uniform declaration WebGL1', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); - final ShaderDeclaration variable = builder.addUniform(ShaderType.kVec4, name: 'v1'); - expect(variable.name, 'v1'); - expect(variable.dataType, ShaderType.kVec4); - expect(variable.storage, ShaderStorageQualifier.kUniform); - builder.addUniform(ShaderType.kVec4); - expect( - builder.build(), - 'uniform vec4 v1;\n' - 'uniform vec4 uni_0;\n', - ); - }); - - test('Uniform declaration WebGL2', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - final ShaderDeclaration variable = builder.addUniform(ShaderType.kVec4, name: 'v1'); - expect(variable.name, 'v1'); - expect(variable.dataType, ShaderType.kVec4); - expect(variable.storage, ShaderStorageQualifier.kUniform); - builder.addUniform(ShaderType.kVec4); - expect( - builder.build(), - '#version 300 es\n' - 'uniform vec4 v1;\n' - 'uniform vec4 uni_0;\n', - ); - }); - - test('Float precision', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - builder.floatPrecision = ShaderPrecision.kLow; - builder.addUniform(ShaderType.kFloat, name: 'f1'); - expect( - builder.build(), - '#version 300 es\n' - 'precision lowp float;\n' - 'uniform float f1;\n', - ); - }); - - test('Integer precision', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - builder.integerPrecision = ShaderPrecision.kLow; - builder.addUniform(ShaderType.kInt, name: 'i1'); - expect( - builder.build(), - '#version 300 es\n' - 'precision lowp int;\n' - 'uniform int i1;\n', - ); - }); - - test('Method', () { - final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); - builder.floatPrecision = ShaderPrecision.kMedium; - final ShaderDeclaration variable = builder.addUniform(ShaderType.kFloat, name: 'f1'); - final ShaderMethod m = builder.addMethod('main'); - m.addStatement('f1 = 5.0;'); - expect( - builder.build(), - '#version 300 es\n' - 'precision mediump float;\n' - 'uniform float ${variable.name};\n' - 'void main() {\n' - ' f1 = 5.0;\n' - '}\n', - ); - }); - }); -} diff --git a/engine/src/flutter/lib/web_ui/test/html/surface/surface_test.dart b/engine/src/flutter/lib/web_ui/test/html/surface/surface_test.dart deleted file mode 100644 index 2c1656762b..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/surface/surface_test.dart +++ /dev/null @@ -1,502 +0,0 @@ -// 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:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/ui_web/src/ui_web.dart' as ui_web; - -import '../../common/test_initialization.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Surface', () { - setUpAll(() async { - await bootstrapAndRunApp(withImplicitView: true); - }); - - setUp(() { - SurfaceSceneBuilder.debugForgetFrameScene(); - }); - - test('debugAssertSurfaceState produces a human-readable message', () { - final SceneBuilder builder = SceneBuilder(); - final PersistedOpacity opacityLayer = builder.pushOpacity(100) as PersistedOpacity; - try { - debugAssertSurfaceState( - opacityLayer, - PersistedSurfaceState.active, - PersistedSurfaceState.pendingRetention, - ); - fail('Expected $PersistedSurfaceException'); - } on PersistedSurfaceException catch (exception) { - expect( - '$exception', - 'PersistedOpacity: is in an unexpected state.\n' - 'Expected one of: PersistedSurfaceState.active, PersistedSurfaceState.pendingRetention\n' - 'But was: PersistedSurfaceState.created', - ); - } - }); - - test('is created', () { - final SceneBuilder builder = SceneBuilder(); - final PersistedOpacity opacityLayer = builder.pushOpacity(100) as PersistedOpacity; - builder.pop(); - - expect(opacityLayer, isNotNull); - expect(opacityLayer.rootElement, isNull); - expect(opacityLayer.isCreated, isTrue); - - builder.build(); - - expect(opacityLayer.rootElement!.tagName.toLowerCase(), 'flt-opacity'); - expect(opacityLayer.isActive, isTrue); - }); - - test('is released', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; - builder1.pop(); - builder1.build(); - expect(opacityLayer.isActive, isTrue); - - SceneBuilder().build(); - expect(opacityLayer.isReleased, isTrue); - expect(opacityLayer.rootElement, isNull); - }); - - test('discarding is recursive', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; - final PersistedTransform transformLayer = - builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; - builder1.pop(); - builder1.pop(); - builder1.build(); - expect(opacityLayer.isActive, isTrue); - expect(transformLayer.isActive, isTrue); - - SceneBuilder().build(); - expect(opacityLayer.isReleased, isTrue); - expect(transformLayer.isReleased, isTrue); - expect(opacityLayer.rootElement, isNull); - expect(transformLayer.rootElement, isNull); - }); - - test('is updated', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; - builder1.pop(); - builder1.build(); - expect(opacityLayer1.isActive, isTrue); - final DomElement element = opacityLayer1.rootElement!; - - final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity opacityLayer2 = - builder2.pushOpacity(200, oldLayer: opacityLayer1) as PersistedOpacity; - expect(opacityLayer1.isPendingUpdate, isTrue); - expect(opacityLayer2.isCreated, isTrue); - expect(opacityLayer2.oldLayer, same(opacityLayer1)); - builder2.pop(); - - builder2.build(); - expect(opacityLayer1.isReleased, isTrue); - expect(opacityLayer1.rootElement, isNull); - expect(opacityLayer2.isActive, isTrue); - expect(opacityLayer2.rootElement, element); // adopts old surface's element - expect(opacityLayer2.oldLayer, isNull); - }); - - test('ignores released surface when updated', () { - // Build a surface - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; - builder1.pop(); - builder1.build(); - expect(opacityLayer1.isActive, isTrue); - final DomElement element = opacityLayer1.rootElement!; - - // Release it - SceneBuilder().build(); - expect(opacityLayer1.isReleased, isTrue); - expect(opacityLayer1.rootElement, isNull); - - // Attempt to update it - final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity opacityLayer2 = - builder2.pushOpacity(200, oldLayer: opacityLayer1) as PersistedOpacity; - builder2.pop(); - expect(opacityLayer1.isReleased, isTrue); - expect(opacityLayer2.isCreated, isTrue); - - builder2.build(); - expect(opacityLayer1.isReleased, isTrue); - expect(opacityLayer2.isActive, isTrue); - expect(opacityLayer2.rootElement, isNot(equals(element))); - }); - - // This test creates a situation when an intermediate layer disappears, - // causing its child to become a direct child of the common ancestor. This - // often happens with opacity layers. When opacity reaches 1.0, the - // framework removes that layer (as it is no longer necessary). This test - // makes sure we reuse the child layer's DOM nodes. Here's the illustration - // of what's happening: - // - // Frame 1 Frame 2 - // - // A A - // | | - // B ┌──>C - // | │ | - // C ────┘ L - // | - // L - // - // Layer "L" is a logging layer used to track what would happen to the - // child of "C" as it's being dragged around the tree. For example, we - // check that the child doesn't get discarded by mistake. - test( - 'reparents DOM element when updated', - () { - final _LoggingTestSurface logger = _LoggingTestSurface(); - final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); - final PersistedTransform a1 = - builder1.pushTransform( - (Matrix4.identity()..scale(EngineFlutterDisplay.instance.browserDevicePixelRatio)) - .toFloat64(), - ) - as PersistedTransform; - final PersistedOpacity b1 = builder1.pushOpacity(100) as PersistedOpacity; - final PersistedTransform c1 = - builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; - builder1.debugAddSurface(logger); - builder1.pop(); - builder1.pop(); - builder1.pop(); - builder1.build(); - expect(logger.log, ['build', 'createElement', 'apply']); - - final DomElement elementA = a1.rootElement!; - final DomElement elementB = b1.rootElement!; - final DomElement elementC = c1.rootElement!; - - expect(elementC.parent, elementB); - expect(elementB.parent, elementA); - - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - final PersistedTransform a2 = - builder2.pushTransform( - (Matrix4.identity()..scale(EngineFlutterDisplay.instance.browserDevicePixelRatio)) - .toFloat64(), - oldLayer: a1, - ) - as PersistedTransform; - final PersistedTransform c2 = - builder2.pushTransform(Matrix4.identity().toFloat64(), oldLayer: c1) - as PersistedTransform; - builder2.addRetained(logger); - builder2.pop(); - builder2.pop(); - - expect(c1.isPendingUpdate, isTrue); - expect(c2.isCreated, isTrue); - builder2.build(); - expect(logger.log, ['build', 'createElement', 'apply', 'retain']); - expect(c1.isReleased, isTrue); - expect(c2.isActive, isTrue); - - expect(a2.rootElement, elementA); - expect(b1.rootElement, isNull); - expect(c2.rootElement, elementC); - - expect(elementC.parent, elementA); - expect(elementB.parent, null); - }, - // This method failed on iOS Safari. - // TODO(ferhat): https://github.com/flutter/flutter/issues/60036 - skip: - ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && - ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs, - ); - - test('is retained', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; - builder1.pop(); - builder1.build(); - expect(opacityLayer.isActive, isTrue); - final DomElement element = opacityLayer.rootElement!; - - final SceneBuilder builder2 = SceneBuilder(); - - expect(opacityLayer.isActive, isTrue); - builder2.addRetained(opacityLayer); - expect(opacityLayer.isPendingRetention, isTrue); - - builder2.build(); - expect(opacityLayer.isActive, isTrue); - expect(opacityLayer.rootElement, element); - }); - - test('revives released surface when retained', () { - final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; - final _LoggingTestSurface logger = _LoggingTestSurface(); - builder1.debugAddSurface(logger); - builder1.pop(); - builder1.build(); - expect(opacityLayer.isActive, isTrue); - expect(logger.log, ['build', 'createElement', 'apply']); - final DomElement element = opacityLayer.rootElement!; - - SceneBuilder().build(); - expect(opacityLayer.isReleased, isTrue); - expect(opacityLayer.rootElement, isNull); - expect(logger.log, ['build', 'createElement', 'apply', 'discard']); - - final SceneBuilder builder2 = SceneBuilder(); - builder2.addRetained(opacityLayer); - expect(opacityLayer.isCreated, isTrue); // revived - expect(logger.log, ['build', 'createElement', 'apply', 'discard', 'revive']); - - builder2.build(); - expect(opacityLayer.isActive, isTrue); - expect(opacityLayer.rootElement, isNot(equals(element))); - }); - - test('reviving is recursive', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; - final PersistedTransform transformLayer = - builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; - builder1.pop(); - builder1.pop(); - builder1.build(); - expect(opacityLayer.isActive, isTrue); - expect(transformLayer.isActive, isTrue); - final DomElement opacityElement = opacityLayer.rootElement!; - final DomElement transformElement = transformLayer.rootElement!; - - SceneBuilder().build(); - - final SceneBuilder builder2 = SceneBuilder(); - builder2.addRetained(opacityLayer); - expect(opacityLayer.isCreated, isTrue); // revived - expect(transformLayer.isCreated, isTrue); // revived - - builder2.build(); - expect(opacityLayer.isActive, isTrue); - expect(transformLayer.isActive, isTrue); - expect(opacityLayer.rootElement, isNot(equals(opacityElement))); - expect(transformLayer.rootElement, isNot(equals(transformElement))); - }); - - // This test creates a situation when a retained layer is moved to another - // parent. We want to make sure that we move the retained layer's elements - // without rebuilding from scratch. No new elements are created in this - // situation. - // - // Here's an illustrated example where layer C is reparented onto B along - // with D: - // - // Frame 1 Frame 2 - // - // A A - // ╱ ╲ | - // B C ──┐ B - // | │ | - // D └──>C - // | - // D - test('reparents DOM elements when retained', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity a1 = builder1.pushOpacity(10) as PersistedOpacity; - final PersistedOpacity b1 = builder1.pushOpacity(20) as PersistedOpacity; - builder1.pop(); - final PersistedOpacity c1 = builder1.pushOpacity(30) as PersistedOpacity; - final PersistedOpacity d1 = builder1.pushOpacity(40) as PersistedOpacity; - builder1.pop(); - builder1.pop(); - builder1.pop(); - builder1.build(); - - final DomElement elementA = a1.rootElement!; - final DomElement elementB = b1.rootElement!; - final DomElement elementC = c1.rootElement!; - final DomElement elementD = d1.rootElement!; - - expect(elementB.parent, elementA); - expect(elementC.parent, elementA); - expect(elementD.parent, elementC); - - final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity a2 = builder2.pushOpacity(10, oldLayer: a1) as PersistedOpacity; - final PersistedOpacity b2 = builder2.pushOpacity(20, oldLayer: b1) as PersistedOpacity; - builder2.addRetained(c1); - builder2.pop(); - builder2.pop(); - builder2.build(); - - expect(a2.rootElement, elementA); - expect(b2.rootElement, elementB); - expect(c1.rootElement, elementC); - expect(d1.rootElement, elementD); - - expect( - [elementD.parent!, elementC.parent!, elementB.parent!], - [elementC, elementB, elementA], - ); - }); - - test('is updated by matching', () { - final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; - builder1.pop(); - builder1.build(); - expect(opacityLayer1.isActive, isTrue); - final DomElement element = opacityLayer1.rootElement!; - - final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity opacityLayer2 = builder2.pushOpacity(200) as PersistedOpacity; - expect(opacityLayer1.isActive, isTrue); - expect(opacityLayer2.isCreated, isTrue); - builder2.pop(); - - builder2.build(); - expect(opacityLayer1.isReleased, isTrue); - expect(opacityLayer1.rootElement, isNull); - expect(opacityLayer2.isActive, isTrue); - expect(opacityLayer2.rootElement, element); // adopts old surface's element - }); - }); - - final Map layerFactories = { - 'ColorFilterEngineLayer': - (SurfaceSceneBuilder builder) => - builder.pushColorFilter(const ColorFilter.mode(Color(0xFFFF0000), BlendMode.srcIn)), - 'OffsetEngineLayer': (SurfaceSceneBuilder builder) => builder.pushOffset(1, 2), - 'TransformEngineLayer': - (SurfaceSceneBuilder builder) => builder.pushTransform(Matrix4.identity().toFloat64()), - 'ClipRectEngineLayer': - (SurfaceSceneBuilder builder) => builder.pushClipRect(const Rect.fromLTRB(0, 0, 10, 10)), - 'ClipRRectEngineLayer': - (SurfaceSceneBuilder builder) => - builder.pushClipRRect(RRect.fromRectXY(const Rect.fromLTRB(0, 0, 10, 10), 1, 2)), - 'ClipPathEngineLayer': - (SurfaceSceneBuilder builder) => - builder.pushClipPath(Path()..addRect(const Rect.fromLTRB(0, 0, 10, 10))), - 'OpacityEngineLayer': (SurfaceSceneBuilder builder) => builder.pushOpacity(100), - 'ImageFilterEngineLayer': - (SurfaceSceneBuilder builder) => - builder.pushImageFilter(ImageFilter.blur(sigmaX: 0.1, sigmaY: 0.2)), - 'BackdropEngineLayer': - (SurfaceSceneBuilder builder) => - builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 0.1, sigmaY: 0.2)), - // Firefox does not support WebGL in headless mode. - if (!isFirefox) - 'ShaderMaskEngineLayer': (SurfaceSceneBuilder builder) { - const List colors = [Color(0xFF000000), Color(0xFFFF3C38)]; - const List stops = [0.0, 1.0]; - const Rect shaderBounds = Rect.fromLTWH(180, 10, 140, 140); - final EngineGradient shader = GradientLinear( - Offset(200 - shaderBounds.left, 30 - shaderBounds.top), - Offset(320 - shaderBounds.left, 150 - shaderBounds.top), - colors, - stops, - TileMode.clamp, - Matrix4.identity().storage, - ); - return builder.pushShaderMask(shader, shaderBounds, BlendMode.srcOver); - }, - }; - - // Regression test for https://github.com/flutter/flutter/issues/104305 - for (final MapEntry layerFactory in layerFactories.entries) { - test('${layerFactory.key} supports addRetained after being discarded', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(0, 0); - final PersistedSurface oldLayer = layerFactory.value(builder) as PersistedSurface; - builder.pop(); - builder.pop(); - builder.build(); - expect(oldLayer.isActive, isTrue); - - // Pump an empty frame so the `oldLayer` is discarded before it's reused. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.build(); - expect(oldLayer.isReleased, isTrue); - - // At this point the `oldLayer` needs to be revived. - final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); - builder3.addRetained(oldLayer); - builder3.build(); - expect(oldLayer.isActive, isTrue); - }); - } -} - -typedef TestEngineLayerFactory = EngineLayer Function(SurfaceSceneBuilder builder); - -class _LoggingTestSurface extends PersistedContainerSurface { - _LoggingTestSurface() : super(null); - - final List log = []; - - @override - void build() { - log.add('build'); - super.build(); - } - - @override - void apply() { - log.add('apply'); - } - - @override - DomElement createElement() { - log.add('createElement'); - return createDomElement('flt-test-layer'); - } - - @override - void update(_LoggingTestSurface oldSurface) { - log.add('update'); - super.update(oldSurface); - } - - @override - void adoptElements(covariant PersistedSurface oldSurface) { - log.add('adoptElements'); - super.adoptElements(oldSurface); - } - - @override - void retain() { - log.add('retain'); - super.retain(); - } - - @override - void discard() { - log.add('discard'); - super.discard(); - } - - @override - void revive() { - log.add('revive'); - super.revive(); - } - - @override - double matchForUpdate(PersistedSurface? existingSurface) { - return 1.0; - } -} diff --git a/engine/src/flutter/lib/web_ui/test/html/testimage.dart b/engine/src/flutter/lib/web_ui/test/html/testimage.dart deleted file mode 100644 index e1ec4d41fa..0000000000 --- a/engine/src/flutter/lib/web_ui/test/html/testimage.dart +++ /dev/null @@ -1,122 +0,0 @@ -// 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/src/engine.dart'; - -/// 50x50 pixel flutter logo image that contains alpha ramps and colors -/// specifically to transparency and blending. -const String _flutterLogoBase64 = - 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAKRlWElm' - 'TU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgExAAIAAAAg' - 'AAAAWodpAAQAAAABAAAAegAAAAAAAABIAAAAAQAAAEgAAAABQWRvYmUgUGhvdG9zaG9wIENT' - 'NiAoTWFjaW50b3NoKQAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMqADAAQAAAABAAAAMgAA' - 'AABWBXsWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEemlUWHRYTUw6Y29tLmFkb2JlLnhtcAAA' - 'AAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENv' - 'cmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5' - 'OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjph' - 'Ym91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94' - 'YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5j' - 'b20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0i' - 'aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0i' - 'aHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFu' - 'Y2VJRD54bXAuaWlkOjMyOERERjc5ODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTp' - 'JbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMyOERERj' - 'dBODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgI' - 'CA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnhtcC5kaWQ6MDE4MDExNzQwNzIwNjgxMTgy' - 'MkFBQjU0OEFBMDMwM0E8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHht' - 'cE1NOkRlcml2ZWRGcm9tIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICA' - 'gPHN0UmVmOmluc3RhbmNlSUQ+eG1wLmlpZDowNDgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QU' - 'EwMzAzQTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SU' - 'Q+eG1wLmRpZDowMTgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QUEwMzAzQTwvc3RSZWY6ZG9jd' - 'W1lbnRJRD4KICAgICAgICAgPC94bXBNTTpEZXJpdmVkRnJvbT4KICAgICAgICAgPHhtcDpD' - 'cmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3JlYXRv' - 'clRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb2' - '4+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhP' - 'gr/+ApQAAAQNUlEQVRoBbVaDYxdRRWemTv3vt2WbrctpRYxtCVU4io1LomElrjYWnbbgtr2' - 'CbISwGpR0UBUjD8YHyZCNCqIP5GmtLWSAn3SVYqw7PbnFUQFXGOMRUFlW9G2gKalu/veu38' - 'zfufce3ff275dWtBJ5s7cuTPnnO+cM2d+3pN2urDiVSHEVCFuOpgT35vVIuaLV8Tgtw8Icf' - 'PZkZgntDggrhNCbhHCuihD9J402Y4OLUulqNy1+iwvFIscaaqRMWqyQRofIxFH2ohpwqjDc' - 't/OZyyYSTAVHQUtSoVIfPiL7xWu3i2i0KA7PnFmstpaEVMLBnHm1rFHhEbiQZ9PKtl83pPF' - 'YnB0xVVne2HlKcdz5wgTC62cZLwlGdJUSxXtWnngFItIOhdzj3xeiWIxzrpPVmpAZg4EJjc' - 'tAcmqazqNxjnJVwEOnKjDhMmuX+/KDRuCoeVXn9EcDf/K0c4cEwYBBmgBjSUJZVbnJiuh9' - '1BZ4ZnYREpNucgtPfAMW7VYjHjM7Ldlg1MaJxYaxlnNzVCUP4THrLRTdRiVWVaEcQ54fpu2' - 'JoTTl9rid+3tBCL8a1d3S1M0/ARAnGWiyEcfjK+xaB0Icg0ZQXGekEoo4V0qdwNEatVR+q8' - '8O6kCqZ+Wx0QPD6jgeTop72Xxd26Yx0/xYlJAFmJa4xdZO74kcwIgPpObF//rce3qhSYMAw' - 'zIpaqsEYSqaE0Kcmso04F/q/fr/uKeE0AQm5OwCCwqvCzn90MzMHEbsijsJ4f1RBuysFCa' - 'TGUaA0C1bGKjKufFh/Zo111kopAsQXQnTABAvu9IR6OiunX/jocagpiQQv0HDYJkhiS1Jc' - 'V+Lupe0g71BRg7mNjsbmHnml7t6IswJ3wog9xpggR4Uhh4mFTapTjwCb17xzbblgSJCQa9' - 'ZvOkIXGy0SkIjihh5+odcKfl6cSeBAQoSrinhYm1qwDi887uHXdbml/7i2MKnYzxBN9eFx' - 'ArCgqWYBDBpWu2wp0+aAKOTll0m4AdQbBGuZ4DELcCxHfINcXAwIRBhAmdxGQ/ZSAEQogC' - '0w861/7Q9dyrGQTNqwkTzxaAEBFAaBOa7zq7dhTIqqJUogk/2XQ6uck+Ie8GH9i7O0oKjI3' - 'ftfabAPEpE/i00mPFz1IDmZKmUHmua6J4g7O753NMq9hGobDBADRaK7NcaJuNbkLMoaV5gn' - 'RqFunocGjr4XeuvcXT7heMXyWXIPLMiHk0FEsEsIQXh/F9zq6fX8/9CgWsxYWG4Zy/1zzmz' - 'n2e6U9VpMPGacIP47vbdqzaAxvCYEX+RtfRd2Jix8IaGq/qdJoteCkBdPCldnNxFO9EiL2' - 'cmmsDRdrtJIv3X+Rdc/6TQRiQAoj3qPyTGGuMNhhj/7QhCDrz61zlEAjonbwCIGrTOBD4xC' - 'BsFO3R/T2vCQKupCSiGkpsYcU25NORg7Q9eu6Fg7POu+lOMWN6kxyB65EUWXpNIAmIYlBen' - 'r/CddRGG4UEAiHUJrvAGmIZ0bQMEGJhiehpZ/Gi94n+nmTxzPZP4zqnr5mGSa7lyE3UDnB' - 'UiJmtrUJUA+u2NktaySEHPTjVazRrTUvb1ZWjnWz10vzKZi3vp9ULoacxiHpr+ADhYa/1p' - '8PD8zpkoWDYNTG/xrEY/5pJRuWx9GNoDBhjl18ul7EJULzFoBBYmyYEwpZ49FG/0pV/T07Z' - 'h0l+YwlJaolaKmk9VWegHJ1DdHpBTZ1+8Vt+c0eFN5SYXw2GTNREpNJ9P0qJzVgSVFDWA8g' - 'INASSudPIijUXuNbsojkFrYR1IGrppdZAE0A4HhR4WLkti+XPtxwjWhcMDJwKiEy2sTLlVc' - 'uyZp5zvxPmSAbC78q/TRtbgiV1DEcHYneU0GgFNACC1IemUCkFEOaoEqctlo9sOZLRGpPo/' - '1ers0jG+Njy/HzHmCeUElPh5yE6aZadHg1B4GCklItwU45k08Wy76eDGa3/jei1TDOK9W2' - 'jQHgyYmIPL/3wnGkqfsJx5EwTxwECRmKJ+nFsCSKJ5gjLFIGIY6kvyT123/4sSOCjPJVcEi' - 'WW55g4lk63TOjXLnlgttgd7fhAa5OuPgk/fzOBwHCP3b8WBDWkcwLfAULCPSWCSW6Z91jxab' - 'YEgkTBpmvMKUC5RF7CUa1VtNJ5pkGqFaT+s04ORhvCwY5rm07zjpccxznHhFEV3Wg7bngC0J' - 'h6GqQxCok43SkRW3mZ7r2/lFoCIAqqIJPtx3K7fOp0QWfoydM/8Xn1S6vVzXNu9ntF75xO0Z' - 'mNsbRI0mgc7vlJ9WSyjwnF1zbUfJZ3fJfWDk53URmvCQjqjmU1nc/ULUkSNzfQuMLNSGzVlb' - 'qv+HA6J/y8zTsAwVv8lfHSexGx3zsim4fQPwunqI4JwHXI1wIuj5/RL5eY86f+Otj31c6mTr' - 'o0mIOcbUcgDAHBZjIZnxLhHYbQe3EeWOLO6NeOWgwQw+iTA3AIkvQb8yLGQJToYAQUjo6ts0' - '73bX8guz1JLcEgVkXLtjrK6Q5tSCvz3FHLMpnkQQIlWiZvBWPpisA/duvXf3v7FtEhvp52' - 'HQPCwmRWyZRB73CNDpwH7PLV/8FqBxFZ97Ryk6gpnaxg5CSkxRyaYoy4ESA28ekOtyckU0E' - 'UmPqqeOkPYK6rQxMeBxtsKhOmia8k9XFWiRylWlUgbnu6+R8F0GoBH+qe5URaFiUJ91yted' - 'C+2Kq+HWutMT3KUdNgPbgW+SSW8vpMExFhlkDILzt9D97F84sWu4S2gnrtqnjZ7VKpGyIT0f' - '2lQxrJMt5JO2nmCw36FmuAcEJz1/bcL7+C79ibHKM+pLTaDFWTTKPg6uoKvs2+q/p7VsMQvT' - 'D1DHSukHsBJM0FIgYQ0ldStUTWfMPp+9ntPA7WJBAdogPbCBGvsku/BDG/iHu2oxhDWiRmpI' - 'AYQoAO5awuqa3iKDFdhXbjTm/fjeDPboCoReNGQaA9fZdp2xgtsGH6fPbmczNGymDRZRhUQn' - 'UmBKxAWxlBHxxmGmPvcPt6biENi/R0RyBKshQttUtuALvbsB/7NyihyygI0ABjChDImKoRWQ' - 'FtFSy4s5zAbtvplT6O/mJADIwGBLyOAsEcI2GjRLGkAIl60gY2JCPtPMAWu9IkDBewMMcrMe' - 'A3YNQK34ab2Qosejri8I+dXT2fpf50ZqfTXbttdwnEvOicaw6Yl+4oi+rLjlUkOBNHRxKYB' - 'E8si0WT2iAERcZZOrA7dub2daMu8ohq7aKdvlMiRVCdAWCK8ThugyIQSfGe0IL0iUXwkRODo' - 'WuZnTvL0puyCjr9Iz7QAacVN4Fbnf6eT1JHW8ANSgpiQA6EC6IFq+FyP8BN+n9eEEeiEVnF' - 'tbuC8BYMUhAQiN6BLgEhzUzHyN6HcvvWEk0K2UWOlPQ2mjIgMeYtA8KXpMzeUyuTpdki2VC' - 'Jicur/C+3HcWEXQlBDkN7jwDEtdSH5gWdLcgSBGK+Pfd9WBDvRph71REKBzZrB+3hqCICEF' - 'YQnMAkIFDSnCvD3VsB4okFB5rX4N122A5dlA1v3OuFz0DAAuRaRBd2G811QPBR0Lmc3ayv5' - '2VldCe2UeuonUHgLqvNtnkE4hx7zkUw8T3wAdwa2ypOKwYzPoyFiV4Qh7A1qAHDmpMj6DcN' - 'IH4/52/hmu+f+6hPIMg1iX6DlAEJMW+5DhdDCdciS3Od3pN8wjaeCLKbJdehB+md3alQiF' - 'NLBPPtwkU4c2zGygJCxoeGcPUqcQam5VuJEPP8gDgsFoi52MNoeKmhfdt0x8q/tLwi8pvO' - 'e3IonV+TnVPoG+UIkx3GQ+IYQnakOjKVaWoIhL7RTSKH2DbcPcGdUu2FC+yCc42JtkB4zB' - 'U7hJ4uaAa8XIIwgdEAU4EMg+KInC/mRtq6LdLEL055VV1535l7/r0errkBVs2EaFCSiKTtD' - 'AwDgbK4KzZfKYgxJMmXBpTqmmgni0PiPDvvPBk7GyH0TAwsI5TiB5w6xWAY9I8nxVEf7jZN' - 'TvEWSOdwa1V//P7mPxxotwLziwWsY0EvtI5A+dhfWPqVaSfybGS6RSGjyAP/PCTmf+w2MfuM' - 'aeI4lkefGKXphDmSfRhXMmAbOXOxZM2AiY+DOKYDbvKtCGBxMGOGXNJ7bGTgKeWPhH82T93' - 'bdYRAEM0Bub2G/Tgu9a9kDXLJzCqoS3oHD4nfXlCX+J7mkwNCoRHuf9D9+14o7CaAyAEQX' - 'Sv5VCLeBtiIMBPYLmHoaNyiDcaV4mePlje8Y9G06++5O5HzQ7HIb69d+JLm+if5DB0lKFe' - 'w5HHJ75JAMEDwIZ5JPjkgoADksdgr9KAc7MeK/DVw8gDCxx4NHgTCsAATNSCsXJwsnw/K2' - 'z8TlO98Z6wXRkfiYdXe3L35W0RKFAEG6xHXGz/IA+goQfdaTeBH9WY+xBnrsxUYAPFMrDQ' - 'ZMYwdly7BBETIBJiHsFP+NgRvggWqWMwpI3oBmMj5Vj7nl39xvV/5xrt85/zAt1W4obWHs' - 'OAvabpq0y1MFQGkARiyBCVypz3IjyD3wgMeQ/nwSLn6lMi5EWIvpocEIALFwHz2fXQ6+YR' - 'whc40+eOzo7OvwvRchw0JXaZBKTn8RvycrfReJ6ufXizkhcmSaHCKhPWgPEVXSrOMUL/wt1' - '33vYQpxTuOq8nrpM8FC5u683dh0SqDP8kxKv+pWYSY0BmcNoSIZAf1wW04Nf0E95dNiMJlg' - 'KgGu7v94NoLq84FcdUGsIVUsFbq7zguIigcQgC8tKl780eJXJI4eGcvXMIKKst79+7lZaJ9f' - 'Xeuis0sZKAzPeaNHc2nDoTYJGBoyZD/kINbrWl60Kq/5oJf5YcrH1tStctUNdROJdJuFf' - 'eSVei7CgCcMZbc5hDm1grvqs15tkbh1jrtMgtEFQrFlH/0o1fY5Q5VeIWniZ9k0ISpOb8' - '+IMyJLUM18aJ+blP49BW9w99Z5oUXNg+FWlWMlVV4DJUVSAMtphlC4NcICptHsIG9PNd9T' - 'xfmCs2XE8AwcTyKbc8ykMMWgZAOfpJ3z2QZZMP59QMhLmSZNPq89O4HNsYt7uPWiTUCJB1x' - 'y7AENomU+VcA/BKQvBNAjIQmASY2l+srN1+cgDnRxTIwSakoaJQ5SzpiEKAkvzEgRL0m+lS' - '3fnQjzn/PQEsKAQCLphgBGGQLUJSTd2onUABCIF9WNrrMvWLTu0QBR+zJwjJub2ENuiAhA' - 'NiEZtmOvHEg48Hcv24z/ABnGUnniFeh46HxGYCGpDVDCDzD+M0Ll14KkyDuyn3knrfWKia' - 'xQs2TNtcYC08YwrpFgOiaCVmB1v8ykTbZQnu19/zgSmXs6QjKFHKZD52U6pIBJPoGc+G6i' - 'koVGq9PFK/5FyISxuA7p7R+7c1vEqGzAusRBQwk2j0mabSSNbzhMgPTsVe7Zx54O85TYz9b' - 'G7q0QYqx43NQp5J+DyaxgBqC4d9IEn9j8Z8VxRvogo76E5ik7C7gmmjkHXjFDz64oKFxDs5' - 'rMQ4IqP4fUo0219/tijMXppoFq1LKbmHySy2/PY/v9H50hhEz8KvE0RkS2xhsP8YlvvGZ3S' - 'yapiT0mk/DqjIsBcr/Atffr8/hCjApAAAAAElFTkSuQmCC'; - -HtmlImage createFlutterLogoTestImage() { - return HtmlImage( - createDomHTMLImageElement()..src = 'data:text/plain;base64,$_flutterLogoBase64', - 50, - 50, - ); -} diff --git a/engine/src/flutter/lib/web_ui/test/ui/canvas_test.dart b/engine/src/flutter/lib/web_ui/test/ui/canvas_test.dart index 15059fff38..b4434ae8cd 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/canvas_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/canvas_test.dart @@ -20,11 +20,6 @@ void main() { Future testMain() async { setUpUnitTests(setUpTestViewDimensions: false); - final bool deviceClipRoundsOut = renderer is! HtmlRenderer; - runCanvasTests(deviceClipRoundsOut: deviceClipRoundsOut); -} - -void runCanvasTests({required bool deviceClipRoundsOut}) { setUp(() { EngineSemantics.debugResetSemantics(); }); @@ -150,23 +145,22 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100)); const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26); - final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds; canvas.clipRect(clipRawBounds); // Save initial return values for testing restored values final ui.Rect initialLocalBounds = canvas.getLocalClipBounds(); final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds(); rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.save(); canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds); - rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsNotClose(canvas.getDestinationClipBounds(), clipExpandedBounds); // Previous return values have not changed rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -178,7 +172,7 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13); rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds); // Destination bounds are unaffected by transform - rectsClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsClose(canvas.getDestinationClipBounds(), clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -191,7 +185,6 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100)); const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26); - final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds; final ui.RRect clip = ui.RRect.fromRectAndRadius(clipRawBounds, const ui.Radius.circular(3)); canvas.clipRRect(clip); @@ -199,16 +192,16 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Rect initialLocalBounds = canvas.getLocalClipBounds(); final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds(); rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.save(); canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds); - rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsNotClose(canvas.getDestinationClipBounds(), clipExpandedBounds); // Previous return values have not changed rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -220,7 +213,7 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13); rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds); // Destination bounds are unaffected by transform - rectsClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsClose(canvas.getDestinationClipBounds(), clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -233,7 +226,6 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100)); const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26); - final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds; final ui.Path clip = ui.Path() ..addRect(clipRawBounds) @@ -244,16 +236,16 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Rect initialLocalBounds = canvas.getLocalClipBounds(); final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds(); rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.save(); canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds); - rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsNotClose(canvas.getDestinationClipBounds(), clipExpandedBounds); // Previous return values have not changed rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -265,7 +257,7 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13); rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds); // Destination bounds are unaffected by transform - rectsClose(canvas.getDestinationClipBounds(), clipDestBounds); + rectsClose(canvas.getDestinationClipBounds(), clipExpandedBounds); canvas.restore(); // save/restore returned the values to their original values @@ -278,14 +270,13 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100)); const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26); - final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds; canvas.clipRect(clipRawBounds); // Save initial return values for testing restored values final ui.Rect initialLocalBounds = canvas.getLocalClipBounds(); final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds(); rectsClose(initialLocalBounds, clipExpandedBounds); - rectsClose(initialDestinationBounds, clipDestBounds); + rectsClose(initialDestinationBounds, clipExpandedBounds); canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15), clipOp: ui.ClipOp.difference); expect(canvas.getLocalClipBounds(), initialLocalBounds); diff --git a/engine/src/flutter/lib/web_ui/test/ui/draw_atlas_golden_test.dart b/engine/src/flutter/lib/web_ui/test/ui/draw_atlas_golden_test.dart index cdcc05e177..de84f98f35 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/draw_atlas_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/draw_atlas_golden_test.dart @@ -123,7 +123,7 @@ Future testMain() async { canvas.drawImage(atlas, ui.Offset.zero, ui.Paint()); await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_atlas.png', region: region); - }, skip: isHtml); // HTML renderer doesn't support drawAtlas + }); test('drawAtlas', () async { final ui.PictureRecorder recorder = ui.PictureRecorder(); @@ -160,7 +160,7 @@ Future testMain() async { await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_draw_atlas.png', region: region); - }, skip: isHtml); // HTML renderer doesn't support drawAtlas + }); test('drawAtlasRaw', () async { final ui.PictureRecorder recorder = ui.PictureRecorder(); @@ -209,5 +209,5 @@ Future testMain() async { await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_draw_atlas_raw.png', region: region); - }, skip: isHtml); // HTML renderer doesn't support drawAtlas + }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/fallback_fonts_golden_test.dart b/engine/src/flutter/lib/web_ui/test/ui/fallback_fonts_golden_test.dart index 2cf002c37d..022ee0f3ee 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/fallback_fonts_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/fallback_fonts_golden_test.dart @@ -22,608 +22,589 @@ void main() { const ui.Rect kDefaultRegion = ui.Rect.fromLTRB(0, 0, 100, 100); void testMain() { - group( - 'Font fallbacks', - () { - setUpUnitTests( - withImplicitView: true, - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, + group('Font fallbacks', () { + setUpUnitTests( + withImplicitView: true, + emulateTesterEnvironment: false, + setUpTestViewDimensions: false, + ); + + setUp(() { + debugDisableFontFallbacks = false; + }); + + /// Used to save and restore [ui.PlatformDispatcher.onPlatformMessage] after each test. + ui.PlatformMessageCallback? savedCallback; + + final List downloadedFontFamilies = []; + + setUp(() { + renderer.fontCollection.debugResetFallbackFonts(); + debugOverrideJsConfiguration( + {'fontFallbackBaseUrl': 'assets/fallback_fonts/'}.jsify() + as JsFlutterConfiguration?, + ); + renderer.fontCollection.fontFallbackManager!.downloadQueue.debugOnLoadFontFamily = + (String family) => downloadedFontFamilies.add(family); + savedCallback = ui.PlatformDispatcher.instance.onPlatformMessage; + }); + + tearDown(() { + downloadedFontFamilies.clear(); + ui.PlatformDispatcher.instance.onPlatformMessage = savedCallback; + }); + + test('Roboto is always a fallback font', () { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, contains('Roboto')); + }); + + test('can override font fallback base URL using JS', () { + expect( + renderer.fontCollection.fontFallbackManager!.downloadQueue.fallbackFontUrlPrefix, + 'assets/fallback_fonts/', + ); + debugOverrideJsConfiguration( + {'fontFallbackBaseUrl': 'http://my-special-fonts.com/'}.jsify() + as JsFlutterConfiguration?, ); - setUp(() { - debugDisableFontFallbacks = false; - }); + expect( + renderer.fontCollection.fontFallbackManager!.downloadQueue.fallbackFontUrlPrefix, + 'http://my-special-fonts.com/', + ); + }); - /// Used to save and restore [ui.PlatformDispatcher.onPlatformMessage] after each test. - ui.PlatformMessageCallback? savedCallback; + test('will download Noto Sans Arabic if Arabic text is added', () async { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, ['Roboto']); - final List downloadedFontFamilies = []; + // Creating this paragraph should cause us to start to download the + // fallback font. + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('مرحبا'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - setUp(() { - renderer.fontCollection.debugResetFallbackFonts(); - debugOverrideJsConfiguration( - {'fontFallbackBaseUrl': 'assets/fallback_fonts/'}.jsify() - as JsFlutterConfiguration?, - ); - renderer.fontCollection.fontFallbackManager!.downloadQueue.debugOnLoadFontFamily = - (String family) => downloadedFontFamilies.add(family); - savedCallback = ui.PlatformDispatcher.instance.onPlatformMessage; - }); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - tearDown(() { - downloadedFontFamilies.clear(); - ui.PlatformDispatcher.instance.onPlatformMessage = savedCallback; - }); + expect( + renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, + contains('Noto Sans Arabic'), + ); - test('Roboto is always a fallback font', () { - expect( - renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - contains('Roboto'), - ); - }); + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); - test('can override font fallback base URL using JS', () { - expect( - renderer.fontCollection.fontFallbackManager!.downloadQueue.fallbackFontUrlPrefix, - 'assets/fallback_fonts/', - ); - debugOverrideJsConfiguration( - {'fontFallbackBaseUrl': 'http://my-special-fonts.com/'}.jsify() - as JsFlutterConfiguration?, - ); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.pushStyle(ui.TextStyle(fontSize: 32)); + pb.addText('مرحبا'); + pb.pop(); + final ui.Paragraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); - expect( - renderer.fontCollection.fontFallbackManager!.downloadQueue.fallbackFontUrlPrefix, - 'http://my-special-fonts.com/', - ); - }); + canvas.drawParagraph(paragraph, ui.Offset.zero); + await drawPictureUsingCurrentRenderer(recorder.endRecording()); - test('will download Noto Sans Arabic if Arabic text is added', () async { - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - ]); + await matchGoldenFile('ui_font_fallback_arabic.png', region: kDefaultRegion); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }); - // Creating this paragraph should cause us to start to download the - // fallback font. - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('مرحبا'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + test('will put the Noto Color Emoji font before other fallback fonts in the list', () async { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, ['Roboto']); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + // Creating this paragraph should cause us to start to download the + // Arabic fallback font. + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('مرحبا'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - expect( - renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - contains('Noto Sans Arabic'), - ); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas(recorder); + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + 'Noto Sans Arabic', + ]); - pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.pushStyle(ui.TextStyle(fontSize: 32)); - pb.addText('مرحبا'); - pb.pop(); - final ui.Paragraph paragraph = pb.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.pushStyle(ui.TextStyle(fontSize: 26)); + pb.addText('Hello 😊 مرحبا'); + pb.pop(); + final ui.Paragraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); - canvas.drawParagraph(paragraph, ui.Offset.zero); - await drawPictureUsingCurrentRenderer(recorder.endRecording()); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - await matchGoldenFile('ui_font_fallback_arabic.png', region: kDefaultRegion); - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 - }); + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + 'Noto Color Emoji 9', + 'Noto Sans Arabic', + ]); + }); - test('will put the Noto Color Emoji font before other fallback fonts in the list', () async { - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - ]); + test('will download Noto Color Emojis and Noto Symbols if no matching Noto Font', () async { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, ['Roboto']); - // Creating this paragraph should cause us to start to download the - // Arabic fallback font. - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('مرحبا'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + // Creating this paragraph should cause us to start to download the + // fallback font. + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('Hello 😊'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - 'Noto Sans Arabic', - ]); + expect( + renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, + contains('Noto Color Emoji 9'), + ); - pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.pushStyle(ui.TextStyle(fontSize: 26)); - pb.addText('Hello 😊 مرحبا'); - pb.pop(); - final ui.Paragraph paragraph = pb.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.pushStyle(ui.TextStyle(fontSize: 26)); + pb.addText('Hello 😊'); + pb.pop(); + final ui.Paragraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - 'Noto Color Emoji 9', - 'Noto Sans Arabic', - ]); - }); + canvas.drawParagraph(paragraph, ui.Offset.zero); + await drawPictureUsingCurrentRenderer(recorder.endRecording()); - test('will download Noto Color Emojis and Noto Symbols if no matching Noto Font', () async { - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - ]); + await matchGoldenFile('ui_font_fallback_emoji.png', region: kDefaultRegion); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }); - // Creating this paragraph should cause us to start to download the - // fallback font. - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('Hello 😊'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + /// Attempts to render [text] and verifies that [expectedFamilies] are downloaded. + /// + /// Then it does the same, but asserts that the families aren't downloaded again + /// (because they already exist in memory). + Future checkDownloadedFamiliesForString( + String text, + List expectedFamilies, + ) async { + // Try rendering text that requires fallback fonts, initially before the fonts are loaded. + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText(text); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + expect(downloadedFontFamilies, expectedFamilies); - expect( - renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - contains('Noto Color Emoji 9'), - ); + // Do the same thing but this time with loaded fonts. + downloadedFontFamilies.clear(); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText(text); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas(recorder); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + expect(downloadedFontFamilies, isEmpty); + } - pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.pushStyle(ui.TextStyle(fontSize: 26)); - pb.addText('Hello 😊'); - pb.pop(); - final ui.Paragraph paragraph = pb.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + /// Asserts that a given [partialFontFamilyName] is downloaded to render + /// a given [charCode]. + /// + /// The match on [partialFontFamilyName] is "starts with", so this method + /// supports split fonts, without hardcoding the shard number (which we + /// don't own). + Future checkDownloadedFamilyForCharCode( + int charCode, + String partialFontFamilyName, { + String? userPreferredLanguage, + }) async { + // downloadedFontFamilies.clear(); + // renderer.fontCollection.debugResetFallbackFonts(); - canvas.drawParagraph(paragraph, ui.Offset.zero); - await drawPictureUsingCurrentRenderer(recorder.endRecording()); - - await matchGoldenFile('ui_font_fallback_emoji.png', region: kDefaultRegion); - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 - }); - - /// Attempts to render [text] and verifies that [expectedFamilies] are downloaded. - /// - /// Then it does the same, but asserts that the families aren't downloaded again - /// (because they already exist in memory). - Future checkDownloadedFamiliesForString( - String text, - List expectedFamilies, - ) async { - // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText(text); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect(downloadedFontFamilies, expectedFamilies); - - // Do the same thing but this time with loaded fonts. - downloadedFontFamilies.clear(); - pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText(text); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect(downloadedFontFamilies, isEmpty); + final fallbackManager = renderer.fontCollection.fontFallbackManager!; + final oldLanguage = fallbackManager.debugUserPreferredLanguage; + if (userPreferredLanguage != null) { + fallbackManager.debugUserPreferredLanguage = userPreferredLanguage; } - /// Asserts that a given [partialFontFamilyName] is downloaded to render - /// a given [charCode]. - /// - /// The match on [partialFontFamilyName] is "starts with", so this method - /// supports split fonts, without hardcoding the shard number (which we - /// don't own). - Future checkDownloadedFamilyForCharCode( - int charCode, - String partialFontFamilyName, { - String? userPreferredLanguage, - }) async { - // downloadedFontFamilies.clear(); - // renderer.fontCollection.debugResetFallbackFonts(); + // Try rendering text that requires fallback fonts, initially before the fonts are loaded. + final ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText(String.fromCharCode(charCode)); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - final fallbackManager = renderer.fontCollection.fontFallbackManager!; - final oldLanguage = fallbackManager.debugUserPreferredLanguage; - if (userPreferredLanguage != null) { - fallbackManager.debugUserPreferredLanguage = userPreferredLanguage; - } - - // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - final ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText(String.fromCharCode(charCode)); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); - - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - if (userPreferredLanguage != null) { - fallbackManager.debugUserPreferredLanguage = oldLanguage; - } - - expect( - downloadedFontFamilies, - hasLength(1), - reason: - 'Downloaded more than one font family for character: 0x${charCode.toRadixString(16)}' - '${userPreferredLanguage == null ? '' : ' (userPreferredLanguage: $userPreferredLanguage)'}', - ); - expect(downloadedFontFamilies.first, startsWith(partialFontFamilyName)); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + if (userPreferredLanguage != null) { + fallbackManager.debugUserPreferredLanguage = oldLanguage; } - // Regression test for https://github.com/flutter/flutter/issues/75836 - // When we had this bug our font fallback resolution logic would end up in an - // infinite loop and this test would freeze and time out. - test('can find fonts for two adjacent unmatched code points from different fonts', () async { - await checkDownloadedFamiliesForString('ヽಠ', [ - 'Noto Sans SC 68', - 'Noto Sans Kannada', - ]); - }); + expect( + downloadedFontFamilies, + hasLength(1), + reason: + 'Downloaded more than one font family for character: 0x${charCode.toRadixString(16)}' + '${userPreferredLanguage == null ? '' : ' (userPreferredLanguage: $userPreferredLanguage)'}', + ); + expect(downloadedFontFamilies.first, startsWith(partialFontFamilyName)); + } - test('can find glyph for 2/3 symbol', () async { - await checkDownloadedFamiliesForString('⅔', ['Noto Sans']); - }); + // Regression test for https://github.com/flutter/flutter/issues/75836 + // When we had this bug our font fallback resolution logic would end up in an + // infinite loop and this test would freeze and time out. + test('can find fonts for two adjacent unmatched code points from different fonts', () async { + await checkDownloadedFamiliesForString('ヽಠ', [ + 'Noto Sans SC 68', + 'Noto Sans Kannada', + ]); + }); - // https://github.com/flutter/devtools/issues/6149 - test('can find glyph for treble clef', () async { - await checkDownloadedFamiliesForString('𝄞', ['Noto Music']); - }); + test('can find glyph for 2/3 symbol', () async { + await checkDownloadedFamiliesForString('⅔', ['Noto Sans']); + }); - // https://github.com/flutter/flutter/issues/148797 - test('can find Tibetan script glyphs (Dzongkha)', () async { - await checkDownloadedFamiliesForString('འཛམ་གླིང་སྤྱི་ཚོགས', [ - 'Noto Serif Tibetan', - ]); - }); + // https://github.com/flutter/devtools/issues/6149 + test('can find glyph for treble clef', () async { + await checkDownloadedFamiliesForString('𝄞', ['Noto Music']); + }); - // https://github.com/flutter/flutter/issues/149616 - test('can find Ethiopic script glyphs ()', () async { - await checkDownloadedFamiliesForString('ኢትዮጵያ', ['Noto Sans Ethiopic']); - }); + // https://github.com/flutter/flutter/issues/148797 + test('can find Tibetan script glyphs (Dzongkha)', () async { + await checkDownloadedFamiliesForString('འཛམ་གླིང་སྤྱི་ཚོགས', ['Noto Serif Tibetan']); + }); - // https://github.com/flutter/flutter/issues/157763 - test('prioritizes Noto Color Emoji over Noto Sans Symbols', () async { - await checkDownloadedFamilyForCharCode(0x1f3d5, 'Noto Color Emoji'); - }); + // https://github.com/flutter/flutter/issues/149616 + test('can find Ethiopic script glyphs ()', () async { + await checkDownloadedFamiliesForString('ኢትዮጵያ', ['Noto Sans Ethiopic']); + }); - // 0x700b is a CJK Unified Ideograph code point that exists in all of our - // CJK fonts. + // https://github.com/flutter/flutter/issues/157763 + test('prioritizes Noto Color Emoji over Noto Sans Symbols', () async { + await checkDownloadedFamilyForCharCode(0x1f3d5, 'Noto Color Emoji'); + }); - // Simplified Chinese - test('prioritizes Noto Sans SC for lang=zh', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans SC', userPreferredLanguage: 'zh'); - }); - test('prioritizes Noto Sans SC for lang=zh-Hans', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans SC', - userPreferredLanguage: 'zh-Hans', - ); - }); - test('prioritizes Noto Sans SC for lang=zh-CN', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans SC', - userPreferredLanguage: 'zh-CN', - ); - }); - test('prioritizes Noto Sans SC for lang=zh-SG', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans SC', - userPreferredLanguage: 'zh-SG', - ); - }); - test('prioritizes Noto Sans SC for lang=zh-MY', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans SC', - userPreferredLanguage: 'zh-MY', - ); - }); + // 0x700b is a CJK Unified Ideograph code point that exists in all of our + // CJK fonts. - // Simplified Chinese is prioritized when preferred language is non-CJK. - test('prioritizes Noto Sans SC for lang=en-US', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans SC', - userPreferredLanguage: 'en-US', - ); - }); + // Simplified Chinese + test('prioritizes Noto Sans SC for lang=zh', () async { + await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans SC', userPreferredLanguage: 'zh'); + }); + test('prioritizes Noto Sans SC for lang=zh-Hans', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans SC', + userPreferredLanguage: 'zh-Hans', + ); + }); + test('prioritizes Noto Sans SC for lang=zh-CN', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans SC', + userPreferredLanguage: 'zh-CN', + ); + }); + test('prioritizes Noto Sans SC for lang=zh-SG', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans SC', + userPreferredLanguage: 'zh-SG', + ); + }); + test('prioritizes Noto Sans SC for lang=zh-MY', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans SC', + userPreferredLanguage: 'zh-MY', + ); + }); - // Traditional Chinese - test('prioritizes Noto Sans TC for lang=zh-Hant', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans TC', - userPreferredLanguage: 'zh-Hant', - ); - }); - test('prioritizes Noto Sans TC for lang=zh-TW', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans TC', - userPreferredLanguage: 'zh-TW', - ); - }); - test('prioritizes Noto Sans TC for lang=zh-MO', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans TC', - userPreferredLanguage: 'zh-MO', - ); - }); + // Simplified Chinese is prioritized when preferred language is non-CJK. + test('prioritizes Noto Sans SC for lang=en-US', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans SC', + userPreferredLanguage: 'en-US', + ); + }); - // Hong Kong - test('prioritizes Noto Sans HK for lang=zh-HK', () async { - await checkDownloadedFamilyForCharCode( - 0x700b, - 'Noto Sans HK', - userPreferredLanguage: 'zh-HK', - ); - }); + // Traditional Chinese + test('prioritizes Noto Sans TC for lang=zh-Hant', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans TC', + userPreferredLanguage: 'zh-Hant', + ); + }); + test('prioritizes Noto Sans TC for lang=zh-TW', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans TC', + userPreferredLanguage: 'zh-TW', + ); + }); + test('prioritizes Noto Sans TC for lang=zh-MO', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans TC', + userPreferredLanguage: 'zh-MO', + ); + }); - // Japanese - test('prioritizes Noto Sans JP for lang=ja', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans JP', userPreferredLanguage: 'ja'); - }); + // Hong Kong + test('prioritizes Noto Sans HK for lang=zh-HK', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans HK', + userPreferredLanguage: 'zh-HK', + ); + }); - // Korean - test('prioritizes Noto Sans KR for lang=ko', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans KR', userPreferredLanguage: 'ko'); - }); + // Japanese + test('prioritizes Noto Sans JP for lang=ja', () async { + await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans JP', userPreferredLanguage: 'ja'); + }); - test('findMinimumFontsForCodePoints for all supported code points', () async { - // Collect all supported code points from all fallback fonts in the Noto - // font tree. - final Set testedFonts = {}; - final Set supportedUniqueCodePoints = {}; - renderer.fontCollection.fontFallbackManager!.codePointToComponents.forEachRange(( - int start, - int end, - FallbackFontComponent component, - ) { - if (component.fonts.isNotEmpty) { - testedFonts.addAll(component.fonts.map((font) => font.name)); - for (int codePoint = start; codePoint <= end; codePoint++) { - supportedUniqueCodePoints.add(codePoint); - } - } - }); + // Korean + test('prioritizes Noto Sans KR for lang=ko', () async { + await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans KR', userPreferredLanguage: 'ko'); + }); - expect(supportedUniqueCodePoints.length, greaterThan(10000)); // sanity check - final allFonts = { - ...[for (int i = 0; i <= 11; i++) 'Noto Color Emoji $i'], - ...[for (int i = 0; i <= 5; i++) 'Noto Sans Symbols 2 $i'], - ...[for (int i = 0; i <= 2; i++) 'Noto Sans Cuneiform $i'], - ...[for (int i = 0; i <= 2; i++) 'Noto Sans Duployan $i'], - ...[for (int i = 0; i <= 2; i++) 'Noto Sans Egyptian Hieroglyphs $i'], - ...[for (int i = 0; i <= 108; i++) 'Noto Sans HK $i'], - ...[for (int i = 0; i <= 123; i++) 'Noto Sans JP $i'], - ...[for (int i = 0; i <= 123; i++) 'Noto Sans KR $i'], - ...[for (int i = 0; i <= 100; i++) 'Noto Sans SC $i'], - ...[for (int i = 0; i <= 104; i++) 'Noto Sans TC $i'], - 'Noto Music', - 'Noto Sans', - 'Noto Sans Symbols', - 'Noto Sans Adlam', - 'Noto Sans Anatolian Hieroglyphs', - 'Noto Sans Arabic', - 'Noto Sans Armenian', - 'Noto Sans Avestan', - 'Noto Sans Balinese', - 'Noto Sans Bamum', - 'Noto Sans Bassa Vah', - 'Noto Sans Batak', - 'Noto Sans Bengali', - 'Noto Sans Bhaiksuki', - 'Noto Sans Brahmi', - 'Noto Sans Buginese', - 'Noto Sans Buhid', - 'Noto Sans Canadian Aboriginal', - 'Noto Sans Carian', - 'Noto Sans Caucasian Albanian', - 'Noto Sans Chakma', - 'Noto Sans Cham', - 'Noto Sans Cherokee', - 'Noto Sans Coptic', - 'Noto Sans Cypriot', - 'Noto Sans Deseret', - 'Noto Sans Devanagari', - 'Noto Sans Elbasan', - 'Noto Sans Elymaic', - 'Noto Sans Ethiopic', - 'Noto Sans Georgian', - 'Noto Sans Glagolitic', - 'Noto Sans Gothic', - 'Noto Sans Grantha', - 'Noto Sans Gujarati', - 'Noto Sans Gunjala Gondi', - 'Noto Sans Gurmukhi', - 'Noto Sans Hanunoo', - 'Noto Sans Hatran', - 'Noto Sans Hebrew', - 'Noto Sans Imperial Aramaic', - 'Noto Sans Indic Siyaq Numbers', - 'Noto Sans Inscriptional Pahlavi', - 'Noto Sans Inscriptional Parthian', - 'Noto Sans Javanese', - 'Noto Sans Kaithi', - 'Noto Sans Kannada', - 'Noto Sans Kayah Li', - 'Noto Sans Kharoshthi', - 'Noto Sans Khmer', - 'Noto Sans Khojki', - 'Noto Sans Khudawadi', - 'Noto Sans Lao', - 'Noto Sans Lepcha', - 'Noto Sans Limbu', - 'Noto Sans Linear A', - 'Noto Sans Linear B', - 'Noto Sans Lisu', - 'Noto Sans Lycian', - 'Noto Sans Lydian', - 'Noto Sans Mahajani', - 'Noto Sans Malayalam', - 'Noto Sans Mandaic', - 'Noto Sans Manichaean', - 'Noto Sans Marchen', - 'Noto Sans Masaram Gondi', - 'Noto Sans Math', - 'Noto Sans Mayan Numerals', - 'Noto Sans Medefaidrin', - 'Noto Sans Meetei Mayek', - 'Noto Sans Meroitic', - 'Noto Sans Miao', - 'Noto Sans Modi', - 'Noto Sans Mongolian', - 'Noto Sans Mro', - 'Noto Sans Multani', - 'Noto Sans Myanmar', - 'Noto Sans NKo', - 'Noto Sans Nabataean', - 'Noto Sans New Tai Lue', - 'Noto Sans Newa', - 'Noto Sans Nushu', - 'Noto Sans Ogham', - 'Noto Sans Ol Chiki', - 'Noto Sans Old Hungarian', - 'Noto Sans Old Italic', - 'Noto Sans Old North Arabian', - 'Noto Sans Old Permic', - 'Noto Sans Old Persian', - 'Noto Sans Old Sogdian', - 'Noto Sans Old South Arabian', - 'Noto Sans Old Turkic', - 'Noto Sans Oriya', - 'Noto Sans Osage', - 'Noto Sans Osmanya', - 'Noto Sans Pahawh Hmong', - 'Noto Sans Palmyrene', - 'Noto Sans Pau Cin Hau', - 'Noto Sans Phags Pa', - 'Noto Sans Phoenician', - 'Noto Sans Psalter Pahlavi', - 'Noto Sans Rejang', - 'Noto Sans Runic', - 'Noto Sans Saurashtra', - 'Noto Sans Sharada', - 'Noto Sans Shavian', - 'Noto Sans Siddham', - 'Noto Sans Sinhala', - 'Noto Sans Sogdian', - 'Noto Sans Sora Sompeng', - 'Noto Sans Soyombo', - 'Noto Sans Sundanese', - 'Noto Sans Syloti Nagri', - 'Noto Sans Syriac', - 'Noto Sans Tagalog', - 'Noto Sans Tagbanwa', - 'Noto Sans Tai Le', - 'Noto Sans Tai Tham', - 'Noto Sans Tai Viet', - 'Noto Sans Takri', - 'Noto Sans Tamil', - 'Noto Sans Tamil Supplement', - 'Noto Sans Telugu', - 'Noto Sans Thaana', - 'Noto Sans Thai', - 'Noto Sans Tifinagh', - 'Noto Sans Tirhuta', - 'Noto Sans Ugaritic', - 'Noto Sans Vai', - 'Noto Sans Wancho', - 'Noto Sans Warang Citi', - 'Noto Sans Yi', - 'Noto Sans Zanabazar Square', - 'Noto Serif Tibetan', - }; - expect( - testedFonts, - unorderedEquals(allFonts), - reason: - 'Found mismatch in fonts.\n' - 'Missing fonts: ${allFonts.difference(testedFonts)}\n' - 'Extra fonts: ${testedFonts.difference(allFonts)}', - ); - - // Construct random paragraphs out of supported code points. - final math.Random random = math.Random(0); - final List supportedCodePoints = supportedUniqueCodePoints.toList()..shuffle(random); - const int paragraphLength = 3; - const int totalTestSize = 1000; - - for (int batchStart = 0; batchStart < totalTestSize; batchStart += paragraphLength) { - final int batchEnd = math.min(batchStart + paragraphLength, supportedCodePoints.length); - final Set codePoints = {}; - for (int i = batchStart; i < batchEnd; i += 1) { - codePoints.add(supportedCodePoints[i]); - } - final Set fonts = {}; - for (final int codePoint in codePoints) { - final List fontsForPoint = - renderer.fontCollection.fontFallbackManager!.codePointToComponents - .lookup(codePoint) - .fonts; - - // All code points are extracted from the same tree, so there must - // be at least one font supporting each code point - expect(fontsForPoint, isNotEmpty); - fonts.addAll(fontsForPoint); - } - - try { - renderer.fontCollection.fontFallbackManager!.findFontsForMissingCodePoints( - codePoints.toList(), - ); - } catch (e) { - print( - 'findFontsForMissingCodePoints failed:\n' - ' Code points: ${codePoints.join(', ')}\n' - ' Fonts: ${fonts.map((NotoFont f) => f.name).join(', ')}', - ); - rethrow; + test('findMinimumFontsForCodePoints for all supported code points', () async { + // Collect all supported code points from all fallback fonts in the Noto + // font tree. + final Set testedFonts = {}; + final Set supportedUniqueCodePoints = {}; + renderer.fontCollection.fontFallbackManager!.codePointToComponents.forEachRange(( + int start, + int end, + FallbackFontComponent component, + ) { + if (component.fonts.isNotEmpty) { + testedFonts.addAll(component.fonts.map((font) => font.name)); + for (int codePoint = start; codePoint <= end; codePoint++) { + supportedUniqueCodePoints.add(codePoint); } } }); - test('fallback fonts do not download when debugDisableFontFallbacks is set', () async { - debugDisableFontFallbacks = true; + expect(supportedUniqueCodePoints.length, greaterThan(10000)); // sanity check + final allFonts = { + ...[for (int i = 0; i <= 11; i++) 'Noto Color Emoji $i'], + ...[for (int i = 0; i <= 5; i++) 'Noto Sans Symbols 2 $i'], + ...[for (int i = 0; i <= 2; i++) 'Noto Sans Cuneiform $i'], + ...[for (int i = 0; i <= 2; i++) 'Noto Sans Duployan $i'], + ...[for (int i = 0; i <= 2; i++) 'Noto Sans Egyptian Hieroglyphs $i'], + ...[for (int i = 0; i <= 108; i++) 'Noto Sans HK $i'], + ...[for (int i = 0; i <= 123; i++) 'Noto Sans JP $i'], + ...[for (int i = 0; i <= 123; i++) 'Noto Sans KR $i'], + ...[for (int i = 0; i <= 100; i++) 'Noto Sans SC $i'], + ...[for (int i = 0; i <= 104; i++) 'Noto Sans TC $i'], + 'Noto Music', + 'Noto Sans', + 'Noto Sans Symbols', + 'Noto Sans Adlam', + 'Noto Sans Anatolian Hieroglyphs', + 'Noto Sans Arabic', + 'Noto Sans Armenian', + 'Noto Sans Avestan', + 'Noto Sans Balinese', + 'Noto Sans Bamum', + 'Noto Sans Bassa Vah', + 'Noto Sans Batak', + 'Noto Sans Bengali', + 'Noto Sans Bhaiksuki', + 'Noto Sans Brahmi', + 'Noto Sans Buginese', + 'Noto Sans Buhid', + 'Noto Sans Canadian Aboriginal', + 'Noto Sans Carian', + 'Noto Sans Caucasian Albanian', + 'Noto Sans Chakma', + 'Noto Sans Cham', + 'Noto Sans Cherokee', + 'Noto Sans Coptic', + 'Noto Sans Cypriot', + 'Noto Sans Deseret', + 'Noto Sans Devanagari', + 'Noto Sans Elbasan', + 'Noto Sans Elymaic', + 'Noto Sans Ethiopic', + 'Noto Sans Georgian', + 'Noto Sans Glagolitic', + 'Noto Sans Gothic', + 'Noto Sans Grantha', + 'Noto Sans Gujarati', + 'Noto Sans Gunjala Gondi', + 'Noto Sans Gurmukhi', + 'Noto Sans Hanunoo', + 'Noto Sans Hatran', + 'Noto Sans Hebrew', + 'Noto Sans Imperial Aramaic', + 'Noto Sans Indic Siyaq Numbers', + 'Noto Sans Inscriptional Pahlavi', + 'Noto Sans Inscriptional Parthian', + 'Noto Sans Javanese', + 'Noto Sans Kaithi', + 'Noto Sans Kannada', + 'Noto Sans Kayah Li', + 'Noto Sans Kharoshthi', + 'Noto Sans Khmer', + 'Noto Sans Khojki', + 'Noto Sans Khudawadi', + 'Noto Sans Lao', + 'Noto Sans Lepcha', + 'Noto Sans Limbu', + 'Noto Sans Linear A', + 'Noto Sans Linear B', + 'Noto Sans Lisu', + 'Noto Sans Lycian', + 'Noto Sans Lydian', + 'Noto Sans Mahajani', + 'Noto Sans Malayalam', + 'Noto Sans Mandaic', + 'Noto Sans Manichaean', + 'Noto Sans Marchen', + 'Noto Sans Masaram Gondi', + 'Noto Sans Math', + 'Noto Sans Mayan Numerals', + 'Noto Sans Medefaidrin', + 'Noto Sans Meetei Mayek', + 'Noto Sans Meroitic', + 'Noto Sans Miao', + 'Noto Sans Modi', + 'Noto Sans Mongolian', + 'Noto Sans Mro', + 'Noto Sans Multani', + 'Noto Sans Myanmar', + 'Noto Sans NKo', + 'Noto Sans Nabataean', + 'Noto Sans New Tai Lue', + 'Noto Sans Newa', + 'Noto Sans Nushu', + 'Noto Sans Ogham', + 'Noto Sans Ol Chiki', + 'Noto Sans Old Hungarian', + 'Noto Sans Old Italic', + 'Noto Sans Old North Arabian', + 'Noto Sans Old Permic', + 'Noto Sans Old Persian', + 'Noto Sans Old Sogdian', + 'Noto Sans Old South Arabian', + 'Noto Sans Old Turkic', + 'Noto Sans Oriya', + 'Noto Sans Osage', + 'Noto Sans Osmanya', + 'Noto Sans Pahawh Hmong', + 'Noto Sans Palmyrene', + 'Noto Sans Pau Cin Hau', + 'Noto Sans Phags Pa', + 'Noto Sans Phoenician', + 'Noto Sans Psalter Pahlavi', + 'Noto Sans Rejang', + 'Noto Sans Runic', + 'Noto Sans Saurashtra', + 'Noto Sans Sharada', + 'Noto Sans Shavian', + 'Noto Sans Siddham', + 'Noto Sans Sinhala', + 'Noto Sans Sogdian', + 'Noto Sans Sora Sompeng', + 'Noto Sans Soyombo', + 'Noto Sans Sundanese', + 'Noto Sans Syloti Nagri', + 'Noto Sans Syriac', + 'Noto Sans Tagalog', + 'Noto Sans Tagbanwa', + 'Noto Sans Tai Le', + 'Noto Sans Tai Tham', + 'Noto Sans Tai Viet', + 'Noto Sans Takri', + 'Noto Sans Tamil', + 'Noto Sans Tamil Supplement', + 'Noto Sans Telugu', + 'Noto Sans Thaana', + 'Noto Sans Thai', + 'Noto Sans Tifinagh', + 'Noto Sans Tirhuta', + 'Noto Sans Ugaritic', + 'Noto Sans Vai', + 'Noto Sans Wancho', + 'Noto Sans Warang Citi', + 'Noto Sans Yi', + 'Noto Sans Zanabazar Square', + 'Noto Serif Tibetan', + }; + expect( + testedFonts, + unorderedEquals(allFonts), + reason: + 'Found mismatch in fonts.\n' + 'Missing fonts: ${allFonts.difference(testedFonts)}\n' + 'Extra fonts: ${testedFonts.difference(allFonts)}', + ); - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ - 'Roboto', - ]); + // Construct random paragraphs out of supported code points. + final math.Random random = math.Random(0); + final List supportedCodePoints = supportedUniqueCodePoints.toList()..shuffle(random); + const int paragraphLength = 3; + const int totalTestSize = 1000; - // Creating this paragraph would cause us to start to download the - // fallback font if we didn't disable font fallbacks. - final ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); - pb.addText('Hello 😊'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + for (int batchStart = 0; batchStart < totalTestSize; batchStart += paragraphLength) { + final int batchEnd = math.min(batchStart + paragraphLength, supportedCodePoints.length); + final Set codePoints = {}; + for (int i = batchStart; i < batchEnd; i += 1) { + codePoints.add(supportedCodePoints[i]); + } + final Set fonts = {}; + for (final int codePoint in codePoints) { + final List fontsForPoint = + renderer.fontCollection.fontFallbackManager!.codePointToComponents + .lookup(codePoint) + .fonts; - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + // All code points are extracted from the same tree, so there must + // be at least one font supporting each code point + expect(fontsForPoint, isNotEmpty); + fonts.addAll(fontsForPoint); + } - // Make sure we didn't download the fallback font. - expect( - renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - isNot(contains('Noto Color Emoji 9')), - ); - }); - - test('only woff2 fonts are used for fallback', () { - final fonts = getFallbackFontList(); - - for (final font in fonts) { - expect( - font.url, - endsWith('.woff2'), - reason: - 'Expected all fallback fonts to be WOFF2, but found ' - '"${font.name}" was not a WOFF2 font: ${font.url}', + try { + renderer.fontCollection.fontFallbackManager!.findFontsForMissingCodePoints( + codePoints.toList(), ); + } catch (e) { + print( + 'findFontsForMissingCodePoints failed:\n' + ' Code points: ${codePoints.join(', ')}\n' + ' Fonts: ${fonts.map((NotoFont f) => f.name).join(', ')}', + ); + rethrow; } - }); - }, - // HTML renderer doesn't use the fallback font manager. - skip: isHtml, - timeout: const Timeout.factor(4), - ); + } + }); + + test('fallback fonts do not download when debugDisableFontFallbacks is set', () async { + debugDisableFontFallbacks = true; + + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, ['Roboto']); + + // Creating this paragraph would cause us to start to download the + // fallback font if we didn't disable font fallbacks. + final ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('Hello 😊'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + + // Make sure we didn't download the fallback font. + expect( + renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, + isNot(contains('Noto Color Emoji 9')), + ); + }); + + test('only woff2 fonts are used for fallback', () { + final fonts = getFallbackFontList(); + + for (final font in fonts) { + expect( + font.url, + endsWith('.woff2'), + reason: + 'Expected all fallback fonts to be WOFF2, but found ' + '"${font.name}" was not a WOFF2 font: ${font.url}', + ); + } + }); + }, timeout: const Timeout.factor(4)); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/filters_test.dart b/engine/src/flutter/lib/web_ui/test/ui/filters_test.dart index d86982d982..3a07ae7e8a 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/filters_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/filters_test.dart @@ -53,14 +53,14 @@ Future testMain() async { ui.Paint()..imageFilter = ui.ImageFilter.dilate(radiusX: 5.0, radiusY: 5.0), ); await matchGoldenFile('ui_filter_dilate_imagefilter.png', region: region); - }, skip: isHtml); // HTML renderer does not support the dilate filter + }); test('erode filter', () async { await drawTestImageWithPaint( ui.Paint()..imageFilter = ui.ImageFilter.erode(radiusX: 5.0, radiusY: 5.0), ); await matchGoldenFile('ui_filter_erode_imagefilter.png', region: region); - }, skip: isHtml); // HTML renderer does not support the erode filter + }); test('matrix filter', () async { await drawTestImageWithPaint( @@ -94,7 +94,7 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region); - }, skip: isHtml); // Only Skwasm and CanvasKit implement composable filters right now. + }); test('compose with colorfilter', () async { final ui.ImageFilter filter = ui.ImageFilter.compose( @@ -103,7 +103,7 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region); - }, skip: isHtml); // Only Skwasm and CanvasKit implements composable filters right now. + }); test('color filter as image filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.mode( @@ -136,14 +136,14 @@ Future testMain() async { await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter); await matchGoldenFile('ui_filter_linear_to_srgb_colorfilter.png', region: region); expect(colorFilter.toString(), 'ColorFilter.linearToSrgbGamma()'); - }, skip: isHtml); // HTML renderer hasn't implemented this. + }); test('srgbToLinearGamma color filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.srgbToLinearGamma(); await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter); await matchGoldenFile('ui_filter_srgb_to_linear_colorfilter.png', region: region); expect(colorFilter.toString(), 'ColorFilter.srgbToLinearGamma()'); - }, skip: isHtml); // HTML renderer hasn't implemented this. + }); test('matrix color filter', () async { const ui.ColorFilter sepia = ui.ColorFilter.matrix([ @@ -412,62 +412,31 @@ Future testMain() async { return ui.Rect.fromLTWH(0, 0, offset * columns + pad, offset * rows + pad); } - test( - 'Rendering ops with ImageFilter blur with default tile mode', - () async { - final region = await renderingOpsWithTileMode(null); - await matchGoldenFile( - 'ui_filter_blurred_rendering_with_default_tile_mode.png', - region: region, - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + test('Rendering ops with ImageFilter blur with default tile mode', () async { + final region = await renderingOpsWithTileMode(null); + await matchGoldenFile('ui_filter_blurred_rendering_with_default_tile_mode.png', region: region); + }); - test( - 'Rendering ops with ImageFilter blur with clamp tile mode', - () async { - final region = await renderingOpsWithTileMode(ui.TileMode.clamp); - await matchGoldenFile('ui_filter_blurred_rendering_with_clamp_tile_mode.png', region: region); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + test('Rendering ops with ImageFilter blur with clamp tile mode', () async { + final region = await renderingOpsWithTileMode(ui.TileMode.clamp); + await matchGoldenFile('ui_filter_blurred_rendering_with_clamp_tile_mode.png', region: region); + }); - test( - 'Rendering ops with ImageFilter blur with decal tile mode', - () async { - final region = await renderingOpsWithTileMode(ui.TileMode.decal); - await matchGoldenFile('ui_filter_blurred_rendering_with_decal_tile_mode.png', region: region); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + test('Rendering ops with ImageFilter blur with decal tile mode', () async { + final region = await renderingOpsWithTileMode(ui.TileMode.decal); + await matchGoldenFile('ui_filter_blurred_rendering_with_decal_tile_mode.png', region: region); + }); - test( - 'Rendering ops with ImageFilter blur with mirror tile mode', - () async { - final region = await renderingOpsWithTileMode(ui.TileMode.mirror); - await matchGoldenFile( - 'ui_filter_blurred_rendering_with_mirror_tile_mode.png', - region: region, - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + test('Rendering ops with ImageFilter blur with mirror tile mode', () async { + final region = await renderingOpsWithTileMode(ui.TileMode.mirror); + await matchGoldenFile('ui_filter_blurred_rendering_with_mirror_tile_mode.png', region: region); + }); - test( - 'Rendering ops with ImageFilter blur with repeated tile mode', - () async { - final region = await renderingOpsWithTileMode(ui.TileMode.repeated); - await matchGoldenFile( - 'ui_filter_blurred_rendering_with_repeated_tile_mode.png', - region: region, - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + test('Rendering ops with ImageFilter blur with repeated tile mode', () async { + final region = await renderingOpsWithTileMode(ui.TileMode.repeated); + await matchGoldenFile( + 'ui_filter_blurred_rendering_with_repeated_tile_mode.png', + region: region, + ); + }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/font_collection_test.dart b/engine/src/flutter/lib/web_ui/test/ui/font_collection_test.dart index f5e2e6e371..3a1bc31f55 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/font_collection_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/font_collection_test.dart @@ -11,7 +11,6 @@ import 'package:ui/src/engine.dart'; import '../common/fake_asset_manager.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -35,10 +34,7 @@ Future testMain() async { () async { final FlutterFontCollection collection = renderer.fontCollection; final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); - expect( - await collection.loadFontFromList(ahemData.asUint8List()), - !isHtml, // HtmlFontCollection requires family name - ); + expect(await collection.loadFontFromList(ahemData.asUint8List()), isTrue); }, ); @@ -100,13 +96,7 @@ Future testMain() async { ); expect(result.loadedFonts, [robotoVariableFontUrl, robotoTestFontUrl]); expect(result.fontFailures, hasLength(1)); - if (isHtml) { - // The HTML renderer doesn't have a way to differentiate 404's from other - // download errors. - expect(result.fontFailures[invalidFontUrl], isA()); - } else { - expect(result.fontFailures[invalidFontUrl], isA()); - } + expect(result.fontFailures[invalidFontUrl], isA()); }); test('Loading asset fonts reports when a font has invalid data', () async { @@ -141,13 +131,7 @@ Future testMain() async { ); expect(result.loadedFonts, [robotoVariableFontUrl, robotoTestFontUrl]); expect(result.fontFailures, hasLength(1)); - if (isHtml) { - // The HTML renderer doesn't have a way to differentiate invalid data - // from other download errors. - expect(result.fontFailures[invalidFontUrl], isA()); - } else { - expect(result.fontFailures[invalidFontUrl], isA()); - } + expect(result.fontFailures[invalidFontUrl], isA()); }); test('Font manifest with numeric and string descriptor values parses correctly', () async { diff --git a/engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart b/engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart index 0c83994615..7c867e9bfa 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/fragment_shader_test.dart @@ -96,5 +96,5 @@ Future testMain() async { // Make sure we can reuse the shader object with a new uniform value and the same Paint object. shader.setFloat(0, 25.0); await drawCircleReusePaint('fragment_shader_voronoi_tile25px_reuse_paint.png'); - }, skip: isHtml); // Fragment shaders are not supported by the HTML renderer. + }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/gradient_golden_test.dart b/engine/src/flutter/lib/web_ui/test/ui/gradient_golden_test.dart index dd065978fc..40f3759833 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/gradient_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/gradient_golden_test.dart @@ -6,7 +6,6 @@ import 'dart:math' as math; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine/browser_detection.dart'; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; @@ -104,5 +103,5 @@ Future testMain() async { await matchGoldenFile('sweep_gradient_paint.png', region: region); }); - }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 + }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/image/html_image_element_codec_test.dart b/engine/src/flutter/lib/web_ui/test/ui/image/html_image_element_codec_test.dart index ade463ffbe..b69ca781ea 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/image/html_image_element_codec_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/image/html_image_element_codec_test.dart @@ -9,7 +9,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine/canvaskit/image.dart'; import 'package:ui/src/engine/dom.dart'; -import 'package:ui/src/engine/html/image.dart'; import 'package:ui/src/engine/html_image_element_codec.dart'; import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; @@ -63,12 +62,12 @@ Future testMain() async { expect(image.height, height); }); test('loads sample image', () async { - final HtmlImageElementCodec codec = createImageElementCodec('sample_image1.png'); + final HtmlImageElementCodec codec = CkImageElementCodec('sample_image1.png'); final ui.FrameInfo frameInfo = await codec.getNextFrame(); expect(codec.imgElement, isNotNull); expect(codec.imgElement!.src, contains('sample_image1.png')); - expect(codec.imgElement!.crossOrigin, isHtml ? isNull : 'anonymous'); + expect(codec.imgElement!.crossOrigin, 'anonymous'); expect(codec.imgElement!.decoding, 'async'); expect(frameInfo.image, isNotNull); @@ -76,7 +75,7 @@ Future testMain() async { expect(frameInfo.image.toString(), '[100×100]'); }); test('dispose image image', () async { - final HtmlImageElementCodec codec = createImageElementCodec('sample_image1.png'); + final HtmlImageElementCodec codec = CkImageElementCodec('sample_image1.png'); final ui.FrameInfo frameInfo = await codec.getNextFrame(); expect(frameInfo.image, isNotNull); expect(frameInfo.image.debugDisposed, isFalse); @@ -85,7 +84,7 @@ Future testMain() async { }); test('provides image loading progress', () async { final StringBuffer buffer = StringBuffer(); - final HtmlImageElementCodec codec = createImageElementCodec( + final HtmlImageElementCodec codec = CkImageElementCodec( 'sample_image1.png', chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); @@ -98,7 +97,7 @@ Future testMain() async { /// Regression test for Firefox /// https://github.com/flutter/flutter/issues/66412 test('Returns nonzero natural width/height', () async { - final HtmlImageElementCodec codec = createImageElementCodec( + final HtmlImageElementCodec codec = CkImageElementCodec( 'data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9I' 'jAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dG' 'l0bGU+QWJzdHJhY3QgaWNvbjwvdGl0bGU+PHBhdGggZD0iTTEyIDBjOS42MDEgMCAx' @@ -124,7 +123,7 @@ Future testMain() async { expect(codec.imgElement, isNotNull); expect(codec.imgElement!.src, contains('sample_image1.png')); - expect(codec.imgElement!.crossOrigin, isHtml ? isNull : 'anonymous'); + expect(codec.imgElement!.crossOrigin, 'anonymous'); expect(codec.imgElement!.decoding, 'async'); expect(frameInfo.image, isNotNull); @@ -146,12 +145,3 @@ Future testMain() async { }); }, skip: isSkwasm); } - -HtmlImageElementCodec createImageElementCodec( - String src, { - ui_web.ImageCodecChunkCallback? chunkCallback, -}) { - return isHtml - ? HtmlRendererImageCodec(src, chunkCallback: chunkCallback) - : CkImageElementCodec(src, chunkCallback: chunkCallback); -} diff --git a/engine/src/flutter/lib/web_ui/test/ui/image_golden_test.dart b/engine/src/flutter/lib/web_ui/test/ui/image_golden_test.dart index 26517ec710..ef5ddbdfcb 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/image_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/image_golden_test.dart @@ -230,7 +230,7 @@ Future testMain() async { await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('${name}_fragment_shader_sampler.png', region: drawRegion); - }, skip: isHtml); // HTML doesn't support fragment shaders + }); test('drawVertices with image shader', () async { final ui.Image image = await generateImage(); @@ -275,7 +275,7 @@ Future testMain() async { await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('${name}_drawVertices_imageShader.png', region: drawRegion); - }, skip: isHtml); // https://github.com/flutter/flutter/issues/127454; + }); test('toByteData_rgba', () async { final ui.Image image = await generateImage(); @@ -294,7 +294,7 @@ Future testMain() async { final ByteData? pngData = await image.toByteData(format: ui.ImageByteFormat.png); expect(pngData, isNotNull); expect(pngData!.lengthInBytes, isNonZero); - }, skip: isHtml); // https://github.com/flutter/flutter/issues/126611 + }); }); } @@ -347,32 +347,29 @@ Future testMain() async { return completer.future; }); - // https://github.com/flutter/flutter/issues/126603 - if (!isHtml) { - emitImageTests('decodeImageFromPixels_scaled', () { - final Uint8List pixels = generatePixelData(50, 50, (double x, double y) { - final double r = sqrt(x * x + y * y); - final double theta = atan2(x, y); - return ui.Color.fromRGBO( - (255 * (sin(r * 10.0) + 1.0) / 2.0).round(), - (255 * (sin(theta * 10.0) + 1.0) / 2.0).round(), - 0, - 1, - ); - }); - final Completer completer = Completer(); - ui.decodeImageFromPixels( - pixels, - 50, - 50, - ui.PixelFormat.rgba8888, - completer.complete, - targetWidth: 150, - targetHeight: 150, + emitImageTests('decodeImageFromPixels_scaled', () { + final Uint8List pixels = generatePixelData(50, 50, (double x, double y) { + final double r = sqrt(x * x + y * y); + final double theta = atan2(x, y); + return ui.Color.fromRGBO( + (255 * (sin(r * 10.0) + 1.0) / 2.0).round(), + (255 * (sin(theta * 10.0) + 1.0) / 2.0).round(), + 0, + 1, ); - return completer.future; }); - } + final Completer completer = Completer(); + ui.decodeImageFromPixels( + pixels, + 50, + 50, + ui.PixelFormat.rgba8888, + completer.complete, + targetWidth: 150, + targetHeight: 150, + ); + return completer.future; + }); emitImageTests('codec_uri', () async { final ui.Codec codec = await renderer.instantiateImageCodecFromUrl( @@ -444,7 +441,7 @@ Future testMain() async { // This API doesn't work in headless Firefox due to requiring WebGL // See https://github.com/flutter/flutter/issues/109265 - if (!isFirefox && !isHtml) { + if (!isFirefox) { emitImageTests('svg_image_bitmap_texture_source', () async { final DomBlob svgBlob = createDomBlob( [ diff --git a/engine/src/flutter/lib/web_ui/test/ui/line_metrics_test.dart b/engine/src/flutter/lib/web_ui/test/ui/line_metrics_test.dart index 902944814a..37b0066526 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/line_metrics_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/line_metrics_test.dart @@ -8,7 +8,6 @@ import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web/testing.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -26,7 +25,7 @@ Future testMain() async { expect(paragraph.getLineMetricsAt(0), isNull); expect(paragraph.numberOfLines, 0); expect(paragraph.getLineNumberAt(0), isNull); - }, skip: isHtml); // https://github.com/flutter/flutter/issues/144412 + }); test('Basic line related metrics', () { const double fontSize = 10; @@ -136,7 +135,7 @@ Future testMain() async { case final List metrics: expect(metrics, hasLength(1)); } - }, skip: isHtml); // The rounding hack doesn't apply to the html renderer + }); test('overrides with flutter test font when debugEmulateFlutterTesterEnvironment is enabled', () { final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); diff --git a/engine/src/flutter/lib/web_ui/test/ui/paragraph_builder_test.dart b/engine/src/flutter/lib/web_ui/test/ui/paragraph_builder_test.dart index 2262015669..f5c6baf803 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/paragraph_builder_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/paragraph_builder_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -102,26 +101,18 @@ Future testMain() async { expect(paragraph.height, lessThan(2 * fontSize)); }); - test( - 'kTextHeightNone StrutStyle', - () { - const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder( - ParagraphStyle( - fontSize: 100, - fontFamily: 'FlutterTest', - strutStyle: StrutStyle( - forceStrutHeight: true, - height: kTextHeightNone, - fontSize: fontSize, - ), - ), - ); - builder.addText('A'); - final Paragraph paragraph = builder.build()..layout(const ParagraphConstraints(width: 1000)); - // The height should be much smaller than fontSize * 10. - expect(paragraph.height, lessThan(2 * fontSize)); - }, - skip: isHtml, // The HTML renderer does not support struts. - ); + test('kTextHeightNone StrutStyle', () { + const double fontSize = 10; + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontSize: 100, + fontFamily: 'FlutterTest', + strutStyle: StrutStyle(forceStrutHeight: true, height: kTextHeightNone, fontSize: fontSize), + ), + ); + builder.addText('A'); + final Paragraph paragraph = builder.build()..layout(const ParagraphConstraints(width: 1000)); + // The height should be much smaller than fontSize * 10. + expect(paragraph.height, lessThan(2 * fontSize)); + }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/path_test.dart b/engine/src/flutter/lib/web_ui/test/ui/path_test.dart index fcd15035f2..86406dd5c7 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/path_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/path_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -52,8 +51,7 @@ Future testMain() async { // the bounds on this will be the same as union - but would draw a missing inside piece. final Path xor = Path.combine(PathOperation.xor, pathCircle1, pathCircle2); expect(xor.getBounds(), equals(c1UnionC2)); - // TODO(hterkelsen): Implement Path.combine in the HTML renderer, https://github.com/flutter/flutter/issues/44572 - }, skip: isHtml); + }); test('path combine oval', () { final Rect c1 = Rect.fromCircle(center: const Offset(10.0, 10.0), radius: 10.0); @@ -83,8 +81,7 @@ Future testMain() async { // the bounds on this will be the same as union - but would draw a missing inside piece. final Path xor = Path.combine(PathOperation.xor, pathCircle1, pathCircle2); expect(xor.getBounds(), equals(c1UnionC2)); - // TODO(hterkelsen): Implement Path.combine in the HTML renderer, https://github.com/flutter/flutter/issues/44572 - }, skip: isHtml); + }); test('path clone', () { final Path p1 = Path()..lineTo(20.0, 20.0); @@ -199,8 +196,7 @@ Future testMain() async { expect(multiContourMetric.iterator.current.length, equals(5.0)); expect(multiContourMetric.iterator.moveNext(), isFalse); expect(() => multiContourMetric.iterator.current, throwsRangeError); - // TODO(hterkelsen): forceClosed in computeMetrics is ignored in the HTML renderer, https://github.com/flutter/flutter/issues/114446 - }, skip: isHtml); + }); test('PathMetrics can remember lengths and isClosed', () { final Path path = @@ -219,8 +215,7 @@ Future testMain() async { expect(metrics[1].isClosed, false); expect(metrics[1].getTangentForOffset(4.0)!.vector, const Offset(1.0, 0.0)); expect(metrics[1].extractPath(4.0, 6.0).computeMetrics().first.length, 2.0); - // TODO(hterkelsen): isClosed always returns false in the HTML renderer, https://github.com/flutter/flutter/issues/114446 - }, skip: isHtml); + }); test('PathMetrics on a mutated path', () { final Path path = Path()..lineTo(0, 10); @@ -252,8 +247,7 @@ Future testMain() async { expect(newFirstMetric.isClosed, true); expect(newFirstMetric.getTangentForOffset(4.0)!.vector, const Offset(0.0, 1.0)); expect(newFirstMetric.extractPath(4.0, 10.0).computeMetrics().first.length, 6.0); - // TODO(hterkelsen): isClosed always returns false in the HTML renderer, https://github.com/flutter/flutter/issues/114446 - }, skip: isHtml); + }); test('path relativeLineTo', () { final p = Path(); diff --git a/engine/src/flutter/lib/web_ui/test/ui/renderer_test.dart b/engine/src/flutter/lib/web_ui/test/ui/renderer_test.dart index a790dac1b6..d0a0c8769b 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/renderer_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/renderer_test.dart @@ -9,7 +9,6 @@ import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; const ui.Color black = ui.Color(0xFF000000); const ui.Color red = ui.Color(0xFFFF0000); @@ -57,7 +56,7 @@ Future testMain() async { host1.remove(); host2.remove(); host3.remove(); - }, skip: isHtml); // HTML renderer doesn't support multi-view. + }); } DomElement createHostElement(ui.Rect rect) { diff --git a/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart b/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart index 421d7bb6f4..de138b6c2e 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart @@ -13,7 +13,6 @@ import 'package:web_engine_tester/golden_tester.dart'; import '../common/rendering.dart'; import '../common/test_initialization.dart'; -import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -210,7 +209,7 @@ Future testMain() async { await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_shader_mask.png', region: region); - }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 + }); test('backdrop filter layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); @@ -527,7 +526,7 @@ Future testMain() async { await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_shader_mask_clipped_out.png', region: region); - }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 + }); test('opacity layer with transformed children', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); @@ -597,80 +596,55 @@ Future testMain() async { ); }); - test( - 'backdrop layer with default blur tile mode', - () async { - final scene = backdropBlurWithTileMode(null, 10, 50); - await renderScene(scene); + test('backdrop layer with default blur tile mode', () async { + final scene = backdropBlurWithTileMode(null, 10, 50); + await renderScene(scene); - await matchGoldenFile( - 'scene_builder_backdrop_filter_blur_default_tile_mode.png', - region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + await matchGoldenFile( + 'scene_builder_backdrop_filter_blur_default_tile_mode.png', + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }); - test( - 'backdrop layer with clamp blur tile mode', - () async { - final scene = backdropBlurWithTileMode(ui.TileMode.clamp, 10, 50); - await renderScene(scene); + test('backdrop layer with clamp blur tile mode', () async { + final scene = backdropBlurWithTileMode(ui.TileMode.clamp, 10, 50); + await renderScene(scene); - await matchGoldenFile( - 'scene_builder_backdrop_filter_blur_clamp_tile_mode.png', - region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + await matchGoldenFile( + 'scene_builder_backdrop_filter_blur_clamp_tile_mode.png', + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }); - test( - 'backdrop layer with mirror blur tile mode', - () async { - final scene = backdropBlurWithTileMode(ui.TileMode.mirror, 10, 50); - await renderScene(scene); + test('backdrop layer with mirror blur tile mode', () async { + final scene = backdropBlurWithTileMode(ui.TileMode.mirror, 10, 50); + await renderScene(scene); - await matchGoldenFile( - 'scene_builder_backdrop_filter_blur_mirror_tile_mode.png', - region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + await matchGoldenFile( + 'scene_builder_backdrop_filter_blur_mirror_tile_mode.png', + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }); - test( - 'backdrop layer with repeated blur tile mode', - () async { - final scene = backdropBlurWithTileMode(ui.TileMode.repeated, 10, 50); - await renderScene(scene); + test('backdrop layer with repeated blur tile mode', () async { + final scene = backdropBlurWithTileMode(ui.TileMode.repeated, 10, 50); + await renderScene(scene); - await matchGoldenFile( - 'scene_builder_backdrop_filter_blur_repeated_tile_mode.png', - region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + await matchGoldenFile( + 'scene_builder_backdrop_filter_blur_repeated_tile_mode.png', + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }); - test( - 'backdrop layer with decal blur tile mode', - () async { - final scene = backdropBlurWithTileMode(ui.TileMode.decal, 10, 50); - await renderScene(scene); + test('backdrop layer with decal blur tile mode', () async { + final scene = backdropBlurWithTileMode(ui.TileMode.decal, 10, 50); + await renderScene(scene); - await matchGoldenFile( - 'scene_builder_backdrop_filter_blur_decal_tile_mode.png', - region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), - ); - }, - // HTML renderer doesn't have tile modes - skip: isHtml, - ); + await matchGoldenFile( + 'scene_builder_backdrop_filter_blur_decal_tile_mode.png', + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }); }); } diff --git a/engine/src/flutter/lib/web_ui/test/ui/utils.dart b/engine/src/flutter/lib/web_ui/test/ui/utils.dart index 225fb31b73..3adc2d73ff 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/utils.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/utils.dart @@ -32,9 +32,6 @@ FlutterView get implicitView => EnginePlatformDispatcher.instance.implicitView!; /// Returns [true] if this test is running in the CanvasKit renderer. bool get isCanvasKit => renderer is CanvasKitRenderer; -/// Returns [true] if this test is running in the HTML renderer. -bool get isHtml => renderer is HtmlRenderer; - bool get isSkwasm => renderer is SkwasmRenderer; bool get isMultiThreaded => isSkwasm && (renderer as SkwasmRenderer).isMultiThreaded; diff --git a/engine/src/flutter/lib/web_ui/test/ui/vertices_test.dart b/engine/src/flutter/lib/web_ui/test/ui/vertices_test.dart index 50dc7ba3c2..632135dc9f 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/vertices_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/vertices_test.dart @@ -61,7 +61,7 @@ void testMain() { await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_vertices_antialiased.png', region: region); - }, skip: isHtml); // https://github.com/flutter/flutter/issues/127454 + }); } ui.Vertices _testVertices() {